Skip to content

Latest commit

 

History

History
199 lines (162 loc) · 6.76 KB

README.md

File metadata and controls

199 lines (162 loc) · 6.76 KB

Simple State Handling

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 field state. 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.

Raw State Handling

    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 the state 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 the value
  • 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

Standard State Handling

    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 value
  • value: template or any: the new value based on the input or a template using the input binding to generate the state value

Tweaking the state access

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.