Skip to content

Commit

Permalink
chore: more doc strings and allow access to shared state from outputs
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Meier <[email protected]>
  • Loading branch information
astromechza committed Mar 11, 2024
1 parent 945c9c0 commit e0ad71d
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 14 deletions.
41 changes: 31 additions & 10 deletions internal/provisioners/templateprov/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,43 @@ import (

"github.com/score-spec/score-compose/internal/project"
"github.com/score-spec/score-compose/internal/provisioners"
"github.com/score-spec/score-compose/internal/util"
)

// Provisioner is the decoded template provisioner.
// A template provisioner provisions a resource by evaluating a series of Go text/templates that have access to some
// input parameters, previous state, and utility functions. Each parameter is expected to return a JSON object.
type Provisioner struct {
ProvisionerUri string `yaml:"uri"`
ResType string `yaml:"type"`
ResClass *string `yaml:"class,omitempty"`
ResId *string `yaml:"id,omitempty"`

InitTemplate string `yaml:"init,omitempty"`
StateTemplate string `yaml:"state,omitempty"`
OutputsTemplate string `yaml:"outputs,omitempty"`
// The InitTemplate is always evaluated first, it is used as temporary or working set data that may be needed in the
// later templates. It has access to the resource inputs and previous state.
InitTemplate string `yaml:"init,omitempty"`
// StateTemplate generates the new state of the resource based on the init and previous state.
StateTemplate string `yaml:"state,omitempty"`
// SharedStateTemplate generates modifications to the shared state, based on the init and current state.
SharedStateTemplate string `yaml:"shared,omitempty"`
// OutputsTemplate generates the outputs of the resource, based on the init and current state.
OutputsTemplate string `yaml:"outputs,omitempty"`

// RelativeDirectoriesTemplate generates a set of directories to create (true) or delete (false). These may then
// be used in mounting requests for volumes or service mounts.
RelativeDirectoriesTemplate string `yaml:"directories,omitempty"`
RelativeFilesTemplate string `yaml:"files,omitempty"`
// RelativeFilesTemplate generates a set of file contents to write (non nil) or delete (nil) from the mounts
// directory. These will then be used during service bind mounting.
RelativeFilesTemplate string `yaml:"files,omitempty"`

// ComposeNetworksTemplate generates a set of networks to add to the compose project. These will replace any with
// the same name already.
ComposeNetworksTemplate string `yaml:"networks,omitempty"`
ComposeVolumesTemplate string `yaml:"volumes,omitempty"`
// ComposeVolumesTemplate generates a set of volumes to add to the compose project. These will replace any with
// the same name already.
ComposeVolumesTemplate string `yaml:"volumes,omitempty"`
// ComposeServicesTemplate generates a set of services to add to the compose project. These will replace any with
// the same name already.
ComposeServicesTemplate string `yaml:"services,omitempty"`
}

Expand Down Expand Up @@ -92,6 +111,7 @@ func renderTemplateAndDecode(raw string, data interface{}, out interface{}) erro
return nil
}

// Data is the structure sent to each template during rendering.
type Data struct {
Uid string
Type string
Expand Down Expand Up @@ -135,15 +155,16 @@ func (p *Provisioner) Provision(ctx context.Context, input *provisioners.Input)
}
data.State = out.ResourceState

out.ResourceOutputs = make(map[string]interface{})
if err := renderTemplateAndDecode(p.OutputsTemplate, &data, &out.ResourceOutputs); err != nil {
return nil, fmt.Errorf("outputs template failed: %w", err)
}

out.SharedState = make(map[string]interface{})
if err := renderTemplateAndDecode(p.SharedStateTemplate, &data, &out.SharedState); err != nil {
return nil, fmt.Errorf("shared template failed: %w", err)
}
data.Shared = util.PatchMap(data.Shared, out.SharedState)

out.ResourceOutputs = make(map[string]interface{})
if err := renderTemplateAndDecode(p.OutputsTemplate, &data, &out.ResourceOutputs); err != nil {
return nil, fmt.Errorf("outputs template failed: %w", err)
}

out.RelativeDirectories = make(map[string]bool)
if err := renderTemplateAndDecode(p.RelativeDirectoriesTemplate, &data, &out.RelativeDirectories); err != nil {
Expand Down
9 changes: 5 additions & 4 deletions internal/provisioners/templateprov/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ b: {{ .Type }}
"state": `
a: {{ .Init.a }}
b: stuff
`,
"outputs": `
b: {{ .State.b | upper }}
`,
"shared": `
c: 1
`,
"outputs": `
b: {{ .State.b | upper }}
c: {{ .Shared.c }}
`,
"directories": `{"blah": true}`,
"files": `{"blah/foo": "content"}`,
Expand Down Expand Up @@ -66,8 +67,8 @@ some-svc:
"a": "thing.default#w.r",
"b": "stuff",
}, out.ResourceState)
assert.Equal(t, map[string]interface{}{"b": "STUFF"}, out.ResourceOutputs)
assert.Equal(t, map[string]interface{}{"c": 1}, out.SharedState)
assert.Equal(t, map[string]interface{}{"b": "STUFF", "c": 1}, out.ResourceOutputs)
assert.Equal(t, map[string]bool{"blah": true}, out.RelativeDirectories)
assert.Equal(t, map[string]*string{"blah/foo": util.Ref("content")}, out.RelativeFileContents)
assert.Equal(t, map[string]compose.NetworkConfig{"default": {Driver: "default"}}, out.ComposeNetworks)
Expand Down

0 comments on commit e0ad71d

Please sign in to comment.