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

Commit 291de15

Browse files
author
David Chung
authored
Add support for accessing metadata in swarm templates (#412)
Signed-off-by: David Chung <[email protected]>
1 parent 6516ace commit 291de15

File tree

12 files changed

+149
-42
lines changed

12 files changed

+149
-42
lines changed

cmd/cli/manager.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/docker/infrakit/pkg/discovery"
99
"github.com/docker/infrakit/pkg/manager"
1010
"github.com/docker/infrakit/pkg/plugin"
11+
metadata_template "github.com/docker/infrakit/pkg/plugin/metadata/template"
1112
"github.com/docker/infrakit/pkg/rpc/client"
1213
group_plugin "github.com/docker/infrakit/pkg/rpc/group"
1314
manager_rpc "github.com/docker/infrakit/pkg/rpc/manager"
@@ -91,6 +92,21 @@ func managerCommand(plugins func() discovery.Plugins) *cobra.Command {
9192
if err != nil {
9293
return err
9394
}
95+
96+
engine.WithFunctions(func() []template.Function {
97+
return []template.Function{
98+
{
99+
Name: "metadata",
100+
Description: []string{
101+
"Metadata function takes a path of the form \"plugin_name/path/to/data\"",
102+
"and calls GET on the plugin with the path \"path/to/data\".",
103+
"It's identical to the CLI command infrakit metadata cat ...",
104+
},
105+
Func: metadata_template.MetadataFunc(plugins),
106+
},
107+
}
108+
})
109+
94110
view, err := engine.Render(nil)
95111
if err != nil {
96112
return err

cmd/cli/metadata.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,18 +213,21 @@ func metadataCommand(plugins func() discovery.Plugins) *cobra.Command {
213213
return err
214214
}
215215

216-
value, err := match.Get(path.Shift(1))
217-
if err == nil {
218-
if value != nil {
219-
str := value.String()
220-
if s, err := strconv.Unquote(value.String()); err == nil {
221-
str = s
216+
if path.Len() == 1 {
217+
fmt.Printf("%v\n", match != nil)
218+
} else {
219+
value, err := match.Get(path.Shift(1))
220+
if err == nil {
221+
if value != nil {
222+
str := value.String()
223+
if s, err := strconv.Unquote(value.String()); err == nil {
224+
str = s
225+
}
226+
fmt.Println(str)
222227
}
223-
fmt.Println(str)
228+
} else {
229+
log.Warningln("Cannot metadata cat on plugin", *first, "err=", err)
224230
}
225-
226-
} else {
227-
log.Warningln("Cannot metadata cat on plugin", *first, "err=", err)
228231
}
229232
}
230233
}

cmd/cli/template.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"fmt"
5+
"strings"
56

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

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

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

2931
// Add functions
32+
for _, global := range globals {
33+
kv := strings.Split(global, "=")
34+
if len(kv) != 2 {
35+
continue
36+
}
37+
key := strings.Trim(kv[0], " \t\n")
38+
val := strings.Trim(kv[1], " \t\n")
39+
if key != "" && val != "" {
40+
engine.Global(key, val)
41+
}
42+
}
43+
3044
engine.WithFunctions(func() []template.Function {
3145
return []template.Function{
3246
{
@@ -50,6 +64,7 @@ func templateCommand(plugins func() discovery.Plugins) *cobra.Command {
5064
},
5165
}
5266
cmd.Flags().StringVar(&templateURL, "url", "", "URL for the template")
67+
cmd.Flags().StringSliceVar(&globals, "global", []string{}, "key=value pairs of 'global' values")
5368

5469
return cmd
5570
}

examples/flavor/swarm/flavor.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import (
1010
"github.com/docker/docker/api/types/swarm"
1111
"github.com/docker/docker/client"
1212
"github.com/docker/go-connections/tlsconfig"
13+
"github.com/docker/infrakit/pkg/discovery"
1314
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1415
metadata_plugin "github.com/docker/infrakit/pkg/plugin/metadata"
16+
metadata_template "github.com/docker/infrakit/pkg/plugin/metadata/template"
1517
"github.com/docker/infrakit/pkg/spi/flavor"
1618
"github.com/docker/infrakit/pkg/spi/instance"
1719
"github.com/docker/infrakit/pkg/spi/metadata"
@@ -65,6 +67,7 @@ type baseFlavor struct {
6567
getDockerClient func(Spec) (client.APIClient, error)
6668
initScript *template.Template
6769
metadataPlugin metadata.Plugin
70+
plugins func() discovery.Plugins
6871
}
6972

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

185188
spec := Spec{}
189+
190+
if s.plugins == nil {
191+
return instanceSpec, fmt.Errorf("no plugin discovery")
192+
}
193+
186194
err := flavorProperties.Decode(&spec)
187195
if err != nil {
188196
return instanceSpec, err
@@ -217,7 +225,7 @@ func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceS
217225

218226
swarmStatus, node, err = swarmState(dockerClient)
219227
if err != nil {
220-
log.Warningln("Worker prepare:", err)
228+
log.Warningln("Cannot prepare:", err)
221229
}
222230

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

246+
initTemplate.WithFunctions(func() []template.Function {
247+
return []template.Function{
248+
{
249+
Name: "metadata",
250+
Description: []string{
251+
"Metadata function takes a path of the form \"plugin_name/path/to/data\"",
252+
"and calls GET on the plugin with the path \"path/to/data\".",
253+
"It's identical to the CLI command infrakit metadata cat ...",
254+
},
255+
Func: metadata_template.MetadataFunc(s.plugins),
256+
},
257+
}
258+
})
238259
initScript, err = initTemplate.Render(context)
239260

240261
log.Debugln(role, ">>> context.retries =", context.retries, "err=", err, "i=", i)

examples/flavor/swarm/flavor_test.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/docker/docker/api/types/filters"
99
"github.com/docker/docker/api/types/swarm"
1010
docker_client "github.com/docker/docker/client"
11+
"github.com/docker/infrakit/pkg/discovery"
1112
mock_client "github.com/docker/infrakit/pkg/mock/docker/docker/client"
1213
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1314
"github.com/docker/infrakit/pkg/spi/flavor"
@@ -26,16 +27,24 @@ func templ(tpl string) *template.Template {
2627
return t
2728
}
2829

30+
func plugins() discovery.Plugins {
31+
d, err := discovery.NewPluginDiscovery()
32+
if err != nil {
33+
panic(err)
34+
}
35+
return d
36+
}
37+
2938
func TestValidate(t *testing.T) {
3039
ctrl := gomock.NewController(t)
3140
defer ctrl.Finish()
3241
managerStop := make(chan struct{})
3342
workerStop := make(chan struct{})
3443

35-
managerFlavor := NewManagerFlavor(func(Spec) (docker_client.APIClient, error) {
44+
managerFlavor := NewManagerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
3645
return mock_client.NewMockAPIClient(ctrl), nil
3746
}, templ(DefaultManagerInitScriptTemplate), managerStop)
38-
workerFlavor := NewWorkerFlavor(func(Spec) (docker_client.APIClient, error) {
47+
workerFlavor := NewWorkerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
3948
return mock_client.NewMockAPIClient(ctrl), nil
4049
}, templ(DefaultWorkerInitScriptTemplate), workerStop)
4150

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

91100
client := mock_client.NewMockAPIClient(ctrl)
92101

93-
flavorImpl := NewWorkerFlavor(func(Spec) (docker_client.APIClient, error) {
102+
flavorImpl := NewWorkerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
94103
return client, nil
95104
}, templ(DefaultWorkerInitScriptTemplate), workerStop)
96105

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

161170
client := mock_client.NewMockAPIClient(ctrl)
162171

163-
flavorImpl := NewManagerFlavor(func(Spec) (docker_client.APIClient, error) {
172+
flavorImpl := NewManagerFlavor(plugins, func(Spec) (docker_client.APIClient, error) {
164173
return client, nil
165174
}, templ(DefaultManagerInitScriptTemplate), managerStop)
166175

examples/flavor/swarm/main.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,20 @@ var defaultTemplateOptions = template.Options{
2929

3030
func main() {
3131

32+
plugins := func() discovery.Plugins {
33+
d, err := discovery.NewPluginDiscovery()
34+
if err != nil {
35+
log.Fatalf("Failed to initialize plugin discovery: %s", err)
36+
os.Exit(1)
37+
}
38+
return d
39+
}
40+
3241
cmd := &cobra.Command{
3342
Use: os.Args[0],
3443
Short: "Docker Swarm flavor plugin",
3544
}
45+
3646
name := cmd.Flags().String("name", "flavor-swarm", "Plugin name to advertise for discovery")
3747
logLevel := cmd.Flags().Int("log", cli.DefaultLogLevel, "Logging level. 0 is least verbose. Max is 5")
3848
managerInitScriptTemplURL := cmd.Flags().String("manager-init-template", "", "URL, init script template for managers")
@@ -53,8 +63,9 @@ func main() {
5363

5464
managerStop := make(chan struct{})
5565
workerStop := make(chan struct{})
56-
managerFlavor := NewManagerFlavor(DockerClient, mt, managerStop)
57-
workerFlavor := NewWorkerFlavor(DockerClient, wt, workerStop)
66+
67+
managerFlavor := NewManagerFlavor(plugins, DockerClient, mt, managerStop)
68+
workerFlavor := NewWorkerFlavor(plugins, DockerClient, wt, workerStop)
5869

5970
cli.RunPlugin(*name,
6071

examples/flavor/swarm/manager.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
log "github.com/Sirupsen/logrus"
77
"github.com/docker/docker/client"
8+
"github.com/docker/infrakit/pkg/discovery"
89
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
910
"github.com/docker/infrakit/pkg/plugin/metadata"
1011
"github.com/docker/infrakit/pkg/spi/instance"
@@ -13,10 +14,11 @@ import (
1314
)
1415

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

19-
base := &baseFlavor{initScript: templ, getDockerClient: connect}
21+
base := &baseFlavor{initScript: templ, getDockerClient: connect, plugins: plugins}
2022
base.metadataPlugin = metadata.NewPluginFromChannel(base.runMetadataSnapshot(stop))
2123
return &ManagerFlavor{baseFlavor: base}
2224
}

examples/flavor/swarm/worker.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
docker_types "github.com/docker/docker/api/types"
88
"github.com/docker/docker/api/types/filters"
99
"github.com/docker/docker/client"
10+
"github.com/docker/infrakit/pkg/discovery"
1011
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1112
"github.com/docker/infrakit/pkg/plugin/metadata"
1213
"github.com/docker/infrakit/pkg/spi/instance"
@@ -16,10 +17,11 @@ import (
1617
)
1718

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

22-
base := &baseFlavor{initScript: templ, getDockerClient: connect}
24+
base := &baseFlavor{initScript: templ, getDockerClient: connect, plugins: plugins}
2325
base.metadataPlugin = metadata.NewPluginFromChannel(base.runMetadataSnapshot(stop))
2426
return &WorkerFlavor{baseFlavor: base}
2527
}

pkg/plugin/metadata/reflect.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,27 @@ func List(path []string, object interface{}) []string {
4242
if v == nil {
4343
return list
4444
}
45+
46+
val := reflect.Indirect(reflect.ValueOf(v))
4547
if any, is := v.(*types.Any); is {
46-
temp := map[string]interface{}{}
48+
var temp interface{}
4749
if err := any.Decode(&temp); err == nil {
48-
if len(temp) > 0 {
49-
return List([]string{"."}, temp)
50-
}
51-
return []string{}
50+
val = reflect.ValueOf(temp)
5251
}
53-
return []string{}
5452
}
5553

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

6461
case reflect.Map:
6562
for _, k := range val.MapKeys() {
6663
list = append(list, k.String())
6764
}
65+
6866
case reflect.Struct:
6967
vt := val.Type()
7068
for i := 0; i < vt.NumField(); i++ {
@@ -122,6 +120,14 @@ func get(path []string, object interface{}) interface{} {
122120
return object
123121
}
124122

123+
if any, is := object.(*types.Any); is {
124+
var temp interface{}
125+
if err := any.Decode(&temp); err == nil {
126+
return get(path, temp)
127+
}
128+
return nil
129+
}
130+
125131
key := path[0]
126132

127133
switch key {
@@ -139,14 +145,6 @@ func get(path []string, object interface{}) interface{} {
139145
return get(path, object)
140146
}
141147

142-
if any, is := object.(*types.Any); is {
143-
temp := map[string]interface{}{}
144-
if err := any.Decode(&temp); err == nil {
145-
return get(path[1:], temp)
146-
}
147-
return nil
148-
}
149-
150148
v := reflect.Indirect(reflect.ValueOf(object))
151149
switch v.Kind() {
152150
case reflect.Slice:

pkg/plugin/metadata/reflect_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"testing"
66

7+
"github.com/docker/infrakit/pkg/types"
78
"github.com/stretchr/testify/require"
89
)
910

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

42-
require.Equal(t, "id-network1", get(Path("region/us-west-1/vpc/vpc1/network/network1/id"), m))
43-
require.Equal(t, "id-network1", get(Path("region/us-west-1/vpc/vpc1/network/network1/id/"), m))
44+
require.Equal(t, "id-network1", Get(Path("region/us-west-1/vpc/vpc1/network/network1/id"), m))
45+
require.Equal(t, "id-network1", Get(Path("region/us-west-1/vpc/vpc1/network/network1/id/"), m))
4446
require.Equal(t, map[string]interface{}{"id": "id-network1"},
4547
get(Path("region/us-west-1/vpc/vpc1/network/network1"), m))
4648
require.Equal(t, map[string]interface{}{
@@ -65,6 +67,12 @@ func TestMap(t *testing.T) {
6567
require.Equal(t, []string{"us-west-1", "us-west-2"}, List(Path("region"), m))
6668
require.Equal(t, []string{"network1", "network2", "network3"}, List(Path("region/us-west-1/vpc/vpc1/network/"), m))
6769
require.Equal(t, []string{}, List(Path("region/us-west-2/metrics/instances/count"), m))
70+
require.Equal(t, []string{"[0]", "[1]"}, List(Path("region/us-west-2/instances"), m))
71+
require.Equal(t, []string{}, List(Path("region/us-west-2/instances/[0]"), m))
72+
require.Equal(t, "a", Get(Path("region/us-west-2/instances/[0]"), m))
73+
require.Equal(t, "b", Get(Path("region/us-west-2/instances/[1]"), m))
74+
require.Equal(t, "a", Get(Path("region/us-west-2/instances[0]"), m))
75+
require.Equal(t, "b", Get(Path("region/us-west-2/instances[1]"), m))
6876

6977
}
7078

0 commit comments

Comments
 (0)