Adapters and Models
Adapters: Loading a Model from configuration in deployfish.yml
Classes derived from deployfish.core.models.abstract.Model
can be configured
from configuration in deployfish.yml
.
Extract the configuration stanza for your object from deployfish.yml:
Generate your configued Model subclass instance by doing:
MyModel.new()
does this:
Find the proper
deployfish.core.adapter.abstract.Adapter
subclass that will translate betweenitem_config
and properly configureddata
forMyModel
by looking in the adapter registrydeployfish.registry.importer_registry
. This registry mapsAdapter
subclasses todeployfish.core.models.abstract.Model
subclasses.Instantiate the
Adapter
subclass, passing in ouritem_config
to its constructor.Run
MyAdapter.convert()
. This will generatedata
, a dict formatted to look like what boto3’sdescribe_*
API method would return for theMyModel
, andkwargs
, extra configurationMyModel
may need in order to function properly.Instantiate a
MyModel
by doing:Set any other necessary attributes on
instance
from the data we returned above inkwargs
.
Note
One of the challenges we have in constructing MyModel
from
deployfish.yml
is that we need to ensure we can also load MyModel
purely from AWS calls. When loading an object from AWS , we want any
dependent objects (e.g. the
deployfish.core.models.ecs.TaskDefinition
of a
deployfish.core.models.ecs.Service
) to be lazy loaded from AWS
in order to reduce the API calls to only the data we need at the moment –
this saves the user from having to wait too long.
When loading an object from deployfish.yml
however, we load all the
dependent objects at the same time have to provide them to the Model
instance all at once, with no lazy loading.
Largely we do this with @property
and @property.setter
decorators.
The main @property
loads the data from AWS if necessary, while the
@property.setter
circumvents the AWS loading.
Create a subclass of
deployfish.core.adapters.abstract.Adapter
The.__init__()
for your subclass will get passed thedeployfish.yml
configuration for your object, and will store it asdeployfish.core.adapters.abstract.Adapter.data
. Overridedeployfish.core.adapters.abstract.Adapter.convert
on that subclass to use self.data to generate data, a dict that replicates what boto3 would return were we to call the describe_* method for that object, and kwargs, keyword arguments for the object’s .new() factory method (described below)
…
Example: Loading a Service from deployfish.yml
First create all the appropriate objects from the service config in
deployfish.yml
.
The Adapter
that handles parsing the services:
entry for your service is
deployfish.core.adapters.deployfish.ServiceAdapter
. It does this,
in this order:
Build the data necessary for the
data
parameter todeployfish.core.models.ecs.Service.__init__
from the service’s config.If a
config:
section is present in the service’s config, load the list ofdeployfish.core.models.secrets.Secret
objects from the service’sconfig:
section viadeployfish.core.adapters.deployfish.SecretAdapter
and possiblydeployfish.core.adapters.deployfish.ExternalSecretAdapter
.Use
deployfish.core.adapters.deployfish.TaskDefinitionAdapter
to create adeployfish.core.models.ecs.TaskDefinition
from the service config. This needs the secrets we created above, if any.If
application_scaling:
section is present in the service’s config, build the Application Scaling objects, which are:deployfish.core.models.appscaling.ScalableTarget
(fromdeployfish.core.adapters.appscaling.ECServiceScalableTargetAdapter
)One or more
deployfish.core.models.appscaling.ScalingPolicy
objects (viadeployfish.core.adapters.appscaling.ECServiceScalingPolicyAdapter
)One
deployfish.core.models.cloudwatch.CloudwatchAlarm
perdeployfish.core.models.appscaling.ScalingPolicy
(viadeployfish.core.adapters.cloudwatch.ECServiceCPUAlarmAdapter
)
If a
service_discovery:
section is present in the service’s config, build adeployfish.core.models.service_discovery.ServiceDiscoveryService
object (viadeployfish.core.adapters.service_discovery.ServiceDiscoveryServiceAdapter
).If a
tasks:
section is present in the service’s config, build configuration for one or moredeployfish.core.models.ecs.ServiceHelperTask
objects (viadeployfish.core.adapters.ecs.ServiceHelperTaskAdapter
, but (important) loaded indeployfish.core.models.ecs.Service.new
, not indeployfish.core.adapters.ecs.ServiceAdapter.convert
– we need the fully configuredService
object in order to make the helper tasks, and that doesn’t happen until we get intoService.new()
.
Finally the Service
object is configured.
Creating a Service
Here’s how deployfish.core.models.ecs.Service.save
works when creating a service:
If we have any
deployfish.core.models.ecs.ServiceHelperTask
objects, create them in AWS and save theirfamily:revisions
on ourdeployfish.core.models.ecs.TaskDefinition
, so that we know which specific revision to run to get the version of the code we want.Create the
deployfish.core.models.ecs.TaskDefinition
in AWS, and save its ARN to theService
astaskDefinition
If we need it, create the
deployfish.core.models.service_discovery.ServiceDiscoveryService
in AWS, and save its ARN to the service asserviceRegistries[0]['registryArn']
; otherwise delete anyServiceDiscoveryService
associated with theService
.Create the
Service
in AWSIf we need it, create the
ScalingTarget
,ScalingPolicy
andCloudwatchAlarm
objects in AWS, otherwise delete any such that exist in AWS