Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Commit

Permalink
Add support for accessing metadata in swarm templates (#412)
Browse files Browse the repository at this point in the history
Signed-off-by: David Chung <[email protected]>
  • Loading branch information
David Chung authored Feb 26, 2017
1 parent 6516ace commit 291de15
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 42 deletions.
16 changes: 16 additions & 0 deletions cmd/cli/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/docker/infrakit/pkg/discovery"
"github.com/docker/infrakit/pkg/manager"
"github.com/docker/infrakit/pkg/plugin"
metadata_template "github.com/docker/infrakit/pkg/plugin/metadata/template"
"github.com/docker/infrakit/pkg/rpc/client"
group_plugin "github.com/docker/infrakit/pkg/rpc/group"
manager_rpc "github.com/docker/infrakit/pkg/rpc/manager"
Expand Down Expand Up @@ -91,6 +92,21 @@ func managerCommand(plugins func() discovery.Plugins) *cobra.Command {
if err != nil {
return err
}

engine.WithFunctions(func() []template.Function {
return []template.Function{
{
Name: "metadata",
Description: []string{
"Metadata function takes a path of the form \"plugin_name/path/to/data\"",
"and calls GET on the plugin with the path \"path/to/data\".",
"It's identical to the CLI command infrakit metadata cat ...",
},
Func: metadata_template.MetadataFunc(plugins),
},
}
})

view, err := engine.Render(nil)
if err != nil {
return err
Expand Down
23 changes: 13 additions & 10 deletions cmd/cli/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,21 @@ func metadataCommand(plugins func() discovery.Plugins) *cobra.Command {
return err
}

value, err := match.Get(path.Shift(1))
if err == nil {
if value != nil {
str := value.String()
if s, err := strconv.Unquote(value.String()); err == nil {
str = s
if path.Len() == 1 {
fmt.Printf("%v\n", match != nil)
} else {
value, err := match.Get(path.Shift(1))
if err == nil {
if value != nil {
str := value.String()
if s, err := strconv.Unquote(value.String()); err == nil {
str = s
}
fmt.Println(str)
}
fmt.Println(str)
} else {
log.Warningln("Cannot metadata cat on plugin", *first, "err=", err)
}

} else {
log.Warningln("Cannot metadata cat on plugin", *first, "err=", err)
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/cli/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"strings"

log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/discovery"
Expand All @@ -12,6 +13,7 @@ import (

func templateCommand(plugins func() discovery.Plugins) *cobra.Command {

globals := []string{}
templateURL := ""
cmd := &cobra.Command{
Use: "template",
Expand All @@ -27,6 +29,18 @@ func templateCommand(plugins func() discovery.Plugins) *cobra.Command {
}

// Add functions
for _, global := range globals {
kv := strings.Split(global, "=")
if len(kv) != 2 {
continue
}
key := strings.Trim(kv[0], " \t\n")
val := strings.Trim(kv[1], " \t\n")
if key != "" && val != "" {
engine.Global(key, val)
}
}

engine.WithFunctions(func() []template.Function {
return []template.Function{
{
Expand All @@ -50,6 +64,7 @@ func templateCommand(plugins func() discovery.Plugins) *cobra.Command {
},
}
cmd.Flags().StringVar(&templateURL, "url", "", "URL for the template")
cmd.Flags().StringSliceVar(&globals, "global", []string{}, "key=value pairs of 'global' values")

return cmd
}
23 changes: 22 additions & 1 deletion examples/flavor/swarm/flavor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig"
"github.com/docker/infrakit/pkg/discovery"
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
metadata_plugin "github.com/docker/infrakit/pkg/plugin/metadata"
metadata_template "github.com/docker/infrakit/pkg/plugin/metadata/template"
"github.com/docker/infrakit/pkg/spi/flavor"
"github.com/docker/infrakit/pkg/spi/instance"
"github.com/docker/infrakit/pkg/spi/metadata"
Expand Down Expand Up @@ -65,6 +67,7 @@ type baseFlavor struct {
getDockerClient func(Spec) (client.APIClient, error)
initScript *template.Template
metadataPlugin metadata.Plugin
plugins func() discovery.Plugins
}

// Runs a poller that periodically samples the swarm status and node info.
Expand Down Expand Up @@ -183,6 +186,11 @@ func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceS
allocation group_types.AllocationMethod) (instance.Spec, error) {

spec := Spec{}

if s.plugins == nil {
return instanceSpec, fmt.Errorf("no plugin discovery")
}

err := flavorProperties.Decode(&spec)
if err != nil {
return instanceSpec, err
Expand Down Expand Up @@ -217,7 +225,7 @@ func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceS

swarmStatus, node, err = swarmState(dockerClient)
if err != nil {
log.Warningln("Worker prepare:", err)
log.Warningln("Cannot prepare:", err)
}

swarmID = "?"
Expand All @@ -235,6 +243,19 @@ func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceS
link: *link,
}

initTemplate.WithFunctions(func() []template.Function {
return []template.Function{
{
Name: "metadata",
Description: []string{
"Metadata function takes a path of the form \"plugin_name/path/to/data\"",
"and calls GET on the plugin with the path \"path/to/data\".",
"It's identical to the CLI command infrakit metadata cat ...",
},
Func: metadata_template.MetadataFunc(s.plugins),
},
}
})
initScript, err = initTemplate.Render(context)

log.Debugln(role, ">>> context.retries =", context.retries, "err=", err, "i=", i)
Expand Down
17 changes: 13 additions & 4 deletions examples/flavor/swarm/flavor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
docker_client "github.com/docker/docker/client"
"github.com/docker/infrakit/pkg/discovery"
mock_client "github.com/docker/infrakit/pkg/mock/docker/docker/client"
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
"github.com/docker/infrakit/pkg/spi/flavor"
Expand All @@ -26,16 +27,24 @@ func templ(tpl string) *template.Template {
return t
}

func plugins() discovery.Plugins {
d, err := discovery.NewPluginDiscovery()
if err != nil {
panic(err)
}
return d
}

func TestValidate(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
managerStop := make(chan struct{})
workerStop := make(chan struct{})

managerFlavor := NewManagerFlavor(func(Spec) (docker_client.APIClient, error) {
managerFlavor := NewManagerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
return mock_client.NewMockAPIClient(ctrl), nil
}, templ(DefaultManagerInitScriptTemplate), managerStop)
workerFlavor := NewWorkerFlavor(func(Spec) (docker_client.APIClient, error) {
workerFlavor := NewWorkerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
return mock_client.NewMockAPIClient(ctrl), nil
}, templ(DefaultWorkerInitScriptTemplate), workerStop)

Expand Down Expand Up @@ -90,7 +99,7 @@ func TestWorker(t *testing.T) {

client := mock_client.NewMockAPIClient(ctrl)

flavorImpl := NewWorkerFlavor(func(Spec) (docker_client.APIClient, error) {
flavorImpl := NewWorkerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
return client, nil
}, templ(DefaultWorkerInitScriptTemplate), workerStop)

Expand Down Expand Up @@ -160,7 +169,7 @@ func TestManager(t *testing.T) {

client := mock_client.NewMockAPIClient(ctrl)

flavorImpl := NewManagerFlavor(func(Spec) (docker_client.APIClient, error) {
flavorImpl := NewManagerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
return client, nil
}, templ(DefaultManagerInitScriptTemplate), managerStop)

Expand Down
15 changes: 13 additions & 2 deletions examples/flavor/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ var defaultTemplateOptions = template.Options{

func main() {

plugins := func() discovery.Plugins {
d, err := discovery.NewPluginDiscovery()
if err != nil {
log.Fatalf("Failed to initialize plugin discovery: %s", err)
os.Exit(1)
}
return d
}

cmd := &cobra.Command{
Use: os.Args[0],
Short: "Docker Swarm flavor plugin",
}

name := cmd.Flags().String("name", "flavor-swarm", "Plugin name to advertise for discovery")
logLevel := cmd.Flags().Int("log", cli.DefaultLogLevel, "Logging level. 0 is least verbose. Max is 5")
managerInitScriptTemplURL := cmd.Flags().String("manager-init-template", "", "URL, init script template for managers")
Expand All @@ -53,8 +63,9 @@ func main() {

managerStop := make(chan struct{})
workerStop := make(chan struct{})
managerFlavor := NewManagerFlavor(DockerClient, mt, managerStop)
workerFlavor := NewWorkerFlavor(DockerClient, wt, workerStop)

managerFlavor := NewManagerFlavor(plugins, DockerClient, mt, managerStop)
workerFlavor := NewWorkerFlavor(plugins, DockerClient, wt, workerStop)

cli.RunPlugin(*name,

Expand Down
6 changes: 4 additions & 2 deletions examples/flavor/swarm/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/docker/client"
"github.com/docker/infrakit/pkg/discovery"
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
"github.com/docker/infrakit/pkg/plugin/metadata"
"github.com/docker/infrakit/pkg/spi/instance"
Expand All @@ -13,10 +14,11 @@ import (
)

// NewManagerFlavor creates a flavor.Plugin that creates manager and worker nodes connected in a swarm.
func NewManagerFlavor(connect func(Spec) (client.APIClient, error), templ *template.Template,
func NewManagerFlavor(plugins func() discovery.Plugins, connect func(Spec) (client.APIClient, error),
templ *template.Template,
stop <-chan struct{}) *ManagerFlavor {

base := &baseFlavor{initScript: templ, getDockerClient: connect}
base := &baseFlavor{initScript: templ, getDockerClient: connect, plugins: plugins}
base.metadataPlugin = metadata.NewPluginFromChannel(base.runMetadataSnapshot(stop))
return &ManagerFlavor{baseFlavor: base}
}
Expand Down
6 changes: 4 additions & 2 deletions examples/flavor/swarm/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
docker_types "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/docker/infrakit/pkg/discovery"
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
"github.com/docker/infrakit/pkg/plugin/metadata"
"github.com/docker/infrakit/pkg/spi/instance"
Expand All @@ -16,10 +17,11 @@ import (
)

// NewWorkerFlavor creates a flavor.Plugin that creates manager and worker nodes connected in a swarm.
func NewWorkerFlavor(connect func(Spec) (client.APIClient, error), templ *template.Template,
func NewWorkerFlavor(plugins func() discovery.Plugins, connect func(Spec) (client.APIClient, error),
templ *template.Template,
stop <-chan struct{}) *WorkerFlavor {

base := &baseFlavor{initScript: templ, getDockerClient: connect}
base := &baseFlavor{initScript: templ, getDockerClient: connect, plugins: plugins}
base.metadataPlugin = metadata.NewPluginFromChannel(base.runMetadataSnapshot(stop))
return &WorkerFlavor{baseFlavor: base}
}
Expand Down
30 changes: 14 additions & 16 deletions pkg/plugin/metadata/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,27 @@ func List(path []string, object interface{}) []string {
if v == nil {
return list
}

val := reflect.Indirect(reflect.ValueOf(v))
if any, is := v.(*types.Any); is {
temp := map[string]interface{}{}
var temp interface{}
if err := any.Decode(&temp); err == nil {
if len(temp) > 0 {
return List([]string{"."}, temp)
}
return []string{}
val = reflect.ValueOf(temp)
}
return []string{}
}

val := reflect.Indirect(reflect.ValueOf(v))
switch val.Kind() {
case reflect.Slice:
// this is a slice, so return the name as '[%d]'
for i := 0; i < val.Len(); i++ {
list = append(list, fmt.Sprintf("[%d]", i)) //val.Index(i).String())
list = append(list, fmt.Sprintf("[%d]", i))
}

case reflect.Map:
for _, k := range val.MapKeys() {
list = append(list, k.String())
}

case reflect.Struct:
vt := val.Type()
for i := 0; i < vt.NumField(); i++ {
Expand Down Expand Up @@ -122,6 +120,14 @@ func get(path []string, object interface{}) interface{} {
return object
}

if any, is := object.(*types.Any); is {
var temp interface{}
if err := any.Decode(&temp); err == nil {
return get(path, temp)
}
return nil
}

key := path[0]

switch key {
Expand All @@ -139,14 +145,6 @@ func get(path []string, object interface{}) interface{} {
return get(path, object)
}

if any, is := object.(*types.Any); is {
temp := map[string]interface{}{}
if err := any.Decode(&temp); err == nil {
return get(path[1:], temp)
}
return nil
}

v := reflect.Indirect(reflect.ValueOf(object))
switch v.Kind() {
case reflect.Slice:
Expand Down
12 changes: 10 additions & 2 deletions pkg/plugin/metadata/reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"testing"

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

Expand Down Expand Up @@ -38,9 +39,10 @@ func TestMap(t *testing.T) {
require.True(t, put(Path("region/us-west-2/vpc/vpc21/network/network210/id"), "id-network210", m))
require.True(t, put(Path("region/us-west-2/vpc/vpc21/network/network211/id"), "id-network211", m))
require.True(t, put(Path("region/us-west-2/metrics/instances/count"), 100, m))
require.True(t, put(Path("region/us-west-2/instances"), types.AnyValueMust([]string{"a", "b"}), m))

require.Equal(t, "id-network1", get(Path("region/us-west-1/vpc/vpc1/network/network1/id"), m))
require.Equal(t, "id-network1", get(Path("region/us-west-1/vpc/vpc1/network/network1/id/"), m))
require.Equal(t, "id-network1", Get(Path("region/us-west-1/vpc/vpc1/network/network1/id"), m))
require.Equal(t, "id-network1", Get(Path("region/us-west-1/vpc/vpc1/network/network1/id/"), m))
require.Equal(t, map[string]interface{}{"id": "id-network1"},
get(Path("region/us-west-1/vpc/vpc1/network/network1"), m))
require.Equal(t, map[string]interface{}{
Expand All @@ -65,6 +67,12 @@ func TestMap(t *testing.T) {
require.Equal(t, []string{"us-west-1", "us-west-2"}, List(Path("region"), m))
require.Equal(t, []string{"network1", "network2", "network3"}, List(Path("region/us-west-1/vpc/vpc1/network/"), m))
require.Equal(t, []string{}, List(Path("region/us-west-2/metrics/instances/count"), m))
require.Equal(t, []string{"[0]", "[1]"}, List(Path("region/us-west-2/instances"), m))
require.Equal(t, []string{}, List(Path("region/us-west-2/instances/[0]"), m))
require.Equal(t, "a", Get(Path("region/us-west-2/instances/[0]"), m))
require.Equal(t, "b", Get(Path("region/us-west-2/instances/[1]"), m))
require.Equal(t, "a", Get(Path("region/us-west-2/instances[0]"), m))
require.Equal(t, "b", Get(Path("region/us-west-2/instances[1]"), m))

}

Expand Down
Loading

0 comments on commit 291de15

Please sign in to comment.