Configuration Templates

    +
    This page describes how configuration templates are used in the Service Broker.

    In the previous section we looked at how configuration bindings map service instances and service bindings for a service plan to a set of resource templates. This section examines how templates are defined and are processed to result in Kubernetes resources that can be created.

    Configuration Templates

    Templates are conceptually very simple. They are a resource template—​that provides scaffolding to build your resource with, with optional dynamic attributes—​that allow the resource template to be customized.

    The Service Broker operates on abstract templates rather than concrete Kubernetes resources because of namespace conflicts. If, for example, your service instance created a Kubernetes Secret called my-secret, then only one service instance could ever be created in a single namespace. Any subsequent attempts to create another service instance would create a Secret with the same name and Kubernetes would reject it.

    By allowing templating, the Service Broker allows resource names to be dynamically generated, and you can guaranteed resources will never conflict. This is best explained by example:

    name: my-secret
    template: (1)
      apiVersion: v1
      kind: Secret
      metadata:
        name: '{{ registry "instance-id" }}' (2)
      stringData:
        mySecretName: TopSecret!

    Lets look in more detail at the configuration:

    1 The template attribute is just that, a template, it may not be able to be created by Kubernetes without more attributes being populated. In this case we want to create a Secret, but cannot as the metadata.name is not explicitly specified.
    2 The metadata.name attribute is an example of using dynamic attributes Dynamic attributes are defined as strings—​Go language templating operates on strings. This particular template references a value from the registry—​a simple key/value store.

    If, for example, our instance ID was camelot, the resulting resource would be:

    apiVersion: v1
    kind: Secret
    metadata:
      name: camelot
    stringData:
      mySecretName: TopSecret!

    This is now a valid Kubernetes resource and can be created by the Service Broker. The key observation to make is that service IDs are unique, therefore if another service instance rendered this template, it would result in a different name and can be created without conflict.

    Optional Attributes

    Consider a template with a dynamic attribute based on an optional parameter. That parameter may be supplied by a service instance creation request, or it may not:

    template:
      apiVersion: v1
      kind: Secret
      metadata:
        name: my-secret
        namespace: '{{ parameter "my-namespace" }}'

    If the parameter did not exist, the resulting resource would look like:

    apiVersion: v1
    kind: Secret
    metadata:
      name: my-secret
      namespace: null

    There is a danger that—​although the parameter itself was optional—​the generated resource would not be valid with the additional null attribute. For this reason, if the Service Broker resolves a dynamic attribute to null, then the corresponding attribute will be removed. The resulting resource will now look like:

    apiVersion: v1
    kind: Secret
    metadata:
      name: my-secret

    Resource Template Constraints

    Resource templates must be specified and must be objects. They cannot be scalar values or arrays. Templates referenced by configuration bindings must generate a valid Kubernetes object.

    As we will see in the next section, dynamic attributes can reference shared template snippets, that are themselves rendered as per the template processing rules. As a result there is no restriction that all templates must be Kubernetes resources.

    Namespaces

    One thing that we have not mentioned when creating Kubernetes resources is namespaces. By default, if not specified in the template the Service Broker will derive the namespace from the request context. The Kubernetes service catalog watches for ServiceInstance resources being created and then calls the Service Broker API in order to create the service instance. The namespace in which the ServiceInstance resource was created is passed to the Service Broker API with a request context. By using implicit namespace determination, the resources associated with a service plan will be created in the same namespace as the ServiceInstance resource.

    The namespace to use for provisioning is determined by the Service Broker using the following methods, in order of precedence:

    1. Explicitly defined in the resource template.

    2. Implicitly defined in the request context.

    3. If not specified in the request context, use the namespace the Service Broker is running in.

    Singletons

    In our earlier example, we made the resource name dynamic in order to prevent Kubernetes namespace conflicts. There are, however, times where you actually want to allow this behavior. Take cluster wide resources such as the ClusterRole, for example. If your service instance wanted to create a ClusterRole, and share it among all service instances, then we’d want to allow conflicts.

    You can specify the template is a singleton in the configuration to enable this behavior. When enabled the errors when creating a resource are looked at more closely. If a conflict is reported by Kubernetes and the resource is marked as a singleton, then the error is ignored and normal processing continues. Any other error will cause the operation to fail.

    Singletons can not be updated by service instance updates. The singleton configuration is considered fixed after creation. If singletons were allowed to be updated during service instance updates, then there is a risk of split-brain problems leading to undefined or unexpected behavior.

    Processing Rules

    Templates are—​under the hood—​JSON objects. When the Service Broker decodes JSON objects these are stored as the Go language map type. Go language map types, are based on hash tables, therefore iteration order is arbitrary. It follows that processing of dynamic attributes is also in an arbitrary order.

    If ordering of dynamic attribute processing matters, consider pre-computing values in the configuration binding.

    Next Steps

    We have seen how service instance and bindings are mapped to a set of configuration templates. Configuration templates map a resource template to final object by processing dynamic attributes in the configuration template. Next we will look at dynamic attributes, where they can source data from, and what processing can be performed on the source data.