This topic explains how to do tile migrations. This refers to changing the names and values of properties when a customer upgrades tile versions. Tile authors supply a JavaScript file to trigger chaining migrations. Chaining migrations allows for multiple migrations to run sequentially.

In the tile installer YAML file that Tile Generator creates, form properties appear in two locations:

  • form_types section: defines the contents and layout of the configuration interface.
  • property_blueprints section: defines the corresponding text box value types and constraints

Update values or property names using JavaScript

To update a product tile:

  1. In a single .js file, write JavaScript functions that return a hash of the tile’s properties.

  2. Name the file in the format TIMESTAMP_NAME.js. TIMESTAMP must be in the form “YYYYMMDDHHMM” to indicate when the author created the migration. NAME is a human-readable name for the migration, for example, 201606150900_example-product.js.

  3. Copy the TIMESTAMP_NAME.js file to the PRODUCT/migrations/v1 directory.

Example javaScript migration file

You can use this example migration file as a base for creating your own:

exports.migrate = function(input) {
    // Append text to a string

    input.properties['.web_server.example_string']['value'] += '!';

    // Delete property 'legacy_property' that's removed in new tile version
    delete input.properties['.properties.legacy_property'];

    // Rename property 'example_port' to 'example_port_renamed',
    // retaining the previous value.
    input.properties['.properties.example_port_renamed'] =
        input.properties['.properties.example_port'];
    delete input.properties['.properties.example_port'];

    // Append text to a string list
    input.properties['.properties.example_string_list']['value'].push(
        'new-string-append-by-migration');

    return input;
};

The properties object passed to your anonymous JavaScript migration functions are composed of properties at the job-level and product-level. Review the property names in the example metadata file in Tutorial Tile V3 for more information about job-level and product-level properties. The tile author must update the migrations to match the corresponding product metadata file.

Each property’s key in the properties object is the property reference from the metadata file.

Property references use one of the following forms:

  • .properties.{property_name} for product-level properties
  • .{job_name}.{property_name} for job-level properties
  • .properties.{property_name}.options.{option_name} or .{job_name}.{property_name}.options.{option_name} for selector option properties

The object accessed through the property reference contains a value key whose structure is specific to the type of the property. Objects can be a string, an array, or a hash. Review the following reference for the structure of each type of property.

JavaScript migrations API

Inside a JavaScript migration function, the system provides the following functions for your code:

console.log(string)
Arguments:  string
Return value:  none
Description:  Prints the string to the Rails log
Example:
  console.log("Hello World");
getCurrentProductVersion()
Arguments:  none
Return value:  string (example:  1.7.1.0)
Description:  Returns the version of the product that is installed
Example:
  console.log(getCurrentProductVersion());
generateGuid()
Arguments:  none
Return value:  string (example:  115f9ced-3167-4c7c-959b-d52c07f32cbf)
Description:  Returns a globally unique identifier (GUID) that can be used as the unique identifier for each element of a Collections property. When updating a Collection property blueprint, you as the migration author are responsible for updating the GUID of each new collection element that you create.
Example:
  console.log("Here's a GUID:  "+generateGuid())
abortMigration(string)
Arguments:  string containing error message
Return value:  none (never returns)
Description:  Causes the migration to fail immediately. Rolls back all migrations in the current chain, i.e, no changes will be committed.
Example:
  if (something > 5) {
    abortMigration("Can't upgrade tile when the value of something is more than 5")
  }


Property Type Value Structure Example
single-value properties Single value, but type-specific properties['.properties.my-prop'].value = 'my-string'; properties['.properties.other-prop'].value = true
dropdown Array of options properties['.properties.my-prop'].value = ['option1', 'option2']
rsa_cert_credentials Object properties['.properties.my-prop'].value = {'private_key_pem' => 'a-private-key', 'cert_pem' => 'a-cert-pem'}
rsa_pkey_credentials Object properties['.properties.my-prop'].value = {'private_key_pem' => 'a-private-key'}
salted_credentials Object properties['.properties.my-prop'].value = {'identity' => 'an-identity', 'salt' => 'mortons', 'password' => 'books'}
simple_credentials Object properties['.properties.my-prop'].value = {'identity' => 'an-identity', 'password' => 'secret'}
collections Array of objects properties['.properties.my-prop'].value = [{name: {value: 'foo'}, record_id: {value: 1}}, {name: {value: 'bar'}, record_id: {value: 2}}]
selectors
Selected value
String properties['.properties.my-prop'].value = 'selected option label'
selectors
{selector option name.property name}
Value object specific to property type properties['.properties.selector.option1.prop1'].value = 'foo' properties['.properties.selector.option1.prop2'].value = 2 properties['.properties.selector.option2.prop3'].value = ['bar', 'baz']

Single value properties refer to properties whose type is any of the following:

  • boolean
  • ca_certificate
  • domain
  • dropdown_select
  • email
  • http_url
  • integer
  • ip_address
  • ip_ranges
  • ldap_url
  • multi_select_options
  • network_address
  • network_address_list
  • port
  • smtp_authentication
  • string
  • string_list
  • text
  • uuid

Refer to the following example properties when you write your own tile migration JS file:

{
  properties: {
    '.properties.example_boolean': { value: false } ,
    '.properties.example_ca_certificate': { value: 'simple-typed-value'},
    '.properties.example_domain': { value: 'simple-typed-value'} ,
    '.properties.example_dropdown_select': { value: 'simple-typed-value'},
    '.properties.example_email': { value: 'simple-typed-value'},
    '.properties.example_http_url': { value: 'simple-typed-value'},
    '.properties.example_integer': { value: 111},
    '.properties.example_ip_address': { value: 'simple-typed-value'},
    '.properties.example_ip_ranges': { value: 'simple-typed-value'},
    '.properties.example_ldap_url': { value: 'simple-typed-value'},
    '.properties.example_multi_select_options': { value: ['simple-typed-value']},
    '.properties.example_network_address': { value: 'simple-typed-value'},
    '.properties.example_network_address_list': { value: 'simple-typed-value'},
    '.properties.example_port': { value: 22},
    '.properties.example_smtp_authentication': { value: 'simple-typed-value'},
    '.properties.example_string': { value: 'simple-typed-value'},
    '.properties.example_string_list': { value: 'simple-typed-value'},
    '.properties.example_text': { value: 'simple-typed-value'},
    '.properties.example_uuid': { value: 'simple-typed-value'},
    '.properties.example_rsa_cert_credentials': {
      value: {'private_key_pem': 'a-private-key', 'cert_pem':'a-cert-pem'},
    },
    '.properties.example_rsa_pkey_credentials': {
      value: {'private_key_pem':'a-private-key'},
    },
    '.properties.example_salted_credentials': {
      value: {'identity':'an-identity', 'salt':'mortons', 'password':'books'},
    },
    '.properties.example_simple_credentials': {
      value: {'identity':'an-identity', 'password':'secret'},
    },
    '.properties.example_collection': [
      {name: {value: 'foo'}, record_id: {value: 1}},
      {name: {value: 'bar'}, record_id: {value: 2}}
    ],
    '.properties.example_selector': {value: 'option1'},
    '.properties.selector.option1.prop1': {value: 'foo'},
    '.properties.selector.option1.prop2': {value: 2},
    '.properties.selector.option2.prop3': {value: 'bar,baz'}
  }
}

Examples demonstrating chaining migrations

You can use migration chaining for multiple migrations to run sequentially when an upgrade is performed that skips an intermediate version. For example, suppose you have three versions of your product: v1.6.0, v1.7.0, and v1.7.1. The v1.6.0 product contains v1.6 metadata, so it does not contain any JavaScript migrations.

The following upgrade scenarios illustrate chaining migrations in more detail, and use the example product versions previously described.

Example of a chain migration scenario.

Scenario A: Upgrading from v1.6.0 > v1.7.0 > v1.7.1

In this scenario, you start with the v1.6.0 product installed. After you upgrade TTanzu Operations Manager, you decide to upgrade the product to v1.7.0. This causes the migration 201606010000_a.js to run. Several weeks later, you decide to upgrade from v1.7.0 to v1.7.1. Now the 201607010000_b.js migration runs. Even though the v1.7.1 product includes both migrations, Tanzu Operations Manager does not run 201606010000_a.js again, because it maintains a record of migrations.

Example chain migration scenario A

Scenario B: Upgrading directly from v1.6.0 > v1.7.1

In this scenario, you also start with v1.6.0 installed, but you decide to upgrade directly to v1.7.1, skipping the v1.7.0 version. Both migrations run in lexicographical order.

Example chain migration scenario B

Scenario C: Installing v1.7.0, then upgrading to v1.7.1

In this scenario, you start with nothing installed. You do a clean installation of version v1.7.0 of the product. On installation of v1.7.0, no migrations run because migrations only run on upgrades. Later, you decide to upgrade to v1.7.1 of the product. Because v1.7.1 contains both migrations, and because no migrations have run on this system, only the second migration 201607010000_b.js runs. The system recorded the fact that v1.7.0 includes 201606010000_a.js, so that migration does not run.

Example of a chain migration scenario C

Scenario D: Installing v1.7.1

In this scenario, you do a clean installation of v1.7.1, with no previous versions of the product installed. Since migrations are only triggered by upgrade events, no migrations run.

Do not omit a migration from a later version of your tile. This breaks the “chaining” nature of migrations. Using the previous example, if you release a v1.7.1 tile without the 201606010000_a.js migration, the system can not detect that 201606010000_b.js is the same migration that was present in the clean installation in Scenario C.

Update deployed job names using the migrated_from job property

To rename a deploy job (instance group), you can use the migrated_from property. This uses the BOSH migrated_from feature. For more information, see the BOSH documentation.

To rename the job:

  1. Change the name property of the job to the new name.
  2. Add the migrated_from property to the job. This property contains an array of hashes, where each hash has a single key, name, that represents the old name of the job.

    For example:

    migrated_from:
    - name: old-name
    
  3. Change the resource_label property of the job to reflect the new name.
  4. Update any co-located errands to reference the new job name.
  5. Update any references to job-level properties with the new job name.
  6. Write a migration that migrates each job-level property.

    For example:

    input.properties['.{new-name}.{property}'] = input.properties['.{old-name}.{property}']
    

Note Using migrated_from creates duplicate entries in CredHub. CredHub contains an entry for each property, with a path referencing both the old and new job names. These are not automatically cleaned up, and are only deleted when the tile itself is deleted.

check-circle-line exclamation-circle-line close-line
Scroll to top icon