Updating a Deployment

With Cloudify, you can update a deployment, which serves multiple purposes: - Changing the deployment topology: for example, adding a new type of database to an existing deployment of webservers and databases. - Changing the settings of existing nodes: for example, resizing a volume, or changing the size of a VM instance. - Updating the real state of the provisioned resources to match the blueprint. For example: after manually resizing a volume by using a cloud provider’s console, run update to bring the volume back to the state described in the blueprint.

Describing a Deployment Update

A deployment update consists of: - An application blueprint. If a new blueprint is not provided, the current blueprint is used, and the overall deployment topology is kept unchanged. - New deployment inputs. If new inputs are not provided, existing inputs are used. - Additional parameters for toggling parts of the update flow on and off.

The application blueprint is a yaml blueprint file, just as any with any other blueprint in Cloudify (note that the blueprint represents the desired state of the deployment after the update). Using the example described in the introduction, the updated application blueprint would include a new database type, some new node templates of the new database type, and some new relationships that represent how these new nodes connect to the existing architecture.

Deployment update steps

When an update is executed, the Cloudify Manager computes the differences between the old blueprint & inputs, and the new blueprint & inputs - based on those differences, the deployment update steps are generated.

A step is a logical concept that represents a single change in a deployment update. There are three different types of steps, add, remove, and modify. The scope of a step is determined by its most top-level change. For example, if a node is added that also contains a new relationship, this is an ‘add node’ step, not an ‘add relationship’ step. Similarly, if a node’s property is modified, it is not a ‘modify node’ step, but a ‘modify property’ step. A list of all possible steps is located here.

Detecting configuration drift

In addition to applying the changes based on a new blueprint, the Cloudify Manager can also: - check the status of each node instance in the deployment - if necessary, heal the failing node instances - check the configuration drift of each node instance - find the differences between the node configuration as described in the blueprint, and the real state of the provisioned resources - if necessary, update the provisioned resources to match the blueprint.

This status and drift checking is based on the operations defined by each node, usually in a plugin.

Deployment Update Flow

Updating a deployment is a multi-stage process. The high-level overview of the workflow is:

  1. The differences between the old and the new blueprint are computed.
  2. The steps composing the deployment update are extracted.
  3. The Cloudify Manager data storage is updated with:
    • the updated deployment attributes (e.g. labels)
    • the newly-created node instances
    • the updated node properties
    • the updated node instance relationships
  4. The uninstall operations are executed on each deleted node instance.
  5. The install operations are executed on each added node instance.
  6. The check_status (and heal if necessary) operations are executed on all started node instances in the deployment.
  7. The check_drift (and update if necessary) operations are executed on all started and healthy node instances in the deployment.
  8. The reinstall operations are executed on each changed node instance.
  9. The deleted nodes and node-instances are removed from the Cloudify Manager data storage.

For a more in-depth description of these steps, see the workflow documentation

For notes about implementing the check_drift and update operations, see implementing drift check

Using Cloudify Management Console to Update a Deployment

To update a deployment from the Cloudify Management Console go to Services page, select Deployment from the left pane, then click on the Deployment Actions button and select Update option.

You will then see Deployment Update modal window:

Deployment Update modal window

In that window you can:

You can now choose if you want to do the update (Update button) or just preview (Preview button) what is going to be changed.

In Preview mode you can see the following information:

Deployment Update Details Preview #1 Deployment Update Details Preview #2

If you want to get the same information about update performed in the past:

  1. Go to History tab on the specific deployment page and scroll to Executions widget

  2. Click on the menu icon (List icon ) on relevant execution and select Show Update Details option (only available for executions associated with deployment update)

  3. See changes in Deployment Update Details modal window:

Deployment Update Details modal window

Using the CLI to Update a Deployment

You can update your deployment using the CLI. Updating a deployment via the CLI is similar to creating a deployment, besides the fact that you supply an argument of the existing deployment’s id. You must also supply either a blueprint id or new inputs (or both) for the deployment update to use. The deployment’s blueprint and inputs will be updated in the data model.

See more of the CLI usage in the CLI deployments documentation.

Skipping parts of the update procedure

You can skip parts of the Deployment Update procedure using flags. By default, all phases are enabled. The flags can be combined as needed.

The relevant CLI flags are: * To run an update without actually doing any changes to the blueprint, but still run the check_drift and update operations, use the --drift-only flag:

  cfy deployments update ID_OF_DEPLOYMENT_TO_UPDATE --drift-only

Ignoring failures while uninstalling nodes

When running uninstall (including uninstall as part or a reinstall) there are 2 possible ways of handling a recoverable error in a task:

This can be used in different situations, for example:

Recovering from a Failed Update

If a deployment update workflow fails during its execution, you would probably want to perform a “rollback” in order to recover. A common solution is to update the deployment with a blueprint which represents the previous (state of the) deployment. In order to do that make sure there is no running update workflow for your deployment. Look for the latest update workflow on the list:

cfy executions list -d DEPLOYMENT_ID

You will find more information on cancelling workflow executions on a dedicated page of this documentation.

The next (and the final) step of recovery opration is performing a deployment update with the original blueprint. The --reevaluate-active-statuses flag will help to make sure that the status of previous deployment update is aligned with the status of relevant execution.

Providing Inputs

You can provide new inputs while updating a deployment. You provide the inputs in the same manner as when creating a deployment, with the following important distinctions:

Overriding inputs

If you provide an input with the same name as an existing deployment input, it overrides its value. Other new inputs will be added to the data model as usual.

Example: Overriding inputs of existing nodes

Assume that you have the following node in your deployment, and that the port input has a value of 8080:

    webserver:
        # ...
        properties:
            port: {get_input: port}

Any new nodes (including new webserver nodes) that were added as a part of that deployment update and use the port input, are assigned with the new port input value - 9090.

The overriden input will cause a modification in the webserver node (his port property was changed). This will trigger an automatic reinstallation of all the instances of the webserver node, so the updated port will take affect. If the --skip-reinstall flag was passed, automatic reinstall will not be triggered, and although the input was overriden to 9090, the actual port on the existing server will remain 8080.

Automatically correcting old inputs’ types

In case you are trying to update a deployment but cannot because of an error similar to this one:

dsl_parser.exceptions.DSLParsingException: Property type validation failed in 'delay': the defined type is 'integer', yet it was assigned with the value '20'

please use the --auto-correct-types flag along with cfy deployments update. It’s purpose is to automatically convert old inputs values’ types from string to integer, float or boolean, based on the type of the input declared in a blueprint.

Implementing drift check

To avoid reinstalling instances when their properties have changed, implement the check_drift and update operations. Instances of nodes that implement those operations, will run them instead of being reinstalled.

The check_drift operation

The cloudify.interfaces.lifecycle.check_drift operation should examine the node properties, the instance runtime properties, and the actual state of the provisioned resource, and compute the differences.

The instance will be considered drifted if this operation returns a non-empty value (for example, a Python dict describing the differences).

The Cloudify Manager doesn’t inspect the returned value, so it can be any object. Plugin authors are advised to return a description of all the differences, so that the update operation can act upon them.

If the check_drift operation is not implemented, the instances are only considered drifted if there are relevant blueprint changes (e.g. the node properties have changed).

The update operations

The update operations are: - cloudify.interfaces.lifecycle.update - cloudify.interfaces.lifecycle.update_config - cloudify.interfaces.lifecycle.update_apply

Plugin authors can implement any, or all, of these operations. These three operations can be implemented in a way that mirrors the create, configure, and start operations.

In these operations, the value returned from check_drift can be accessed using ctx.instance.drift.

If none of these operations are implemented, the instance will be reinstalled in case of any drift.

check_drift and update example

In this example, consider a volume created on some cloud platform - to allow resizing that volume, implement the check_drift and update operations:

node_types:
  type: cloudify.nodes.SomeHypotheticalCloud.Volume
  properties:
    size: 100MB
  interfaces:
    cloudify.interfaces.lifecycle:
      check_drift: plugin.some_cloud.volumes.check_drift
      update: plugin.some_cloud.volumes.update
def check_drift(ctx):
    cloud_client = _make_cloud_client(ctx)
    volume = cloud_client.volumes.get(ctx.instance.id)

    # drift must be a COMPLETE description of drift - including ALL things that
    # changed. If the result is empty, update will NOT run. In this example,
    # the only property of the volume is size, so we only check the size.
    drift = {}

    actual_size = volume.size
    requested_size = ctx.node.properties['size']
    if actual_size != requested_size:
      drift['size'] = {'actual': actual_size, 'requested': requested_size}
    # if the actual size is equal to requested, return an empty dict - no drift
    return drift


def update(ctx):
    cloud_client = _make_cloud_client(ctx)
    volume = cloud_client.volumes.get(ctx.instance.id)

    drift = ctx.instance.drift

    # in this example, if update runs, `size` will always be present, because
    # that's the only thing that could have changed. In more complex types,
    # there could be many more changed properties.
    if 'size' in drift:
        actual, requested = drift['size']['actual'], drift['size']['requested']
        ctx.logger.info('Volume size should be %s, but is %s, resizing...',
                        requested, actual)
        cloud_client.volumes.resize(volume.id, requested)

What Can be Updated as a Part of a Deployment Update

The following can be updated as part of a deployment update, subject to the limitations that were previously described in the Unsupported Changes section.

Nodes

You can add or remove nodes, including all their relationships, operations, an so on. Remember that adding or removing a node triggers the install/uninstall workflow in regard to that node.

Relationships

With the exception of being added or removed as part of adding or removing a node, you can add or remove relationships independently. Adding a relationship triggers an execution of its establish operations (assuming a default install workflow). Similarly, removing a relationship triggers an execution of the unlink operations. You can also change a node’s relationship order. The operations of the added and removed relationships are executed according the order of the relationships in the deployment update blueprint.

# original deployment blueprint
node_templates:
    node1:
        relationships:
          - type: cloudify.relationships.connected_to
            target: node2
# deployment update blueprint
node_templates:
    node1:
        relationships:
          - type: cloudify.relationships.connected_to
            target: node3  # the previous relationship to node2 will be removed (unlinked), and a new relationship to node3 will be added (established)

Operations:

You can add, remove or modify node operations and relationship operations.

# original deployment blueprint
node_templates:
    node1:
        interfaces:
            interface1:
                operation1:
                    implementation:
                        plugin1.path.to.module.taskA
                operation2:
                    implementation:
                        plugin2.path.to.module.taskA
        relationships:
          - # ...
            source_interfaces:
                interface1:
                    operation1:
                        implementation:
                            plugin1.path.to.module.taskB
plugins:
    plugin1:
        # ...
    plugin2:
        # ...
# deployment update blueprint
node_templates:
    node1:
        interfaces:
            interface1:
                operation1:
                    implementation:  # modified operation1 (changed implementation)
                        plugin1.path.to.module.taskB
                # removed operation2
                operation3:  # added operation 3
                    implementation:
                        plugin2.path.to.module.taskB
        relationships:
          - # ...
            source_interfaces:
                interface1:
                    operation1:
                        implementation:  # modified operation1 (changed implementation to a different plugin)
                            plugin2.path.to.module.taskC
plugins:
    plugin1:
        # ...
    plugin2:
        # ...

Properties

You can add, remove, or modify properties. Note that overriding a default property value is treated as a property modification.

# original deployment blueprint
node_templates:
    node1:
        type: Cloudify.nodes.Compute
    node2:
        type: my_custom_node_type
        properties:
            prop1: value1
# deployment update blueprint
node_templates:
    node1:
        type: Cloudify.nodes.Compute
        properties:
            ip: 192.0.2.1  # modified the property by overriding its default (from types.yaml)
    node2:
        type: my_custom_node_type
        properties:
            # removed property prop1
            prop2: value2  # added property prop2

Outputs

You can add, remove or modify outputs.

# original deployment blueprint
outputs:
    output1:
        value: {get_input: inputA}
    output2:
        # ...
# deployment update blueprint
outputs:
    output1:
        value: {get_input: inputB}  # modified the value of output1
    # removed output2
    output3:  # added output3
        # ...

Workflows

You can add, remove or modify workflows.

# original deployment blueprint
workflows:
    workflow1: plugin_name.module_name.task1
    workflow2:
        # ...
# deployment update blueprint
outputs:
    workflow1:
        value: plugin_name.module_name.task2  # modified the value of workflow1
    # removed workflow2
    workflow3:  # added workflow3
        # ...

Plugins

You can add, remove or modify plugins.

# original deployment blueprint
imports:
  - plugin:plugin-name-1?version=1.0
  - plugin:plugin-name-2?version=1.0
# deployment update blueprint
imports:
  - plugin:plugin-name-1?version=2.0
  - plugin:plugin-name-3?version=1.0

In this example, plugin-name-1 will be updated from version 1.0 to version 2.0, plugin-name-3 will be added to the deployment, and plugin-name-2 removed from it.

In cases of updating a plugin that was used to install nodes in the deployment (for example, Openstack plugin used to install Openstack nodes), the plugin update may trigger automatic reinstallation of those nodes. It can be avoided by using the --skip-reinstall flag.

If you’d like to update the plugins for all the deployments of some specific blueprint, see the section below.

If you’d like to learn more about plugins version ranges, go here.

Updating plugins for a collection of deployments

If you’d like to perform an update for all the deployment of some blueprint, and update only their plugins, you can perform a plugins update. You can find more information on the CLI command here.

Description:

You can add, remove or modify the description.

Adding a description:

# original deployment blueprint
# no description field
# deployment update blueprint
description: new_description  # added description

Removing a description:

# original deployment blueprint
description: description_content
# deployment update blueprint
# removed the description

Modifying a description:

# original deployment blueprint
description: old_description
# deployment update blueprint
description: new_description

Unsupported Changes in a Deployment Update

If a deployment update blueprint contains changes that are not currently supported as a part of an update, the update is not executed, and a message indicating the unsupported changes will be displayed to the user. Following is a list of unsupported changes, together with some possible examples.

Node Type

You cannot change a node’s type.

# original deployment blueprint
node_templates:
    node1:
        type: my_type
# deployment update blueprint
node_templates:
    node1:
        type: my_updated_type  # unsupported update - can't modify a node's type!

contained_in Relationship Target

You cannot change the target value of a cloudify.relationships.contained_in type relationship, or any type that derives from it.

# original deployment blueprint
node_templates:
    node1:
        relationships:
          - type: cloudify.relationships.contained_in
            target: node2
# deployment update blueprint
node_templates:
    node1:
        relationships:
          - type: cloudify.relationships.contained_in
            target: node3  # unsupported update - can't modify a contained_in relationship's target

Relationship Properties

You cannot change a relationship’s property, for example, connection_type.

# original deployment blueprint
node_templates:
    node1:
        relationships:
          - # ...
            properties:
                connection_type: all_to_all
# deployment update blueprint
node_templates:
    node1:
        relationships:
          - # ...
            properties:
                connection_type: all_to_one  # unsupported update - can't modify a relationship's property

Groups, Policy Types and Policy Triggers

You cannot make changes in the top level fields groups, policy_types and policy_triggers as a part of a deployment update blueprint.