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

Commit a1e94af

Browse files
authored
Add a Type field to Attachments (#316)
Signed-off-by: Bill Farner <[email protected]>
1 parent 227dc68 commit a1e94af

File tree

6 files changed

+79
-30
lines changed

6 files changed

+79
-30
lines changed

docs/plugins/instance.md

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

3-
<!-- SOURCE-CHECKSUM pkg/spi/instance/* 0c778e96cbeb32043532709412e15e6cc86778d7153c65b1d7422b9f0b5901c89362666bfdd75de7 -->
3+
<!-- SOURCE-CHECKSUM pkg/spi/instance/* 0c778e96cbeb32043532709412e15e6cc86778d7338393f886f528c3824986fc97cb27410aefd8e2 -->
44

55
## API
66

@@ -39,7 +39,7 @@ Provisions a new instance.
3939
"Tags": {"tag_key": "tag_value"},
4040
"Init": "",
4141
"LogicalID": "logical_id",
42-
"Attachments": ["attachment_id"]
42+
"Attachments": [{"ID": "attachment_id", "Type": "block-device"}]
4343
}
4444
}
4545
```

docs/plugins/types.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ Fields:
3333
- `Properties`: A JSON object representing the Group. The schema is defined by the Group plugin in use.
3434

3535
### Instance Attachment
36-
An identifier for an external entity associated with an Instance. The meaning of Attachments and handling of Attachment
37-
IDs is defined by the Instance plugin.
36+
An external entity associated with an Instance. The meaning of an Attachment is defined by the Instance plugin.
37+
38+
Fields:
39+
- `ID`: A unique identifier for the Attachment
40+
- `Type`: An identifier for the kind of attachment.
3841

3942

4043
### Instance Description

pkg/example/flavor/combo/flavor_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var inst = instance.Spec{
2828
Tags: map[string]string{},
2929
Init: "",
3030
LogicalID: logicalID("id"),
31-
Attachments: []instance.Attachment{"att1"},
31+
Attachments: []instance.Attachment{{ID: "att1", Type: "nic"}},
3232
}
3333

3434
func pluginLookup(plugins map[string]flavor.Plugin) group.FlavorPluginLookup {
@@ -72,15 +72,15 @@ func TestMergeBehavior(t *testing.T) {
7272
Tags: map[string]string{"a": "1", "c": "4"},
7373
Init: "init data a",
7474
LogicalID: inst.LogicalID,
75-
Attachments: []instance.Attachment{"a"},
75+
Attachments: []instance.Attachment{{ID: "a", Type: "nic"}},
7676
}, nil)
7777

7878
b.EXPECT().Prepare(json.RawMessage(`{"b": "2"}`), inst, allocation).Return(instance.Spec{
7979
Properties: inst.Properties,
8080
Tags: map[string]string{"b": "2", "c": "5"},
8181
Init: "init data b",
8282
LogicalID: inst.LogicalID,
83-
Attachments: []instance.Attachment{"b"},
83+
Attachments: []instance.Attachment{{ID: "b", Type: "gpu"}},
8484
}, nil)
8585

8686
result, err := combo.Prepare(flavorProperties, inst, types.AllocationMethod{Size: 1})
@@ -91,7 +91,7 @@ func TestMergeBehavior(t *testing.T) {
9191
Tags: map[string]string{"a": "1", "b": "2", "c": "5"},
9292
Init: "init data a\ninit data b",
9393
LogicalID: inst.LogicalID,
94-
Attachments: []instance.Attachment{"att1", "a", "b"},
94+
Attachments: []instance.Attachment{{ID: "att1", Type: "nic"}, {ID: "a", Type: "nic"}, {ID: "b", Type: "gpu"}},
9595
}
9696
require.Equal(t, expected, result)
9797
}
@@ -103,7 +103,7 @@ func TestMergeNoLogicalID(t *testing.T) {
103103
Properties: jsonPtr("{}"),
104104
Tags: map[string]string{},
105105
Init: "",
106-
Attachments: []instance.Attachment{"att1"},
106+
Attachments: []instance.Attachment{{ID: "att1", Type: "nic"}},
107107
}
108108

109109
ctrl := gomock.NewController(t)
@@ -136,15 +136,15 @@ func TestMergeNoLogicalID(t *testing.T) {
136136
Tags: map[string]string{"a": "1", "c": "4"},
137137
Init: "init data a",
138138
LogicalID: inst.LogicalID,
139-
Attachments: []instance.Attachment{"a"},
139+
Attachments: []instance.Attachment{{ID: "a", Type: "nic"}},
140140
}, nil)
141141

142142
b.EXPECT().Prepare(json.RawMessage(`{"b": "2"}`), inst, allocation).Return(instance.Spec{
143143
Properties: inst.Properties,
144144
Tags: map[string]string{"b": "2", "c": "5"},
145145
Init: "init data b",
146146
LogicalID: inst.LogicalID,
147-
Attachments: []instance.Attachment{"b"},
147+
Attachments: []instance.Attachment{{ID: "b", Type: "gpu"}},
148148
}, nil)
149149

150150
result, err := combo.Prepare(flavorProperties, inst, types.AllocationMethod{Size: 1})
@@ -155,7 +155,7 @@ func TestMergeNoLogicalID(t *testing.T) {
155155
Tags: map[string]string{"a": "1", "b": "2", "c": "5"},
156156
Init: "init data a\ninit data b",
157157
LogicalID: inst.LogicalID,
158-
Attachments: []instance.Attachment{"att1", "a", "b"},
158+
Attachments: []instance.Attachment{{ID: "att1", Type: "nic"}, {ID: "a", Type: "nic"}, {ID: "b", Type: "gpu"}},
159159
}
160160
require.Equal(t, expected, result)
161161
}

pkg/example/flavor/swarm/flavor.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import (
2020
type nodeType string
2121

2222
const (
23-
worker nodeType = "worker"
24-
manager nodeType = "manager"
23+
worker nodeType = "worker"
24+
manager nodeType = "manager"
25+
ebsAttachment string = "ebs"
2526
)
2627

2728
// NewSwarmFlavor creates a flavor.Plugin that creates manager and worker nodes connected in a swarm.
@@ -61,14 +62,33 @@ func validateIDsAndAttachments(logicalIDs []instance.LogicalID, attachments map[
6162
}
6263
}
6364

65+
// Only EBS attachments are supported.
66+
for _, atts := range attachments {
67+
for _, attachment := range atts {
68+
if attachment.Type == "" {
69+
return fmt.Errorf(
70+
"Attachment Type %s must be specified for '%s'",
71+
ebsAttachment,
72+
attachment.ID)
73+
}
74+
75+
if attachment.Type != ebsAttachment {
76+
return fmt.Errorf(
77+
"Invalid attachment Type '%s', only %s is supported",
78+
attachment.Type,
79+
ebsAttachment)
80+
}
81+
}
82+
}
83+
6484
// Each attachment may only be used once.
65-
allAttachments := map[instance.Attachment]bool{}
66-
for _, att := range attachments {
67-
for _, attachment := range att {
68-
if _, exists := allAttachments[attachment]; exists {
69-
return fmt.Errorf("Attachment %v specified more than once", attachment)
85+
allAttachmentIDs := map[string]bool{}
86+
for _, atts := range attachments {
87+
for _, attachment := range atts {
88+
if _, exists := allAttachmentIDs[attachment.ID]; exists {
89+
return fmt.Errorf("Attachment %v specified more than once", attachment.ID)
7090
}
71-
allAttachments[attachment] = true
91+
allAttachmentIDs[attachment.ID] = true
7292
}
7393
}
7494

@@ -100,7 +120,7 @@ func (s swarmFlavor) Validate(flavorProperties json.RawMessage, allocation types
100120
if properties.Type == manager {
101121
for _, id := range allocation.LogicalIDs {
102122
if att, exists := properties.Attachments[id]; !exists || len(att) == 0 {
103-
log.Warnf("LogicalID %s has no attachments, which is needed for durability")
123+
log.Warnf("LogicalID %s has no attachments, which is needed for durability", id)
104124
}
105125
}
106126
}

pkg/example/flavor/swarm/flavor_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestValidate(t *testing.T) {
3333
json.RawMessage(`{
3434
"Type": "manager",
3535
"DockerRestartCommand": "systemctl restart docker",
36-
"Attachments": {"127.0.0.1": ["a", "b"]}}`),
36+
"Attachments": {"127.0.0.1": [{"ID": "a", "Type": "ebs"}, {"ID": "b", "Type": "ebs"}]}}`),
3737
types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}}))
3838

3939
require.Error(t, swarmFlavor.Validate(json.RawMessage(`{"type": "other"}`), types.AllocationMethod{Size: 5}))
@@ -50,10 +50,20 @@ func TestValidate(t *testing.T) {
5050
json.RawMessage(`{
5151
"Type": "manager",
5252
"DockerRestartCommand": "systemctl restart docker",
53-
"Attachments": {"127.0.0.1": ["a"], "127.0.0.2": ["a"]}}`),
53+
"Attachments": {"127.0.0.1": [{"ID": "a", "Type": "ebs"}], "127.0.0.2": [{"ID": "a", "Type": "ebs"}]}}`),
5454
types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1", "127.0.0.2", "127.0.0.3"}})
5555
require.Error(t, err)
5656
require.Equal(t, "Attachment a specified more than once", err.Error())
57+
58+
// Unsupported Attachment Type.
59+
err = swarmFlavor.Validate(
60+
json.RawMessage(`{
61+
"Type": "manager",
62+
"DockerRestartCommand": "systemctl restart docker",
63+
"Attachments": {"127.0.0.1": [{"ID": "a", "Type": "keyboard"}]}}`),
64+
types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}})
65+
require.Error(t, err)
66+
require.Equal(t, "Invalid attachment Type 'keyboard', only ebs is supported", err.Error())
5767
}
5868

5969
func TestWorker(t *testing.T) {
@@ -142,7 +152,7 @@ func TestManager(t *testing.T) {
142152

143153
id := instance.LogicalID("127.0.0.1")
144154
details, err := flavorImpl.Prepare(
145-
json.RawMessage(`{"Type": "manager", "Attachments": {"127.0.0.1": ["a"]}}`),
155+
json.RawMessage(`{"Type": "manager", "Attachments": {"127.0.0.1": [{"ID": "a", "Type": "gpu"}]}}`),
146156
instance.Spec{Tags: map[string]string{"a": "b"}, LogicalID: &id},
147157
types.AllocationMethod{LogicalIDs: []instance.LogicalID{"127.0.0.1"}})
148158
require.NoError(t, err)
@@ -157,7 +167,7 @@ func TestManager(t *testing.T) {
157167
require.NotContains(t, details.Init, swarmInfo.JoinTokens.Worker)
158168
require.Contains(t, details.Init, nodeInfo.ManagerStatus.Addr)
159169

160-
require.Equal(t, []instance.Attachment{"a"}, details.Attachments)
170+
require.Equal(t, []instance.Attachment{{ID: "a", Type: "gpu"}}, details.Attachments)
161171

162172
// An instance with no association information is considered unhealthy.
163173
health, err := flavorImpl.Healthy(json.RawMessage("{}"), instance.Description{})

pkg/spi/instance/types.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,29 @@ type Description struct {
1818
type LogicalID string
1919

2020
// Attachment is an identifier for a resource to attach to an instance.
21-
type Attachment string
21+
type Attachment struct {
22+
// ID is the unique identifier for the attachment.
23+
ID string
24+
25+
// Type is the kind of attachment. This allows multiple attachments of different types, with the supported
26+
// types defined by the plugin.
27+
Type string
28+
}
2229

2330
// Spec is a specification of an instance to be provisioned
2431
type Spec struct {
25-
Properties *json.RawMessage
26-
Tags map[string]string
27-
Init string
28-
LogicalID *LogicalID
32+
// Properties is the opaque instance plugin configuration.
33+
Properties *json.RawMessage
34+
35+
// Tags are metadata that describes an instance.
36+
Tags map[string]string
37+
38+
// Init is the boot script to execute when the instance is created.
39+
Init string
40+
41+
// LogicalID is the logical identifier assigned to this instance, which may be absent.
42+
LogicalID *LogicalID
43+
44+
// Attachments are instructions for external entities that should be attached to the instance.
2945
Attachments []Attachment
3046
}

0 commit comments

Comments
 (0)