Skip to content

Commit 1f03abd

Browse files
authored
Inline Templating (#12)
While very safe defining templates in YAML, it was very verbose. I can foresee one of the core complaints to be this, so fix it up. By using a templating based approach we should get control flow (e.g. loops) for free, and more importanlty it's not our problem, the language can be delegated to the go documentation instead. Implements #11.
1 parent 4cc0aff commit 1f03abd

File tree

28 files changed

+1545
-3642
lines changed

28 files changed

+1545
-3642
lines changed

.aspell.en.pws

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,4 @@ generateKey
102102
generatePassword
103103
readinessChecks
104104
interoperability
105+
Customizations

.github/workflows/push.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ on:
55
- '*'
66
tags-ignore:
77
- '*'
8+
pull_request:
9+
branches:
10+
- '*'
11+
tags-ignore:
12+
- '*'
813
jobs:
914
license:
1015
name: License

crds/servicebroker.couchbase.com_servicebrokerconfigs.yaml

Lines changed: 40 additions & 898 deletions
Large diffs are not rendered by default.
-4.33 KB
Loading

documentation/modules/ROOT/nav.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
** xref:concepts/catalog.adoc[Service Catalog]
1111
** xref:concepts/bindings.adoc[Configuration Bindings]
1212
** xref:concepts/templates.adoc[Configuration Templates]
13-
** xref:concepts/parameters.adoc[Configuration Parameters]
13+
** xref:concepts/dynamic-attributes.adoc[Dynamic Attributes]
1414
** xref:concepts/registry.adoc[Service Instance and Binding Registries]
1515
** xref:concepts/security.adoc[Security Models]
1616
* xref:tasks/index.adoc[Tasks]
@@ -19,5 +19,6 @@
1919
** xref:tasks/tls.adoc[Configure Service Instance TLS]
2020
* xref:reference/index.adoc[Reference]
2121
** xref:reference/servicebrokerconfigs.adoc[ServiceBrokerConfig Resource Reference]
22+
** xref:reference/template-functions.adoc[Dynamic Attribute Function Reference]
2223
** xref:reference/container.adoc[Service Broker Container Reference]
2324
** xref:reference/osb-api.adoc[Open Service Broker API Reference]

documentation/modules/ROOT/pages/concepts/architecture.adoc

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ When a service instance or binding is created by the Service Broker, all it is d
8282

8383
Take for instance the creation of a web application.
8484
It may need Kubernetes `ServiceAccount`, `Role`, `RoleBinding`, `Deployment` and `Service` resources in order to create a consumable service.
85-
Each of these resources is represented in the Service Broker configuration as a xref:concepts/templates.adoc[template].
85+
Each of these resources is represented in the Service Broker configuration as a xref:concepts/templates.adoc[resource template].
8686
Templates are xref:concepts/bindings.adoc[bound] to a service offering and plan in the catalog.
8787
Thus when you create a service instance of the web application, all the broker does is lookup the set of templates associated with the service offering and plan then dynamically create then in Kubernetes.
8888

@@ -92,33 +92,35 @@ By treating every resource template as generic and untyped it will work with any
9292
.Service Instance Creation Bound to Resource Templates
9393
image::sb-binding.png[align="center"]
9494

95-
=== Resource Templating
95+
=== Templated Resources
9696

97-
If the Service Broker just created static resources, then it would be impossible to create multiple service instances in the same namespace--they would create the same resources and conflict.
97+
If the Service Broker only created static resources, then it would be impossible to create multiple service instances in the same namespace--they would create the same resources, with the same name, and conflict.
9898
This is why the Service Broker uses templates and not resources.
9999

100-
Templates are modeled on JSON objects (all Kubernetes resources are JSON objects) and can be xref:concepts/parameters.adoc[parameterized].
101-
Parameters allow resource templates to be customized before creation.
102-
Fox example resource names can be dynamically modified to be based on the unique service instance ID, and therefore be guaranteed not to conflict with one another.
100+
Templates are modeled on JSON objects--all Kubernetes resources are JSON objects--as this allows the structure to be validated before being accepted.
101+
Individual resource templates may be customized before creation with dynamic attributes.
102+
For example, resource names can be dynamically modified to be based on the unique service instance ID, and therefore be guaranteed not to conflict with one another.
103103

104-
Applying parameters to templates is done using the well known https://tools.ietf.org/html/rfc6902[JSON pointer^] and https://tools.ietf.org/html/rfc6902[JSON patch^] specifications.
104+
Dynamic attributes are based on the Go language https://golang.org/pkg/text/template/[template^] specification.
105+
This will be familiar to users of Helm, however, the key difference is that the Service Broker applies structured JSON data to a structured JSON template.
106+
Helm operates on YAML text files, where white space is a concern.
105107

106-
.JSON Template Parameterized with JSON Patches
108+
.JSON Template Parameterized with Dynamic Attributes
107109
image::sb-templates.png[align="center"]
108110

109-
=== Flexible Parameterized Configuration
111+
=== Dynamic Attributes
110112

111-
Parameters are provided by a number of different sources.
113+
Template customization is provided by a number of different data sources.
112114

113115
The xref:concepts/registry.adoc[registry] is a key/value database that is pre-populated with metadata associated with the service instance or binding (e.g. unique ID, namespace).
114-
Registry values can also be defined by the result of parameter resolution for later retrieval.
116+
Registry values can also be user defined for later retrieval by processing a Go language template.
115117

116-
Parameter values can also be generated by generic functions.
118+
Customization values can also be generated by generic functions.
117119
These allow simple operations such as string formatting or password generation.
118120
They also allow more complex functionality such as generating cryptographic keys and certificates.
119121

120122
The Open Service Broker API also allows parameters to be specified when a service instance or binding is created.
121-
Template parameters can also refer to these parameters explicitly passed by the user.
123+
Customizations can also refer to these parameters explicitly passed by the user.
122124

123125
== Next Steps
124126

documentation/modules/ROOT/pages/concepts/bindings.adoc

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ In our example this would be a `ServiceAccount`, `Role` and all the other resour
3333
In reality, the resources the binding refers to are all templates--a base Kubernetes resource that can be modified dynamically based on request parameters.
3434
Templates are covered in more detail in the next section.
3535

36-
=== Parameters
36+
=== Registry Definitions
3737

38-
Configuration bindings may also contain lists of parameters.
39-
These are simple functions that accept inputs from a number of sources, optionally apply transformations to them, then store the output somewhere.
40-
These are covered in more detail in a later section, however we will mention that parameters in the configuration binding may be used to generate values that are stored, and can used as inputs to template rending later.
38+
Configuration bindings may also contain lists of registry definitions.
39+
These are simple functions--based on Go language templates--that accept inputs from a number of sources, optionally apply transformations to them, then store the output to a named registry key.
40+
41+
These are covered in more detail in a xref:concepts/dynamic-attributes.adoc[later section], however we will mention that registry entries defined in the configuration binding may be used to generate values that can used as inputs to template rending later.
4142
Such values can be shared resource names, passwords and even TLS configuration.
4243

4344
=== Readiness Checks
@@ -57,22 +58,22 @@ Service instances and service bindings have their own separate lists of template
5758

5859
When configuration bindings are processed, the Service Broker guarantees some ordering constraints:
5960

60-
. Configuration binding parameters are processed, in-order, first.
61+
. Configuration binding registry definitions are processed, in-order, first.
6162
. Configuration binding templates are processed, in-order, last.
6263

6364
Given processing is ordered, outputs from an earlier parameter can be used as inputs into a later one.
6465

65-
Both templates and parameters are optional.
66+
Both templates and registry definitions are optional.
6667
Take, for example, a service plan that has calculated its credentials while creating a service instance.
6768
When creating a service binding, in order to communicate these credentials back to an end user, the use of templates is not required.
68-
Credentials can be communicated back with only parameters.
69+
Credentials can be communicated back with only registry definitions.
6970

7071
Readiness checks are performed during asynchronous operation polling.
7172
This allows the client to control the duration it should poll for, rather than have the asynchronous provisioning operation poll for an arbitrary amount of time.
7273

7374
== Next Steps
7475

75-
We have seen how a service plan is mapped to lists of templates and parameters for both service instances and bindings.
76+
We have seen how a service plan is mapped to lists of templates and registry definitions for both service instances and bindings.
7677
Next we will look in detail at how templates are rendered into Kubernetes resources.
7778

7879
* xref:concepts/templates.adoc[Configuration Templates]
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
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

Comments
 (0)