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

Commit dd74ee6

Browse files
author
David Chung
authored
Add launch index in flavor.Prepare call (#443)
Signed-off-by: David Chung <[email protected]>
1 parent f8ea16b commit dd74ee6

File tree

28 files changed

+255
-64
lines changed

28 files changed

+255
-64
lines changed

cmd/cli/flavor.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1414
flavor_plugin "github.com/docker/infrakit/pkg/rpc/flavor"
1515
"github.com/docker/infrakit/pkg/spi/flavor"
16+
"github.com/docker/infrakit/pkg/spi/group"
1617
"github.com/docker/infrakit/pkg/spi/instance"
1718
"github.com/docker/infrakit/pkg/types"
1819
"github.com/spf13/cobra"
@@ -53,6 +54,9 @@ func flavorPluginCommand(plugins func() discovery.Plugins) *cobra.Command {
5354

5455
logicalIDs := []string{}
5556
groupSize := uint(0)
57+
groupID := ""
58+
groupSequence := uint(0)
59+
5660
addAllocationMethodFlags := func(cmd *cobra.Command) {
5761
cmd.Flags().StringSliceVar(
5862
&logicalIDs,
@@ -65,7 +69,6 @@ func flavorPluginCommand(plugins func() discovery.Plugins) *cobra.Command {
6569
0,
6670
"Group Size to use as the Allocation method")
6771
}
68-
6972
allocationMethodFromFlags := func() group_types.AllocationMethod {
7073
ids := []instance.LogicalID{}
7174
for _, id := range logicalIDs {
@@ -78,6 +81,23 @@ func flavorPluginCommand(plugins func() discovery.Plugins) *cobra.Command {
7881
}
7982
}
8083

84+
indexFlags := func(cmd *cobra.Command) {
85+
cmd.Flags().StringVar(
86+
&groupID,
87+
"index-group",
88+
"",
89+
"ID of the group")
90+
cmd.Flags().UintVar(
91+
&groupSequence,
92+
"index-sequence",
93+
0,
94+
"Sequence number within the group")
95+
}
96+
97+
indexFromFlags := func() group_types.Index {
98+
return group_types.Index{Group: group.ID(groupID), Sequence: groupSequence}
99+
}
100+
81101
validate := &cobra.Command{
82102
Use: "validate <flavor configuration file>",
83103
Short: "validate a flavor configuration",
@@ -132,7 +152,9 @@ func flavorPluginCommand(plugins func() discovery.Plugins) *cobra.Command {
132152
spec, err = flavorPlugin.Prepare(
133153
types.AnyBytes(flavorProperties),
134154
spec,
135-
allocationMethodFromFlags())
155+
allocationMethodFromFlags(),
156+
indexFromFlags(),
157+
)
136158
if err == nil {
137159
buff, err = json.MarshalIndent(spec, " ", " ")
138160
if err == nil {
@@ -143,6 +165,7 @@ func flavorPluginCommand(plugins func() discovery.Plugins) *cobra.Command {
143165
},
144166
}
145167
addAllocationMethodFlags(prepare)
168+
indexFlags(prepare)
146169
cmd.AddCommand(prepare)
147170

148171
healthy := &cobra.Command{

docs/plugins/flavor.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Flavor plugin API
22

3-
<!-- SOURCE-CHECKSUM pkg/spi/flavor/* 76aecf22acb4af73f36857bb2fad861896ae0b62 -->
3+
<!-- SOURCE-CHECKSUM pkg/spi/flavor/* e27841cf1ead137533b608b3ccc90ab72596532a -->
44

55
## API
66

@@ -52,6 +52,10 @@ may add, remove, or modify any fields in the Instance `Spec` by returning the de
5252
"Allocation": {
5353
"Size": 1,
5454
"LogicalIDs": ["logical_id_1"]
55+
},
56+
"Index" : {
57+
"Group" : "workers",
58+
"Sequence" : 100
5559
}
5660
}
5761
```
@@ -60,6 +64,7 @@ Parameters:
6064
- `Properties`: A JSON object representing the Flavor. The schema is defined by the Flavor plugin in use.
6165
- `Spec`: The [Spec](types.md#instance-spec) of the Instance being prepared.
6266
- `Allocation`: An [Allocation](types.md#allocation)
67+
- `Index`: an [Index](types.md#index)
6368

6469
#### Response
6570
```json

docs/plugins/types.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ Fields:
1313
- `Size`: An integer, the number of instances to maintain in the Group.
1414
- `LogicalIDs`: An array of strings, the logical identifeirs to maintain in the group.
1515

16+
# Index
17+
Index is a context object that is used to denote the instance's relationship with respect to the group it belongs.
18+
An Index has two fields: a group ID and a sequence number. The group ID is the identifier of the group, while the
19+
sequence number represents the order in which the instance is created with respect to the group. So for a group of
20+
size N, the sequence number will be 0, 1, 2, ... N-1. This can be used by the flavor plugin to determine which one
21+
of the instances, as represented by a fixed set of nodes (e.g. pets, or stateful nodes), the current instance is.
22+
In cases where provisioning instances where IP addresses cannot be assigned upfront, this provides a mechanism for
23+
looking up which IP address a particular node represents if given a list of IP addresses as a result of instance
24+
creations for this group. A similar concept can be found in AWS Autoscaling group's Launch index.
25+
26+
1627
# Group ID
1728
A globally-unique string identifier for an Group.
1829

examples/flavor/combo/flavor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ func mergeSpecs(initial instance.Spec, specs []instance.Spec) (instance.Spec, er
136136

137137
func (f flavorCombo) Prepare(flavor *types.Any,
138138
inst instance.Spec,
139-
allocation group_types.AllocationMethod) (instance.Spec, error) {
139+
allocation group_types.AllocationMethod,
140+
index group_types.Index) (instance.Spec, error) {
140141

141142
combo := Spec{}
142143
err := flavor.Decode(&combo)
@@ -154,7 +155,7 @@ func (f flavorCombo) Prepare(flavor *types.Any,
154155
return inst, err
155156
}
156157

157-
flavorOutput, err := plugin.Prepare(pluginSpec.Properties, clone, allocation)
158+
flavorOutput, err := plugin.Prepare(pluginSpec.Properties, clone, allocation, index)
158159
if err != nil {
159160
return inst, err
160161
}

examples/flavor/combo/flavor_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import (
66

77
mock_flavor "github.com/docker/infrakit/pkg/mock/spi/flavor"
88
"github.com/docker/infrakit/pkg/plugin"
9-
"github.com/docker/infrakit/pkg/plugin/group"
9+
group_plugin "github.com/docker/infrakit/pkg/plugin/group"
1010
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1111
"github.com/docker/infrakit/pkg/spi/flavor"
12+
"github.com/docker/infrakit/pkg/spi/group"
1213
"github.com/docker/infrakit/pkg/spi/instance"
1314
"github.com/docker/infrakit/pkg/types"
1415
"github.com/golang/mock/gomock"
@@ -28,7 +29,7 @@ var inst = instance.Spec{
2829
Attachments: []instance.Attachment{{ID: "att1", Type: "nic"}},
2930
}
3031

31-
func pluginLookup(plugins map[string]flavor.Plugin) group.FlavorPluginLookup {
32+
func pluginLookup(plugins map[string]flavor.Plugin) group_plugin.FlavorPluginLookup {
3233
return func(key plugin.Name) (flavor.Plugin, error) {
3334
plugin, has := plugins[key.String()]
3435
if has {
@@ -63,24 +64,24 @@ func TestMergeBehavior(t *testing.T) {
6364
}`)
6465

6566
allocation := group_types.AllocationMethod{Size: 1}
66-
67-
a.EXPECT().Prepare(types.AnyString(`{"a": "1"}`), inst, allocation).Return(instance.Spec{
67+
index := group_types.Index{Group: group.ID("group"), Sequence: 0}
68+
a.EXPECT().Prepare(types.AnyString(`{"a": "1"}`), inst, allocation, index).Return(instance.Spec{
6869
Properties: inst.Properties,
6970
Tags: map[string]string{"a": "1", "c": "4"},
7071
Init: "init data a",
7172
LogicalID: inst.LogicalID,
7273
Attachments: []instance.Attachment{{ID: "a", Type: "nic"}},
7374
}, nil)
7475

75-
b.EXPECT().Prepare(types.AnyString(`{"b": "2"}`), inst, allocation).Return(instance.Spec{
76+
b.EXPECT().Prepare(types.AnyString(`{"b": "2"}`), inst, allocation, index).Return(instance.Spec{
7677
Properties: inst.Properties,
7778
Tags: map[string]string{"b": "2", "c": "5"},
7879
Init: "init data b",
7980
LogicalID: inst.LogicalID,
8081
Attachments: []instance.Attachment{{ID: "b", Type: "gpu"}},
8182
}, nil)
8283

83-
result, err := combo.Prepare(flavorProperties, inst, group_types.AllocationMethod{Size: 1})
84+
result, err := combo.Prepare(flavorProperties, inst, group_types.AllocationMethod{Size: 1}, index)
8485
require.NoError(t, err)
8586

8687
expected := instance.Spec{
@@ -127,24 +128,24 @@ func TestMergeNoLogicalID(t *testing.T) {
127128
}`)
128129

129130
allocation := group_types.AllocationMethod{Size: 1}
130-
131-
a.EXPECT().Prepare(types.AnyString(`{"a": "1"}`), inst, allocation).Return(instance.Spec{
131+
index := group_types.Index{Group: group.ID("group"), Sequence: 0}
132+
a.EXPECT().Prepare(types.AnyString(`{"a": "1"}`), inst, allocation, index).Return(instance.Spec{
132133
Properties: inst.Properties,
133134
Tags: map[string]string{"a": "1", "c": "4"},
134135
Init: "init data a",
135136
LogicalID: inst.LogicalID,
136137
Attachments: []instance.Attachment{{ID: "a", Type: "nic"}},
137138
}, nil)
138139

139-
b.EXPECT().Prepare(types.AnyString(`{"b": "2"}`), inst, allocation).Return(instance.Spec{
140+
b.EXPECT().Prepare(types.AnyString(`{"b": "2"}`), inst, allocation, index).Return(instance.Spec{
140141
Properties: inst.Properties,
141142
Tags: map[string]string{"b": "2", "c": "5"},
142143
Init: "init data b",
143144
LogicalID: inst.LogicalID,
144145
Attachments: []instance.Attachment{{ID: "b", Type: "gpu"}},
145146
}, nil)
146147

147-
result, err := combo.Prepare(flavorProperties, inst, group_types.AllocationMethod{Size: 1})
148+
result, err := combo.Prepare(flavorProperties, inst, group_types.AllocationMethod{Size: 1}, index)
148149
require.NoError(t, err)
149150

150151
expected := instance.Spec{

examples/flavor/swarm/flavor.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ func (s *baseFlavor) Healthy(flavorProperties *types.Any, inst instance.Descript
210210
}
211211

212212
func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceSpec instance.Spec,
213-
allocation group_types.AllocationMethod) (instance.Spec, error) {
213+
allocation group_types.AllocationMethod,
214+
index group_types.Index) (instance.Spec, error) {
214215

215216
spec := Spec{}
216217

@@ -269,6 +270,7 @@ func (s *baseFlavor) prepare(role string, flavorProperties *types.Any, instanceS
269270
flavorSpec: spec,
270271
instanceSpec: instanceSpec,
271272
allocation: allocation,
273+
index: index,
272274
swarmStatus: swarmStatus,
273275
nodeInfo: node,
274276
link: *link,
@@ -407,6 +409,7 @@ type templateContext struct {
407409
flavorSpec Spec
408410
instanceSpec instance.Spec
409411
allocation group_types.AllocationMethod
412+
index group_types.Index
410413
swarmStatus *swarm.Swarm
411414
nodeInfo *swarm.Node
412415
link types.Link
@@ -446,6 +449,13 @@ func (c *templateContext) Funcs() []template.Function {
446449
return c.allocation
447450
},
448451
},
452+
{
453+
Name: "INDEX",
454+
Description: []string{"The launch index of this instance. Contains the group ID and the sequence number of instance."},
455+
Func: func() interface{} {
456+
return c.index
457+
},
458+
},
449459
{
450460
Name: "INFRAKIT_LABELS",
451461
Description: []string{"The Docker engine labels to be applied for linking the Docker engine to this instance."},

examples/flavor/swarm/flavor_test.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
mock_client "github.com/docker/infrakit/pkg/mock/docker/docker/client"
1212
group_types "github.com/docker/infrakit/pkg/plugin/group/types"
1313
"github.com/docker/infrakit/pkg/spi/flavor"
14+
"github.com/docker/infrakit/pkg/spi/group"
1415
"github.com/docker/infrakit/pkg/spi/instance"
1516
"github.com/docker/infrakit/pkg/template"
1617
"github.com/docker/infrakit/pkg/types"
@@ -117,10 +118,12 @@ func TestWorker(t *testing.T) {
117118
client.EXPECT().NodeInspectWithRaw(gomock.Any(), nodeID).Return(nodeInfo, nil, nil).AnyTimes()
118119
client.EXPECT().Close().AnyTimes()
119120

121+
index := group_types.Index{Group: group.ID("group"), Sequence: 0}
120122
details, err := flavorImpl.Prepare(
121123
types.AnyString(`{}`),
122124
instance.Spec{Tags: map[string]string{"a": "b"}},
123-
group_types.AllocationMethod{Size: 5})
125+
group_types.AllocationMethod{Size: 5},
126+
index)
124127
require.NoError(t, err)
125128
require.Equal(t, "b", details.Tags["a"])
126129

@@ -188,11 +191,13 @@ func TestManager(t *testing.T) {
188191
client.EXPECT().NodeInspectWithRaw(gomock.Any(), nodeID).Return(nodeInfo, nil, nil).AnyTimes()
189192
client.EXPECT().Close().AnyTimes()
190193

194+
index := group_types.Index{Group: group.ID("group"), Sequence: 0}
191195
id := instance.LogicalID("127.0.0.1")
192196
details, err := flavorImpl.Prepare(
193197
types.AnyString(`{"Attachments": {"127.0.0.1": [{"ID": "a", "Type": "gpu"}]}}`),
194198
instance.Spec{Tags: map[string]string{"a": "b"}, LogicalID: &id},
195-
group_types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}})
199+
group_types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}},
200+
index)
196201
require.NoError(t, err)
197202
require.Equal(t, "b", details.Tags["a"])
198203

@@ -208,11 +213,13 @@ func TestManager(t *testing.T) {
208213
require.Contains(t, details.Init, associationID)
209214

210215
// another instance -- note that this id is not the first in the allocation list of logical ids.
216+
index = group_types.Index{Group: group.ID("group"), Sequence: 1}
211217
id = instance.LogicalID("172.200.100.2")
212218
details, err = flavorImpl.Prepare(
213219
types.AnyString(`{"Attachments": {"172.200.100.2": [{"ID": "a", "Type": "gpu"}]}}`),
214220
instance.Spec{Tags: map[string]string{"a": "b"}, LogicalID: &id},
215-
group_types.AllocationMethod{LogicalIDs: []instance.LogicalID{"172.200.100.1", "172.200.100.2"}})
221+
group_types.AllocationMethod{LogicalIDs: []instance.LogicalID{"172.200.100.1", "172.200.100.2"}},
222+
index)
216223
require.NoError(t, err)
217224

218225
require.Contains(t, details.Init, swarmInfo.JoinTokens.Manager)
@@ -239,3 +246,50 @@ func TestManager(t *testing.T) {
239246

240247
close(managerStop)
241248
}
249+
250+
func TestTemplateFunctions(t *testing.T) {
251+
ctrl := gomock.NewController(t)
252+
defer ctrl.Finish()
253+
254+
managerStop := make(chan struct{})
255+
256+
client := mock_client.NewMockAPIClientCloser(ctrl)
257+
258+
flavorImpl := NewManagerFlavor(plugins, func(Spec) (docker.APIClientCloser, error) {
259+
return client, nil
260+
}, templ(DefaultManagerInitScriptTemplate), managerStop)
261+
262+
swarmInfo := swarm.Swarm{
263+
ClusterInfo: swarm.ClusterInfo{ID: "ClusterUUID"},
264+
JoinTokens: swarm.JoinTokens{
265+
Manager: "ManagerToken",
266+
Worker: "WorkerToken",
267+
},
268+
}
269+
270+
client.EXPECT().SwarmInspect(gomock.Any()).Return(swarmInfo, nil).AnyTimes()
271+
client.EXPECT().Info(gomock.Any()).Return(infoResponse, nil).AnyTimes()
272+
nodeInfo := swarm.Node{ManagerStatus: &swarm.ManagerStatus{Addr: "1.2.3.4"}}
273+
client.EXPECT().NodeInspectWithRaw(gomock.Any(), nodeID).Return(nodeInfo, nil, nil).AnyTimes()
274+
client.EXPECT().Close().AnyTimes()
275+
276+
initTemplate := `{{/* totally not useful init script just for test*/}}{{ INDEX.Group }},{{ INDEX.Sequence }}`
277+
278+
properties := types.AnyString(`
279+
{
280+
"Attachments": {"127.0.0.1": [{"ID": "a", "Type": "gpu"}]},
281+
"InitScriptTemplateURL" : "str://` + initTemplate + `"
282+
}
283+
`)
284+
285+
index := group_types.Index{Group: group.ID("group"), Sequence: 100}
286+
id := instance.LogicalID("127.0.0.1")
287+
details, err := flavorImpl.Prepare(properties,
288+
instance.Spec{Tags: map[string]string{"a": "b"}, LogicalID: &id},
289+
group_types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}},
290+
index)
291+
require.NoError(t, err)
292+
require.Equal(t, fmt.Sprintf("%v,%v", index.Group, index.Sequence), details.Init)
293+
294+
close(managerStop)
295+
}

examples/flavor/swarm/manager.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func (s *ManagerFlavor) Validate(flavorProperties *types.Any, allocation group_t
5555

5656
// Prepare sets up the provisioner / instance plugin's spec based on information about the swarm to join.
5757
func (s *ManagerFlavor) Prepare(flavorProperties *types.Any,
58-
instanceSpec instance.Spec, allocation group_types.AllocationMethod) (instance.Spec, error) {
59-
return s.baseFlavor.prepare("manager", flavorProperties, instanceSpec, allocation)
58+
instanceSpec instance.Spec, allocation group_types.AllocationMethod,
59+
index group_types.Index) (instance.Spec, error) {
60+
return s.baseFlavor.prepare("manager", flavorProperties, instanceSpec, allocation, index)
6061
}

examples/flavor/swarm/worker.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ type WorkerFlavor struct {
3333

3434
// Prepare sets up the provisioner / instance plugin's spec based on information about the swarm to join.
3535
func (s *WorkerFlavor) Prepare(flavorProperties *types.Any, instanceSpec instance.Spec,
36-
allocation group_types.AllocationMethod) (instance.Spec, error) {
37-
return s.baseFlavor.prepare("worker", flavorProperties, instanceSpec, allocation)
36+
allocation group_types.AllocationMethod,
37+
index group_types.Index) (instance.Spec, error) {
38+
return s.baseFlavor.prepare("worker", flavorProperties, instanceSpec, allocation, index)
3839
}
3940

4041
// Drain in the case of worker will force a node removal in the swarm.

examples/flavor/vanilla/flavor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ func (f vanillaFlavor) Drain(flavorProperties *types.Any, inst instance.Descript
4444

4545
func (f vanillaFlavor) Prepare(flavor *types.Any,
4646
instance instance.Spec,
47-
allocation group_types.AllocationMethod) (instance.Spec, error) {
47+
allocation group_types.AllocationMethod,
48+
index group_types.Index) (instance.Spec, error) {
4849

4950
s := Spec{}
5051
err := flavor.Decode(&s)

0 commit comments

Comments
 (0)