Dynamic Attributes

      +
      This page describes how dynamic attributes in templates are processed by the Service Broker.

      In the last section we described configuration templates and how they are used to generate valid Kubernetes resources. In this section we will look in detail at dynamic attributes, how they work, what they can do and how they can make your service instances truly dynamic.

      While this section focuses on dynamic attributes of resource templates, the same language syntax is used to process configuration binding registry definitions.

      Dynamic Attributes

      Dynamic attributes are best thought of as functions: they accept input arguments and return a single result. Dynamic attributes do not have any side effects.

      In general, dynamic attributes use the Go language template library with a few specializations. Control flow is allowed, however, each dynamic attribute should execute at one action in order to generate a value. Dot is not defined initially, all data must be accessed though accessor functions. Iteration is not supported.

      Dynamic Attribute Typing

      The key thing to note is that Go language templating operates on text. Text has no concept of type (other than being a string) therefore all dynamic attributes must be serialized to JSON in order to preserve type information and allow the Service Broker to make the correct decisions. The JSON serialization function is added implicitly, by the Service Broker, to all action pipelines that would usually generate template output. All dynamic attributes are initially defined as strings, however the attribute itself takes on the type of the value returned by attribute template processing.

      Optional Attributes

      All attribute templates are optional by default. This supports such use cases where parameters supplied by the end user—​as defined by the service catalog schemas--are optional. If a user defined parameter is not specified, but referred to by an attribute template, the parameter argument lookup fails and the result will be internally set to nil. If a configuration parameter value is nil, then the attribute is unset.

      By exploiting optional parameters and how they are handled by the Service Broker, you can create basic conditional elements to your templates.

      Mandatory Attributes

      As we have seen in our earlier template example, resource templates will not generate valid Kubernetes resources without certain attributes being populated. This will manifest itself as a Kubernetes creation error, which may be difficult to debug. You can catch these types of errors earlier, and with more context, by specifying that the parameter is required.

      {{ parameter "/optional-parameter" | required }}

      Unlike optional parameters, required parameters will raise an error if the result resolves to nil. This will indicate the error as related to the specific named parameter in the named template.

      Service Broker Defined Functions

      Service Broker defined functions are divided into categories based on their behavior. These are:

      • Accessors—​these lookup data items.

      • Mutators—​these transform existing data items.

      • Generators—​these generate data items.

      • Assertions—​these perform error checking.

      Accessors

      Accessors do a simple lookup of a value from available data sources.

      Registry

      The registry is described in detail in the next section. It is a typed key/value store. A registry source for name foo will return any value associated with the registry key foo as in the following example:

      {{ registry "fool" }}

      Parameter

      A parameter refers to a user specified parameter supplied with a create or update operation. Parameters are a supplied as a free-form JSON object to the API. The parameter function access to a string, number, array or object in the supplied JSON object. Parameters are accessed with the JSON pointer specification. For example, given the parameters passed to the API:

      {
        "size": 16,
        "resources": {
          "requests": {
            "cpu": "4",
            "memory": "16Gi"
          }
        }
      }

      A dynamic attribute of:

      {{ parameter "/size" }}

      would resolve to "4".

      A dynamic attribute of:

      {{ parameter "/resources/requests" }}

      would resolve to {"cpu":"4","memory":"16Gi"}.

      Template Snippet

      The snippet function enables generation of complex results, and recursive template generation. A specific example could involve Kubernetes label selectors. Resources are labeled with a set of values, label selectors then filter resources based on the same labels. In both cases the labels are the same, and can be generated by a common template snippet, rather than duplicated. Template snippets are the one case where a configuration template need not generate a Kubernetes resource.

      To demonstrate consider the following configuration template snippet definition:

      name: label-snippet
      template:
        app: '{{ registry "my-app-name" }}' (1)
      1 The app attribute is dynamically set to the value defined by the my-app-name registry key.

      Therefore if the registry key my-app-name contained the value merlin, then the snippet would generate the result {"app":"merlin"}.

      To use the snippet, the following configuration template shows how:

      template:
        apiVersion: v1
        kind: Secret
        metadata:
          name: my-secret
          labels: '{{ snippet "label-snippet" }}'

      This would generate the following Kubernetes resource:

      apiVersion: v1
      kind: Secret
      metadata:
        name: my-secret
        labels:
          app: merlin

      Template Array Snippet

      The array snippet function acts like the snippet function, but extends it to iterate across an array of inputs and provide them as "dot" parameters to the snippet. Array snippets return arrays of values, and provide a form of iteration.

      For example, consider the following parameters:

      {
        "ports": [
          {"name":"http","port":80},
          {"name":"https","port":443},
        ]
      }

      You can define a snippet to generate Kubernetes ports:

      name: port-snippet
      template:
        name: {{ .name }}
        containerPort: {{ .port }}
        protocol: TCP

      Then reference this snippet, and the parameter in your main pod template:

      template:
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - ports: '{{ snippetArray "port-snippet" (parameter "ports" | required) }}'

      This will yield the following rendered template:

      template:
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP

      For further information on "dot" parameters, see the official Go language templating documentation.

      Mutators

      Mutators allow data to be modified.

      Default

      The default function allows a dynamic attribute to have a value set when an optional input argument is not specified:

      {{ parameter "/size" | default 3 }}

      Upper

      The upper function upper cases a string:

      {{ upper "some string" }}

      Lower

      The lower function lower cases a string:

      {{ lower "some string" }}

      Title

      The upper function upper cases a string:

      {{ title "some string" }}

      Generators

      Generators create new values. They may accept arguments that allow the generation functions to be dynamically configured. Any cryptographic generators use cryptographically secure random number generators.

      Generate Password

      The password generator generates ephemeral passwords of a specific length and results in a string. The dictionary of characters used to generate passwords defaults to [a-zA-Z0-9], however this can be explicitly defined. To generate a 32 character password:

      {{ generatePassword 32 nil }}

      Generate Key

      The key generator creates a private key and results in a string containing a PEM encoded private key. Supported key types are RSA, ECDSA and ED25519. Supported encoding types are PKCS#1, PKCS#8 and SEC 1.

      For example, to generate a PKCS#8 encoded P256 elliptic curve private key:

      {{ generatePrivateKey "EllipticP256" "PKCS#8" nil }}

      Generate Certificate

      The certificate generator generates X.509 certificates and results in a string containing a PEM encoded certificate. This generator optionally accepts a CA certificate and key pair with which to sign the resulting certificate. If no CA is specified then the resulting certificate is self-signed.

      The certificate generator supports CA, server and client certificate types. Server and client certificates may be specified with DNS and e-mail subject alternative names respectively.

      For example, to generate a signed X.509 certificate:

      {{ generateCertificate (registry "my-key") "My Certificate" "24h" "Server" (list "localhost") (registry "my-ca-key") (registry "my-ca-cert") }}
      Recursive Template Processing

      This example demonstrates the use of dynamic function arguments. The private key associated with the certificate is provided as a PEM encoded string. In this example we recursively lookup the certificate from the registry with (registry "my-key").

      Automatic Certificate Rotation

      The Service Broker is reactive—​it responds to API calls—​therefore will never support certificate rotation directly. You should deploy a certificate manager with your service instances if this functionality is required by your security policy.

      Assertions

      Assertions allow error checking to be performed earlier in the pipeline to raise errors in a more constrained manner.

      Required

      The required function will raise an error if the input argument is nil.

      {{ parameter "/password" | required }}

      Next Steps

      The final step to explain the Service Broker configuration is to look at the registry. This is the last fundamental component of the Service Broker that must be understood in order to use and configure it effectively.