|
| 1 | += Dynamic Attributes |
| 2 | + |
| 3 | +[abstract] |
| 4 | +This page describes how dynamic attributes in templates are processed by the Service Broker. |
| 5 | + |
| 6 | +ifdef::env-github[] |
| 7 | +:relfileprefix: ../ |
| 8 | +:imagesdir: https://github.com/couchbase/service-broker/raw/master/documentation/modules/ROOT/assets/images |
| 9 | +endif::[] |
| 10 | + |
| 11 | +In the xref:concepts/templates.adoc[last section] we described configuration templates and how they are used to generate valid Kubernetes resources. |
| 12 | +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. |
| 13 | + |
| 14 | +While this section focuses on dynamic attributes of resource templates, the same language syntax is used to process configuration binding registry definitions. |
| 15 | + |
| 16 | +== Dynamic Attributes |
| 17 | + |
| 18 | +Dynamic attributes are best thought of as functions: they accept input arguments and return a single result. |
| 19 | +Dynamic attributes do not have any side effects. |
| 20 | + |
| 21 | +In general, dynamic attributes use the Go language https://golang.org/pkg/text/template/[template^] library with a few specializations. |
| 22 | +All actions, pipelines and functions are fully supported, providing control flow and function chaining out of the box. |
| 23 | +Arguments are confined to scalar values only (typically `int`, `string` and `bool`) and undefined values (`nil`). |
| 24 | + |
| 25 | +=== Dynamic Attribute Typing |
| 26 | + |
| 27 | +The key thing to note is that Go language templating operates on text. |
| 28 | +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. |
| 29 | +All dynamic attributes are initially defined as strings, however the attribute itself takes on the type of the value returned by attribute template processing. |
| 30 | + |
| 31 | +Internally, the templating engine treats all data as abstract values. |
| 32 | +Functions, however, may require a parameter to be of a specific type. |
| 33 | +The templating engine will attempt to cast from an abstract value to a concrete data type where required by a function argument. |
| 34 | +If this conversion fails, an error is raised. |
| 35 | + |
| 36 | +==== Optional Attributes |
| 37 | + |
| 38 | +All attribute templates are optional by default. |
| 39 | +This supports such use cases where parameters supplied by the end user--as defined by the xref:concepts/catalog.adoc#json-schemas[service catalog schemas]--are optional. |
| 40 | +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`. |
| 41 | +If a configuration parameter value is `nil`, then the attribute is unset. |
| 42 | + |
| 43 | +By exploiting optional parameters and how they are handled by the Service Broker, you can create basic conditional elements to your templates. |
| 44 | + |
| 45 | +==== Mandatory Attributes |
| 46 | + |
| 47 | +As we have seen in our xref:concepts/templates.adoc#template-example[earlier template example], resource templates will not generate valid Kubernetes resources without certain attributes being populated. |
| 48 | +This will manifest itself as a Kubernetes creation error, which may be difficult to debug. |
| 49 | +You can catch these types of errors earlier, and with more context, by specifying that the parameter is required. |
| 50 | + |
| 51 | +[source] |
| 52 | +---- |
| 53 | +{{ parameter "/optional-parameter" | required | json }} |
| 54 | +---- |
| 55 | + |
| 56 | +Unlike optional parameters, required parameters will raise an error if the result resolves to `nil`. |
| 57 | +This will indicate the error as related to the specific named parameter in the named template. |
| 58 | + |
| 59 | +== Service Broker Defined Functions |
| 60 | + |
| 61 | +Service Broker defined functions are divided into categories based on their behavior. These are: |
| 62 | + |
| 63 | +* Accessors--these lookup data items. |
| 64 | +* Mutators--these transform existing data items. |
| 65 | +* Generators--these generate data items. |
| 66 | +* Assertions--these perform error checking. |
| 67 | + |
| 68 | +[#accessors] |
| 69 | +=== Accessors |
| 70 | + |
| 71 | +Accessors do a simple lookup of a value from available data sources. |
| 72 | + |
| 73 | +==== Registry |
| 74 | + |
| 75 | +The registry is described in detail in the next section. |
| 76 | +It is a typed key/value store. |
| 77 | +A registry source for name `foo` will return any value associated with the registry key `foo` as in the following example: |
| 78 | + |
| 79 | +[source] |
| 80 | +---- |
| 81 | +{{ registry "fool" | json }} |
| 82 | +---- |
| 83 | + |
| 84 | +==== Parameter |
| 85 | + |
| 86 | +A parameter refers to a user specified parameter supplied with a create or update operation. |
| 87 | +Parameters are a supplied as a free-form JSON object to the API. |
| 88 | +The parameter function access to a string, number, array or object in the supplied JSON object. |
| 89 | +Parameters are accessed with the https://tools.ietf.org/html/rfc6902[JSON pointer^] specification. |
| 90 | +For example, given the parameters passed to the API: |
| 91 | + |
| 92 | +[source,json] |
| 93 | +---- |
| 94 | +{ |
| 95 | + "size": 16, |
| 96 | + "resources": { |
| 97 | + "requests": { |
| 98 | + "cpu": "4", |
| 99 | + "memory": "16Gi" |
| 100 | + } |
| 101 | + } |
| 102 | +} |
| 103 | +---- |
| 104 | + |
| 105 | +A dynamic attribute of: |
| 106 | + |
| 107 | +[source] |
| 108 | +---- |
| 109 | +{{ parameter "/size" | json }} |
| 110 | +---- |
| 111 | + |
| 112 | +would resolve to `"4"`. |
| 113 | + |
| 114 | +A dynamic attribute of: |
| 115 | + |
| 116 | +[source] |
| 117 | +---- |
| 118 | +{{ parameter "/resources/requests" | json }} |
| 119 | +---- |
| 120 | + |
| 121 | +would resolve to `{"cpu":"4","memory":"16Gi"}`. |
| 122 | + |
| 123 | +==== Template Snippet |
| 124 | + |
| 125 | +The snippet function enables generation of complex results, and recursive template generation. |
| 126 | +A specific example could involve Kubernetes label selectors. |
| 127 | +Resources are labeled with a set of values, label selectors then filter resources based on the same labels. |
| 128 | +In both cases the labels are the same, and can be generated by a common template snippet, rather than duplicated. |
| 129 | +Template snippets are the one case where a configuration template need not generate a Kubernetes resource. |
| 130 | + |
| 131 | +To demonstrate consider the following configuration template snippet definition: |
| 132 | + |
| 133 | +[source,yaml] |
| 134 | +---- |
| 135 | +name: label-snippet |
| 136 | +template: |
| 137 | + app: '{{ registry "my-app-name" | json }}' # <1> |
| 138 | +---- |
| 139 | + |
| 140 | +<1> The `app` attribute is dynamically set to the value defined by the `my-app-name` registry key. |
| 141 | + |
| 142 | +Therefore if the registry key `my-app-name` contained the value `merlin`, then the snippet would generate the result `{"app":"merlin"}`. |
| 143 | + |
| 144 | +To use the snippet, the following configuration template shows how: |
| 145 | + |
| 146 | +[source,yaml] |
| 147 | +---- |
| 148 | +template: |
| 149 | + apiVersion: v1 |
| 150 | + kind: Secret |
| 151 | + metadata: |
| 152 | + name: my-secret |
| 153 | + labels: '{{ snippet "label-snippet" | json }}' |
| 154 | +---- |
| 155 | + |
| 156 | +This would generate the following Kubernetes resource: |
| 157 | + |
| 158 | +[source,yaml] |
| 159 | +---- |
| 160 | +apiVersion: v1 |
| 161 | +kind: Secret |
| 162 | +metadata: |
| 163 | + name: my-secret |
| 164 | + labels: |
| 165 | + app: merlin |
| 166 | +---- |
| 167 | + |
| 168 | +=== Mutators |
| 169 | + |
| 170 | +Mutators build upon accessors and allow data to be modified. |
| 171 | + |
| 172 | +==== Default |
| 173 | + |
| 174 | +The default function allows a dynamic attribute to have a value set when an optional input argument is not specified: |
| 175 | + |
| 176 | +[source] |
| 177 | +---- |
| 178 | +{{ parameter "/size" | default 3 | json }} |
| 179 | +---- |
| 180 | + |
| 181 | +=== Generators |
| 182 | + |
| 183 | +Generators create new values. |
| 184 | +They may accept arguments that allow the generation functions to be dynamically configured. |
| 185 | +Any cryptographic generators use cryptographically secure random number generators. |
| 186 | + |
| 187 | +[#generate-password] |
| 188 | +==== Generate Password |
| 189 | + |
| 190 | +The password generator generates ephemeral passwords of a specific length and results in a string. |
| 191 | +The dictionary of characters used to generate passwords defaults to `[a-zA-Z0-9]`, however this can be explicitly defined. |
| 192 | +To generate a 32 character password: |
| 193 | + |
| 194 | +[source] |
| 195 | +---- |
| 196 | +{{ generatePassword 32 nil | json }} |
| 197 | +---- |
| 198 | + |
| 199 | +==== Generate Key |
| 200 | + |
| 201 | +The key generator creates a private key and results in a string containing a PEM encoded private key. |
| 202 | +Supported key types are RSA, ECDSA and ED25519. |
| 203 | +Supported encoding types are PKCS#1, PKCS#8 and SEC 1. |
| 204 | + |
| 205 | +For example, to generate a PKCS#8 encoded P256 elliptic curve private key: |
| 206 | + |
| 207 | +[source] |
| 208 | +---- |
| 209 | +{{ generatePrivateKey "EllipticP256" "PKCS#8" nil | json }} |
| 210 | +---- |
| 211 | + |
| 212 | +==== Generate Certificate |
| 213 | + |
| 214 | +The certificate generator generates X.509 certificates and results in a string containing a PEM encoded certificate. |
| 215 | +This generator optionally accepts a CA certificate and key pair with which to sign the resulting certificate. |
| 216 | +If no CA is specified then the resulting certificate is self-signed. |
| 217 | + |
| 218 | +The certificate generator supports CA, server and client certificate types. |
| 219 | +Server and client certificates may be specified with DNS and e-mail subject alternative names respectively. |
| 220 | + |
| 221 | +For example, to generate a signed X.509 certificate: |
| 222 | + |
| 223 | +[source] |
| 224 | +---- |
| 225 | +{{ generateCertificate (registry "my-key") "My Certificate" "24h" "Server" (list "localhost") (registry "my-ca-key") (registry "my-ca-cert") | json }} |
| 226 | +---- |
| 227 | + |
| 228 | +.Recursive Template Processing |
| 229 | +[TIP] |
| 230 | +==== |
| 231 | +This example demonstrates the use of dynamic function arguments. |
| 232 | +The private key associated with the certificate is provided as a PEM encoded string. |
| 233 | +In this example we recursively lookup the certificate from the registry with `(registry "my-key")`. |
| 234 | +==== |
| 235 | + |
| 236 | +.Automatic Certificate Rotation |
| 237 | +[TIP] |
| 238 | +==== |
| 239 | +The Service Broker is reactive--it responds to API calls--therefore will never support certificate rotation directly. |
| 240 | +You should deploy a certificate manager with your service instances if this functionality is required by your security policy. |
| 241 | +==== |
| 242 | + |
| 243 | +=== Assertions |
| 244 | + |
| 245 | +Assertions allow error checking to be performed earlier in the pipeline to raise errors in a more constrained manner. |
| 246 | + |
| 247 | +==== Required |
| 248 | + |
| 249 | +The required function will raise an error if the input argument is `nil`. |
| 250 | + |
| 251 | +[source] |
| 252 | +---- |
| 253 | +{{ parameter "/password" | required | json }} |
| 254 | +---- |
| 255 | + |
| 256 | +== Next Steps |
| 257 | + |
| 258 | +The final step to explain the Service Broker configuration is to look at the registry. |
| 259 | +This is the last fundamental component of the Service Broker that must be understood in order to use and configure it effectively. |
| 260 | + |
| 261 | +* xref:concepts/registry.adoc[Service Instance and Binding Registries] |
0 commit comments