Stitch rules

A Stitch rule is a customization rule that can either transform the content of the provided configuration file or generate it. A rule comprises of conditions (the ‘when’) and processors (the ‘how’). Deploy uses a YAML definition of rules for Stitch, provided the YAML file follows a naming convention, is of the valid YAML format, and has the root property kind set to the value Rules. See the example below.

kind: Rules
metadata:
  namespace: example-namespace
spec:
  - name: "exampleName"
    condition:
      deployedType: k8s.Resources
      expression: "#ctx.deployedApplication.environment.name == 'DevEnv'"
    processor:
      ...

Namespaces

Namespaces are used for easier categorization of customizations, and are defined in the root property metadata and sub property namespace, as shown in the sample YAML file above. The namespace name cannot contain a colon character (:) as it is used for resolution of macros.

Note: You must specify a system-wide unique combination of the name and namespace.

Conditions

The condition property of the Stitch rule determines whether a rule will be applied or not in a specific deployment. This property is mandatory and shouldn’t be empty for the repository validation to pass (or the repository synchronization will fail). There are a few condition types that can be used.

Supported condition types are:

  • deployedType - condition that checks if specified deployed type is same as the type of deployed in deployment
  • expression - condition that checks if specified expression is satisfied. SpEL expressions is used here with two variables and a few utility classes:

    • ctx - the deployment context as defined by the Java interface com.xebialabs.deployit.plugin.stitch.service.engine.context.DeploymentContext

      Method Description Return type Returns
      getDeployed Returns deployed for deployment. Deployed Deployed that is being deployed.
      getPreviousDeployed Returns deployed from previous deployment. Deployed Deployed that was deployed before this deployment.
      getCurrentDeployed Returns current deployed for create, update or rollback. Deployed Deployed that is currently being deployed. Value may be different than getDeployed for rollback action.
      getDeployedApplication Returns deployed application for deployment. DeployedApplication Deployed application that is being deployed.
      getPreviousDeployedApplication Returns previously deployed application. DeployedApplication Deployed application that was deployed in previous deployment.
      getCurrentDeployedApplication Returns current deployed application for create, update or rollback. DeployedApplication Deployed application that is currently being deployed. Value may be different than getDeployedApplication for rollback action.

      For more information on Deployed and DeployedApplication types see deployment context

    • input - the deployment context as defined by the Java interface com.xebialabs.deployit.plugin.stitch.service.engine.context.InputContext

      Method Description Parameters Return type Returns
      pathExistsWithValue Returns true if path exists and has assigned value to it. String value of pathExpression. boolean Evaluated flag for given path.
      getStringValue Returns String value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. String Evaluated String value for given pathExpression.
      getBooleanValue Returns boolean value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. boolean Evaluated boolean value for given pathExpression.
      getIntegerValue Returns Integer value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. Integer Evaluated Integer value for given pathExpression.
      getLongValue Returns Long value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. Long Evaluated Long value for given pathExpression.
      getDoubleValue Returns Double value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. Double Evaluated Double value for given pathExpression.
      getJsonNode Returns JsonNode value in given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. JsonNode Evaluated JsonNode value for given pathExpression.
      getStringList Returns List of String for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
      getBooleanList Returns List of boolean for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
      getIntegerList Returns List of Integer for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
      getLongList Returns List of Long for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
      getDoubleList Returns List of Double for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
      getJsonNodeList Returns List of JsonNode for given path. If path doesn’t exist or isn’t single value null will be returned. String value of pathExpression. List Evaluated List value for given pathExpression.
    • Utility classes are extensions that make SpEL much simpler. Please refer for stitch-extensibility Here are existing extensions and implemented methods that you can use out of the box:

      • @currentDeployed, @deployed, @previousDeployed

        • container - returns the id of the deployed container
        • deployable - returns the id of the deployable
        • id - returns the id of the deployed
        • name - returns the name of the deployed

          Note: If the value is not presented as per the above methods , returns ‘null’.

        • nameEquals - return true or false if the name equals to the given string
        • nameMatches - return true or false if the name matches the given regex
        • nameContains - return true or false if the name contains the given substring
        • get - every method above also calls get to resolve the deployed, and the return type slightly differs depending on the utility class:

          • @currentDeployed.get - same as ctx.getCurrentDeployed
          • @previousDeployed.get - same as ctx.getPreviousDeployed
          • @deployed.get - calls @currentDeployed.get and, if that value is null, calls @previousDeployed.get

        For example, calling @currentDeployed.id actually calls #ctx.getCurrentDeployed().getId()

      • @currentDeployedApplication, @deployedApplication, @previousDeployedApplication

        • version - returns the version of the deployed application
        • id - returns the id of the deployed application
        • name - returns the name of the deployed application

        Note: If the value is not presented as per the above methods , returns ‘null’.

        • nameEquals - return true or false if the name equals to the given string
        • nameMatches - return true or false if the name matches the given regex
        • nameContains - return true or false if the name contains the given substring
        • get - every method above also calls get to resolve the deployed application, and the return slightly differs depending on the utility class:

          • @currentDeployedApplication.get - same as ctx.getCurrentDeployedApplication
          • @previousDeployedApplication.get - same as ctx.getPreviousDeployedApplication
          • @deployedApplication.get - calls @currentDeployedApplication.get and, if that value is null, calls @previousDeployedApplication.get

        For Example calling @currentDeployedApplication.nameEquals('test-app') actually calls #ctx.getCurrentDeployedApplication().getName().equals('test-app')

      • @environment

        • dictionaries - returns the list of dictionaries providing placeholder values that are associated with the environment
        • members - returns the set of containers that are members of the environment
        • patchDictionaries - returns the list of patch dictionaries
        • getDictionary - returns the dictionary for the given id
        • getDictionaryValue - returns the value in the dictionary for the given dictionary id and key
        • getMember - returns the container for the given id
        • id - returns the id of the environment
        • name - returns the name of the environment

          Note: If the value is not presented as per the above methods , returns ‘null’.

        • nameEquals - return true or false if the name equals to the given string
        • nameMatches - return true or false if the name matches the given regex
        • nameContains - return true or false if the name contains the given substring
        • get - every method above also calls get to resolve the environment, and the return type is described below:

          • @environment.get - calls ctx.getCurrentDeployedApplication.getEnvironment and, if that value is null, calls ctx.getPreviousDeployedApplication.getEnvironment
      • @dictionary

        • containsKey - returns true or false if the dictionary contains a value for the given key
        • getValue - returns the value for the given key
        • get - every method above also calls get to resolve the dictionary, and the return type is a consolidated dictionary containing every dictionary related to the deployed application and container
      • @k8s

        • isDeployment - returns true or false if the kind value from the input context is equal to “Deployment”
        • isService - returns true or false if the kind value from the input context is equal to “Service”
        • isIngress - returns true or false if the kind value from the input context is equal to “Ingress”
        • isPersistentVolume - returns true or false if the kind value from the input context is equal to “PersistentVolume”
        • isSecret - returns true or false if the kind value from the input context is equal to “Secret”
        • isConfigMap - returns true or false if the kind value from the input context is equal to “ConfigMap”
        • isKind - returns true or false if the kind value from the input context is equal to the given value
      • @pathString

        • value - returns the value of the given path from the input context
        • valueEquals - returns true or false if the given path from the input context is equal to the given value
        • valueContains - returns true or false if the given path from the input context contains the given value
        • valueMatches - returns true or false if the given path from the input context matches the given regex
  • preProcessing - special condition that if its value evaluates to true then rule can be applied in preprocessing stitch transformation, see stitch preprocessing and postprocessing transformation
  • postProcessing - special condition that if its value evaluates to true then rule can be applied in postprocessing stitch transformation, see stitch preprocessing and postprocessing transformation

Note: If you specify an unsupported condition type, the validation won’t pass. Note: Pay special attention to the null checks in the condition expressions. Missing checks could lead to runtime errors while fetching fields which might have null values in them. For example having #ctx.deployedApplication.version could lead to NullPointerException when a rule is run against Rollback or Revert deployments.

Note: null checks are not required while using already implemented methods of existing utility classes

Given below are few examples of the expression condition.

  • Condition to compare the deployed application version:

    ...
    expression: "#ctx.deployedApplication != null && #ctx.deployedApplication.version.name == '2.0'"
    ...
    • or using utility classes

      ...
      expression: "@deployedApplication.version == '2.0'"
      ...
  • Condition to compare the deployed application name

    ...
    expression: "(#ctx.currentDeployed != null && #ctx.currentDeployed.name == 'MyApplication') || (#ctx.previousDeployed != null && #ctx.previousDeployed.name == 'MyApplication')"
    ...
    • Or using utility classes

      ...
      expression: "@deployed.nameEquals('MyApplication')"
      ...
  • Condition to compare the environment name

    ...
    expression: "#ctx.deployedApplication != null && #ctx.deployedApplication.enviornment.name.contains('k8s')"
    ...
    • Or using utility classes

      ...
      expression: "@environment.nameContains('k8s')"
      ...
  • Condition to compare the container value

    • Depending on the container type, different parameters will be available. While using this type of comparison, be sure the parameter exists on the container.

      ...
      expression: "@environment.getMember('myContainer') != null && @environment.getMember('myContainer').name == 'MyName'"
      ...
  • Condition to compare the dictionary value

    • searching specific dictionary

      ...
      expression: "@environment.getDictionaryValue('myDictionary', 'myKey') == 'myValue'"
      ...
    • or directly using the dictionary utility class (now it will search through every dictionary related to the used environment)

      ...
      expression: "@dictionary.getValue('myKey') == 'myValue'"
      ...
  • Condition to check if the input JSON or YAML document has a kind field with the String value of 'Service'

    ...
    expression: "#input.getStringValue('$.kind') == 'Service'"
    ...
  • Condition to check if the input JSON or YAML document has an existing spec.template.spec.containers path with 'petclinic' as a name field (notice the JsonPath filters used)

    ...
    expression: "#input.pathExistsWithValue(\"$.spec.template.spec.containers[?(@.name == 'petclinic')]\")"
    ...
  • Combined condition to check if the input JSON or YAML document has the $.spec.template.spec.containers[*].name field of that has value equal to #ctx.getDeployedApplication().getEnvironment().getName()

    ...
    expression: "#input.getStringList(\"$.spec.template.spec.containers[*].name\").contains(#ctx.getCurrentApplication().getEnvironment().getName())"
    ...
  • Condition to check if input JSON or YAML document has a value of 88 on $.spec.template.spec.containers[?(@.name == 'front-end')].ports[*].containerPort path

    ...
    expression: "#input.getIntegerValue('$.spec.template.spec.containers[?(@.name == 'front-end')].ports[*].containerPort' == 88)"
    ...
  • Condition to check if input JSON or YAML document has a value in $.spec.template.spec.containers array with name field of 'front-end' (notice combination of JsonPath and SpEL Collection Projections used in example)

    ...
    expression: "#input.getJsonNodeList('$.spec.template.spec.containers[*].![get('name').asText()].contains('front-end')"
    ...

    Note: In order for a rule to be applied, all the conditions must be satisfied. See processors.

Limitation

If the Stitch rules are updated between the Initial Deployment and Update Deployment, the result of Stitch processing during Update Deployment may not be as expected. For example, if changes are made to the pre-processing rules after the Initial Deployment and these changes use the modified rules as part of the Update Deployment, then the result of Stitch processing may not reflect the updated Stitch rules.