This library offers three lambda expressions that are able to maintain and update state in and for a template finally stored in a dedicated state stub file.
The package name is utilities.state
.
To use this package a shell wrapper is required, that generates and replaces
the state stub. An example for such an wrapper can be taken from gen.sh.
Alternatively the new option --state <path>
can be
used ( spiff state handling support).
The provided lambda expressions work on two types of data:
-
an input data representing the input for generating a state value. When this input changes (or no previous state value is available) a new state value is generated by using
-
a value template or a direct value. The template is instantiated with the input as binding
input
whenever a new state value is required. The state is then taken from sub fieldstate
. The rest of the template is ignored and can be used for providing intermediate values.If no template, but a direct value is specified, this value is used directly, whenever a state change is indicated by an input change.
The following example for generating certificates for an etcd service running on kubernetes illustrates the usage of the package:
utilities:
<<: (( &temporary(merge) ))
svcHosts: (( lambda |svc,ns|->($x=[svc, ns, "svc", "cluster", "local"]) sum[x|[]|s,i,v|-> s join(".",x.[0..i])] ))
spec:
ca:
input:
spec:
commonName: ca:etcd
isCA: true
usage:
- Signature
- KeyEncipherment
value:
<<: (( &template ))
spec:
<<: (( input.spec ))
privateKey: (( state.key ))
state:
key: (( x509genkey() ))
pub: (( x509publickey(key) ))
cert: (( x509cert(spec) ))
server:
input:
spec:
commonName: etcd-server:etcd
caCert: (( .state.ca.value.cert ))
caPrivateKey: (( .state.ca.value.key ))
validity: 87600
usage:
- ServerAuth
- ClientAuth
- KeyEncipherment
hosts:
- etcd-main-0
- <<: (( utilities.svcHosts("etcd-main", "default") ))
- localhost
value:
<<: (( &template ))
spec:
<<: (( input.spec ))
caPrivateKey: (( .state.ca.value.key ))
publicKey: (( state.pub ))
state:
key: (( x509genkey(2048) ))
pub: (( x509publickey(key) ))
cert: (( x509cert(spec) ))
client:
input:
spec:
commonName: garden:etcd-client:etcd
caCert: (( state.ca.value.cert ))
caPrivateKey: (( state.ca.value.key ))
validity: 87600
usage:
- ServerAuth
- ClientAuth
- KeyEncipherment
value:
<<: (( &template ))
spec:
<<: (( input.spec ))
publicKey: (( state.pub ))
state:
key: (( x509genkey(2048) ))
pub: (( x509publickey(key) ))
cert: (( x509cert(spec) ))
state:
<<: (( merge none ))
ca: (( utilities.state.standard(spec.ca, false) ))
server: (( utilities.state.standard(spec.server,false) ))
client: (( utilities.state.standard(spec.client,false) ))
deployment:
cert: (( state.server.value.cert ))
It maintains state below the state
node. In every sub-node a
map with two fields is available: the input
used to generate the actual
state and the state value
hosting the actual state.
Those fields must be stored in nodes with disabled auto-merge. The functions maintain the merge from the stub on their own.
A simplified certificate support can be found in the
utilities.certs
package.
utilities.state.data(<input>,<new>,<update>=false)
- <input>: any value: the input data used to generate the state value
- <new>: template or value: the new value based on the input or a
template using the
input
binding to generate the state value from thestate
field. - <update>: bool: (optional) setting to true enforces a value update
- <relpath>: []: (optional) additional path segments for state access
- <relindex>: 0: (optional) relative location (from the end) to insert the additional path segments
It generates a state map with two fields:
input
: the input used to generate thevalue
value
: the final value. If the state feature of spiff is used, this value will be kept until the actual input differs from the stored one.
utilities.state.valuedata(<input>,<new>,<update>=false)
- <input>: any value: the input data used to generate the state value
- <new>: template or value: the new value based on the input or a
template using the
input
binding to generate the state value directly from its value - <update>: bool: (optional) setting to true enforces a value update
- <relpath>: []: (optional) additional path segments for state access
- <relindex>: 0: (optional) relative location (from the end) to insert the additional path segments
utilities.state.standard(<spec>,<update>=false)
- <spec>: map: structure containing the specification for this state value
- <update>: bool: (optional) setting to true enforces a value update
- <relpath>: []: (optional) additional path segments for state access
- <relindex>: 0: (optional) relative location (from the end) to insert the additional path segments
This function is a wrapper for the one above. The spec map must contain two fields:
input
: any: the input data used to generate the state valuevalue
: template or any: the new value based on the input or a template using theinput
binding to generate the state value
By default the old state is always accessed using the stub()
function
to access the same field containing the state lambda in the stub which
is typically the state yaml. But this only works correctly if
the state expression directly generates the state fields.
The optional relpath
parameter can be used to adjust the stub access
(for accessing old state) in case of generating multiple state instances
with map
/sum
generating implicit intermediate sub structures between the
field containing the lambda expression and the generated state field.
for example, when generating wireguard keys for a dynamic set of names:
names:
- alice
- bob
state:
<<: (( &state(merge none) ))
wireguard: (( map{names|m|-> utilities.certs.wireguardKey(false, [m])} ))
The optional relindex
parameter is used together with the relpath
parameter.
It specifies the relative location (from the end) where the relative path
should be inserted into the path.