Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/flavor/swarm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ to join the swarm, are now part of the plugin configuration.

This plugin makes heavy use of Golang template to enable customization of instance behavior on startup. For example,
the `InitScriptTemplateURL` field above is a URL where a init script template is served. The plugin will fetch this
template from the URL and processe the template to render the final init script for the instance.
template from the URL and processes the template to render the final init script for the instance.

The plugin exposes a set of template functions that can be used, along with primitives already in [Golang template]
(https://golang.org/pkg/text/template/) and functions from [Sprig](https://github.com/Masterminds/sprig#functions).
Expand Down
7 changes: 5 additions & 2 deletions examples/flavor/vanilla/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ Here's a skeleton of this Plugin's schema:
```json
{
"Init": [],
"Tags": {}
"Tags": {},
"InitScriptTemplateURL": "http://your.github.io/your/project/script.sh"
}
```

The supported fields are:
* `Init`: an array of shell code lines to use for the Instance's Init script
* `Tags`: a string-string mapping of keys and values to add as Instance Tags
* `InitScriptTemplateURL`: string URL where a init script template is served. The plugin will fetch this
template from the URL and process the template to render the final init script for the instance.

Here's an example Group configuration using the default [infrakit/group](/cmd/group) Plugin and the Vanilla Plugin:
```json
Expand Down Expand Up @@ -107,7 +110,7 @@ in your config JSON. For instance, you may start up this plugin as `french-vani

```shell
$ build/infrakit-flavor-vanilla --name french-vanilla
INFO[0000] Listening at: ~/.infrakit/plugins/french-vanilla
INFO[0000] Listening at: ~/.infrakit/plugins/french-vanilla
```

Then in your JSON config for the default group plugin, you would reference it by name:
Expand Down
46 changes: 43 additions & 3 deletions examples/flavor/vanilla/flavor.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package main

import (
"fmt"
"strings"

log "github.com/Sirupsen/logrus"
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
"github.com/docker/infrakit/pkg/spi/flavor"
"github.com/docker/infrakit/pkg/spi/instance"
"github.com/docker/infrakit/pkg/template"
"github.com/docker/infrakit/pkg/types"
)

Expand All @@ -14,6 +17,9 @@ type Spec struct {
// Init
Init []string

// InitScriptTemplateURL provides a URL to a template that is used to generaete Init
InitScriptTemplateURL string

// Tags
Tags map[string]string

Expand All @@ -32,7 +38,27 @@ func NewPlugin() flavor.Plugin {
type vanillaFlavor int

func (f vanillaFlavor) Validate(flavorProperties *types.Any, allocation group_types.AllocationMethod) error {
return flavorProperties.Decode(&Spec{})
spec := Spec{}
err := flavorProperties.Decode(&spec)
if err != nil {
return err
}
if spec.InitScriptTemplateURL != "" && len(spec.Init) > 0 {
return fmt.Errorf("Either \"Init\" or \"InitScriptTemplateURL\" can be specified but not both")
}

if spec.InitScriptTemplateURL != "" {
template, err := template.NewTemplate(spec.InitScriptTemplateURL, defaultTemplateOptions)
if err != nil {
return err
}
_, err = template.Render(nil)
if err != nil {
return err
}
}

return nil
}

func (f vanillaFlavor) Healthy(flavorProperties *types.Any, inst instance.Description) (flavor.Health, error) {
Expand All @@ -56,12 +82,26 @@ func (f vanillaFlavor) Prepare(flavor *types.Any,
return instance, err
}

// Append Init
// Handle Init lines, either from templated script or raw input; append to
// and instance.Init lines
lines := []string{}
if instance.Init != "" {
lines = append(lines, instance.Init)
}
lines = append(lines, s.Init...)
if s.InitScriptTemplateURL != "" {
template, err := template.NewTemplate(s.InitScriptTemplateURL, defaultTemplateOptions)
if err != nil {
return instance, err
}
initScript, err := template.Render(nil)
if err != nil {
return instance, err
}
lines = append(lines, initScript)
log.Infoln("Init script data:", initScript)
} else {
lines = append(lines, s.Init...)
}

instance.Init = strings.Join(lines, "\n")

Expand Down
203 changes: 203 additions & 0 deletions examples/flavor/vanilla/flavor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package main

import (
"testing"

group_types "github.com/docker/infrakit/pkg/plugin/group/types"

"github.com/docker/infrakit/pkg/spi/group"
"github.com/docker/infrakit/pkg/spi/instance"
"github.com/docker/infrakit/pkg/types"
"github.com/stretchr/testify/require"
)

func TestValidateValid(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
err := plugin.Validate(
types.AnyString(`{
"Init": ["l1"],
"InitScriptTemplateURL": "",
"Tags": {"tag1": "val1"},
"Attachments": []
}`),
group_types.AllocationMethod{Size: 1})
require.NoError(t, err)

err = plugin.Validate(
types.AnyString(`{
"InitScriptTemplateURL": "str://l1",
"Tags": {"tag1": "val1"},
"Attachments": []
}`),
group_types.AllocationMethod{Size: 1})
require.NoError(t, err)
}

func TestValidateInvalidJSON(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
err := plugin.Validate(
types.AnyString("not-json"),
group_types.AllocationMethod{Size: 1})
require.Error(t, err)
}

func TestValidateInitLinesWithInitScript(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
err := plugin.Validate(
types.AnyString(`{
"Init": ["l1"],
"InitScriptTemplateURL": "str://{{ var \"my-var\" \"value\" }}echo {{ var \"my-var\" }}"
}`),
group_types.AllocationMethod{Size: 1})
require.Error(t, err)
require.Equal(t,
"Either \"Init\" or \"InitScriptTemplateURL\" can be specified but not both",
err.Error())
}

func TestValidateInitScriptRenderError(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
err := plugin.Validate(
types.AnyString(`{
"InitScriptTemplateURL": "str://{{ nosuchfunc }}"
}`),
group_types.AllocationMethod{Size: 1})
require.Error(t, err)
}

func TestPrepareEmptyVanillaData(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(""),
instance.Spec{
Tags: map[string]string{"t1": "v1"},
Init: "l0\nl1",
Attachments: []instance.Attachment{{ID: "a0"}},
},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "l0\nl1", spec.Init)
require.Equal(t, map[string]string{"t1": "v1"}, spec.Tags)
require.Equal(t, []instance.Attachment{{ID: "a0"}}, spec.Attachments)
}

func TestPrepareWithAttachments(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Attachments": [{"ID": "a1"}]
}`),
instance.Spec{},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "", spec.Init)
require.Nil(t, spec.Tags)
require.Equal(t, []instance.Attachment{{ID: "a1"}}, spec.Attachments)
}

func TestPrepareWithAttachmentsAndInstanceSpecAttachments(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Attachments": [{"ID": "a1"}]
}`),
instance.Spec{Attachments: []instance.Attachment{{ID: "a0"}}},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "", spec.Init)
require.Nil(t, spec.Tags)
require.Equal(t, []instance.Attachment{{ID: "a0"}, {ID: "a1"}}, spec.Attachments)
}

func TestPrepareWithTags(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Tags": {"tag1": "val1"}
}`),
instance.Spec{},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "", spec.Init)
require.Equal(t, map[string]string{"tag1": "val1"}, spec.Tags)
}

func TestPrepareWithTagsAndInstanceSpecTags(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Tags": {"tag1": "val1"}
}`),
instance.Spec{
Tags: map[string]string{"t1": "v1"},
},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "", spec.Init)
require.Equal(t,
map[string]string{"t1": "v1", "tag1": "val1"},
spec.Tags)
}

func TestPrepareWithInit(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Init": ["line1", "line2"]
}`),
instance.Spec{},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "line1\nline2", spec.Init)
require.Nil(t, spec.Tags)
}

func TestPrepareWithInitAndInstanceSpecInit(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"Init": ["line2", "line3"]
}`),
instance.Spec{
Init: "l0\nl1",
},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "l0\nl1\nline2\nline3", spec.Init)
require.Nil(t, spec.Tags)
}

func TestPrepareWithInitScriptAndInstanceSpecInit(t *testing.T) {
plugin := NewPlugin()
require.NotNil(t, plugin)
spec, err := plugin.Prepare(
types.AnyString(`{
"InitScriptTemplateURL": "str://{{ var \"my-var\" \"value\" }}echo {{ var \"my-var\" }}"
}`),
instance.Spec{
Init: "l0\nl1",
},
group_types.AllocationMethod{Size: 1},
group_types.Index{Group: group.ID("group"), Sequence: 0})
require.NoError(t, err)
require.Equal(t, "l0\nl1\necho value", spec.Init)
require.Nil(t, spec.Tags)
}
3 changes: 3 additions & 0 deletions examples/flavor/vanilla/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/cli"
flavor_plugin "github.com/docker/infrakit/pkg/rpc/flavor"
"github.com/docker/infrakit/pkg/template"
"github.com/spf13/cobra"
)

var defaultTemplateOptions = template.Options{MultiPass: true}

func main() {

cmd := &cobra.Command{
Expand Down