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.Adaptersubclass that will translate betweenitem_configand properly configureddataforMyModelby looking in the adapter registrydeployfish.registry.importer_registry. This registry mapsAdaptersubclasses todeployfish.core.models.abstract.Modelsubclasses.Instantiate the
Adaptersubclass, passing in ouritem_configto 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 configurationMyModelmay need in order to function properly.Instantiate a
MyModelby doing:Set any other necessary attributes on
instancefrom 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.AdapterThe.__init__()for your subclass will get passed thedeployfish.ymlconfiguration for your object, and will store it asdeployfish.core.adapters.abstract.Adapter.data. Overridedeployfish.core.adapters.abstract.Adapter.converton 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
dataparameter 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.Secretobjects from the service’sconfig:section viadeployfish.core.adapters.deployfish.SecretAdapterand possiblydeployfish.core.adapters.deployfish.ExternalSecretAdapter.Use
deployfish.core.adapters.deployfish.TaskDefinitionAdapterto create adeployfish.core.models.ecs.TaskDefinitionfrom 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.ScalingPolicyobjects (viadeployfish.core.adapters.appscaling.ECServiceScalingPolicyAdapter)One
deployfish.core.models.cloudwatch.CloudwatchAlarmperdeployfish.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.ServiceDiscoveryServiceobject (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.ServiceHelperTaskobjects (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 configuredServiceobject 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.ServiceHelperTaskobjects, create them in AWS and save theirfamily:revisionson 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.TaskDefinitionin AWS, and save its ARN to theServiceastaskDefinitionIf we need it, create the
deployfish.core.models.service_discovery.ServiceDiscoveryServicein AWS, and save its ARN to the service asserviceRegistries[0]['registryArn']; otherwise delete anyServiceDiscoveryServiceassociated with theService.Create the
Servicein AWSIf we need it, create the
ScalingTarget,ScalingPolicyandCloudwatchAlarmobjects in AWS, otherwise delete any such that exist in AWS