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

Commit 3714614

Browse files
kaufersDavid Chung
authored andcommitted
Enrollment controller updates to use instance Properties as key (#701)
Signed-off-by: Steven Kaufer <[email protected]>
1 parent 07a8e08 commit 3714614

File tree

6 files changed

+265
-7
lines changed

6 files changed

+265
-7
lines changed

pkg/controller/enrollment/enroller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type enroller struct {
4646

4747
// template that we use to render with a source instance.Description to get the link Key
4848
sourceKeySelectorTemplate *template.Template
49+
// template that we use to render with an enrollment instance.Description to get the link Key
50+
enrollmentKeySelectorTemplate *template.Template
4951
// template used to render the enrollment's Provision propertiesx
5052
enrollmentPropertiesTemplate *template.Template
5153
}

pkg/controller/enrollment/enroller_test.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,224 @@ options:
139139
"Destroy",
140140
}, <-seen)
141141
}
142+
143+
func TestEnrollerNoTags(t *testing.T) {
144+
145+
// Group members: 1, 2, 3
146+
source := []instance.Description{
147+
{ID: instance.ID("instance-1"), Properties: types.AnyString(`{"backend_id":"1"}`)},
148+
{ID: instance.ID("instance-2"), Properties: types.AnyString(`{"backend_id":"2"}`)},
149+
{ID: instance.ID("instance-3"), Properties: types.AnyString(`{"backend_id":"3"}`)},
150+
}
151+
152+
// Currently enrolled: 1, 2, 4
153+
enrolled := []instance.Description{
154+
{ID: instance.ID("1")},
155+
{ID: instance.ID("2")},
156+
{ID: instance.ID("4")},
157+
}
158+
159+
seen := make(chan []interface{}, 10)
160+
161+
enroller := newEnroller(
162+
func() discovery.Plugins {
163+
return fakePlugins{
164+
"test": &plugin.Endpoint{},
165+
}
166+
},
167+
fakeLeader(func() (bool, error) { return false, nil }),
168+
enrollment.Options{})
169+
enroller.groupPlugin = &group_test.Plugin{
170+
DoDescribeGroup: func(gid group.ID) (group.Description, error) {
171+
result := group.Description{Instances: source}
172+
return result, nil
173+
},
174+
}
175+
enroller.instancePlugin = &instance_test.Plugin{
176+
DoDescribeInstances: func(t map[string]string, p bool) ([]instance.Description, error) {
177+
return enrolled, nil
178+
},
179+
DoProvision: func(spec instance.Spec) (*instance.ID, error) {
180+
181+
seen <- []interface{}{spec, "Provision"}
182+
return nil, nil
183+
},
184+
DoDestroy: func(id instance.ID, ctx instance.Context) error {
185+
186+
seen <- []interface{}{id, ctx, "Destroy"}
187+
return nil
188+
},
189+
}
190+
191+
require.False(t, enroller.Running())
192+
193+
// Build a spec that uses the "backend_id" as the key for the source and just
194+
// the "ID" for the enrolled
195+
spec := types.Spec{}
196+
require.NoError(t, types.AnyYAMLMust([]byte(`
197+
kind: enrollment
198+
metadata:
199+
name: nfs
200+
properties:
201+
List: group/workers
202+
Instance:
203+
Plugin: nfs/authorization
204+
Properties:
205+
backend_id: \{\{ $x := .Properties | jsonDecode \}\}\{\{ int $x.backend_id \}\}
206+
options:
207+
SourceKeySelector: \{\{ $x := .Properties | jsonDecode \}\}\{\{ int $x.backend_id \}\}
208+
EnrollmentKeySelector: \{\{.ID\}\}
209+
`)).Decode(&spec))
210+
211+
require.NoError(t, enroller.updateSpec(spec))
212+
213+
st, err := enroller.getSourceKeySelectorTemplate()
214+
require.NoError(t, err)
215+
require.NotNil(t, st)
216+
217+
et, err := enroller.getEnrollmentPropertiesTemplate()
218+
require.NoError(t, err)
219+
require.NotNil(t, et)
220+
221+
require.NoError(t, err)
222+
223+
s, err := enroller.getSourceInstances()
224+
require.NoError(t, err)
225+
require.Equal(t, source, s)
226+
227+
found, err := enroller.getEnrolledInstances()
228+
require.NoError(t, err)
229+
require.Equal(t, enrolled, found)
230+
231+
require.NoError(t, enroller.sync())
232+
233+
// check the provision and destroy calls, instance 3 should be added and 4
234+
// should be removed
235+
require.Equal(t, []interface{}{
236+
instance.Spec{
237+
Properties: types.AnyString(`{"backend_id":"3"}`),
238+
Tags: map[string]string{
239+
"infrakit.enrollment.sourceID": "instance-3",
240+
"infrakit.enrollment.name": "nfs",
241+
},
242+
},
243+
"Provision",
244+
}, <-seen)
245+
require.Equal(t, []interface{}{
246+
instance.ID("4"),
247+
instance.Termination,
248+
"Destroy",
249+
}, <-seen)
250+
require.Len(t, seen, 0)
251+
}
252+
253+
func TestEnrollerMissingProps(t *testing.T) {
254+
255+
// Group members: 1, 2, 3 (no props), 4
256+
source := []instance.Description{
257+
{ID: instance.ID("instance-1"), Properties: types.AnyString(`{"backend_id":"1"}`)},
258+
{ID: instance.ID("instance-2"), Properties: types.AnyString(`{"backend_id":"2"}`)},
259+
{ID: instance.ID("instance-3")},
260+
{ID: instance.ID("instance-4"), Properties: types.AnyString(`{"backend_id":"4"}`)},
261+
}
262+
263+
// Currently enrolled: 1, 2, 5
264+
enrolled := []instance.Description{
265+
{ID: instance.ID("1")},
266+
{ID: instance.ID("2")},
267+
{ID: instance.ID("5")},
268+
}
269+
270+
seen := make(chan []interface{}, 10)
271+
272+
enroller := newEnroller(
273+
func() discovery.Plugins {
274+
return fakePlugins{
275+
"test": &plugin.Endpoint{},
276+
}
277+
},
278+
fakeLeader(func() (bool, error) { return false, nil }),
279+
enrollment.Options{})
280+
enroller.groupPlugin = &group_test.Plugin{
281+
DoDescribeGroup: func(gid group.ID) (group.Description, error) {
282+
result := group.Description{Instances: source}
283+
return result, nil
284+
},
285+
}
286+
enroller.instancePlugin = &instance_test.Plugin{
287+
DoDescribeInstances: func(t map[string]string, p bool) ([]instance.Description, error) {
288+
return enrolled, nil
289+
},
290+
DoProvision: func(spec instance.Spec) (*instance.ID, error) {
291+
292+
seen <- []interface{}{spec, "Provision"}
293+
return nil, nil
294+
},
295+
DoDestroy: func(id instance.ID, ctx instance.Context) error {
296+
297+
seen <- []interface{}{id, ctx, "Destroy"}
298+
return nil
299+
},
300+
}
301+
302+
require.False(t, enroller.Running())
303+
304+
// Build a spec that uses the "backend_id" as the key for the source and just
305+
// the "ID" for the enrolled
306+
spec := types.Spec{}
307+
require.NoError(t, types.AnyYAMLMust([]byte(`
308+
kind: enrollment
309+
metadata:
310+
name: nfs
311+
properties:
312+
List: group/workers
313+
Instance:
314+
Plugin: nfs/authorization
315+
Properties:
316+
backend_id: \{\{ $x := .Properties | jsonDecode \}\}\{\{ int $x.backend_id \}\}
317+
options:
318+
SourceKeySelector: \{\{ $x := .Properties | jsonDecode \}\}\{\{ int $x.backend_id \}\}
319+
EnrollmentKeySelector: \{\{.ID\}\}
320+
`)).Decode(&spec))
321+
322+
require.NoError(t, enroller.updateSpec(spec))
323+
324+
st, err := enroller.getSourceKeySelectorTemplate()
325+
require.NoError(t, err)
326+
require.NotNil(t, st)
327+
328+
et, err := enroller.getEnrollmentPropertiesTemplate()
329+
require.NoError(t, err)
330+
require.NotNil(t, et)
331+
332+
require.NoError(t, err)
333+
334+
s, err := enroller.getSourceInstances()
335+
require.NoError(t, err)
336+
require.Equal(t, source, s)
337+
338+
found, err := enroller.getEnrolledInstances()
339+
require.NoError(t, err)
340+
require.Equal(t, enrolled, found)
341+
342+
require.NoError(t, enroller.sync())
343+
344+
// check the provision and destroy calls, instance 3 should be added, instance
345+
// 4 should be ignored (cannot be indexed), and 5 should be removed
346+
require.Equal(t, []interface{}{
347+
instance.Spec{
348+
Properties: types.AnyString(`{"backend_id":"4"}`),
349+
Tags: map[string]string{
350+
"infrakit.enrollment.sourceID": "instance-4",
351+
"infrakit.enrollment.name": "nfs",
352+
},
353+
},
354+
"Provision",
355+
}, <-seen)
356+
require.Equal(t, []interface{}{
357+
instance.ID("5"),
358+
instance.Termination,
359+
"Destroy",
360+
}, <-seen)
361+
require.Len(t, seen, 0)
362+
}

pkg/controller/enrollment/set.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func index(list instance.Descriptions, getKey keyFunc) (map[string]instance.Desc
1616
for _, n := range list {
1717
key, err := getKey(n)
1818
if err != nil {
19-
log.Error("cannot index entry", "instane.Description", n)
19+
log.Error("cannot index entry", "instance.Description", n, "err", err)
2020
continue
2121
}
2222
this.Add(key)

pkg/controller/enrollment/sync.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ func (l *enroller) getSourceKeySelectorTemplate() (*template.Template, error) {
6666
return l.sourceKeySelectorTemplate, nil
6767
}
6868

69+
func (l *enroller) getEnrollmentKeySelectorTemplate() (*template.Template, error) {
70+
l.lock.Lock()
71+
defer l.lock.Unlock()
72+
73+
if l.options.EnrollmentKeySelector != "" {
74+
if l.enrollmentKeySelectorTemplate == nil {
75+
t, err := enrollment.TemplateFrom([]byte(l.options.EnrollmentKeySelector))
76+
if err != nil {
77+
return nil, err
78+
}
79+
l.enrollmentKeySelectorTemplate = t
80+
}
81+
}
82+
83+
return l.enrollmentKeySelectorTemplate, nil
84+
}
85+
6986
func (l *enroller) getEnrollmentPropertiesTemplate() (*template.Template, error) {
7087
l.lock.Lock()
7188
defer l.lock.Unlock()
@@ -119,13 +136,27 @@ func (l *enroller) sync() error {
119136
return string(d.ID), nil
120137
}
121138

122-
// As long as the downstream enrollment records are labeled correctly we
123-
// can even support 'importing' out-of-band created enrollment records
139+
// If specified, use the given enrollment selectior to get the index key;
140+
// else check for the labels so that we can even support 'importing'
141+
// out-of-band created enrollment records
124142
enrolledKeyFunc := func(d instance.Description) (string, error) {
125-
if v, has := d.Tags["infrakit.enrollment.sourceID"]; has {
126-
return v, nil
143+
144+
t, err := l.getEnrollmentKeySelectorTemplate()
145+
if err != nil {
146+
return "", err
147+
}
148+
if t == nil {
149+
if v, has := d.Tags["infrakit.enrollment.sourceID"]; has {
150+
return v, nil
151+
}
152+
return "", fmt.Errorf("not-matched:%v", d.ID)
127153
}
128-
return "", fmt.Errorf("not-matched:%v", d.ID)
154+
view, err := t.Render(d)
155+
if err != nil {
156+
return "", err
157+
}
158+
return view, nil
159+
129160
}
130161

131162
// compute the delta required to make enrolled look like source

pkg/controller/enrollment/types/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ type Options struct {
101101
// SourceKeySelector: \{\{ .ID \}\} # selects the ID field.
102102
SourceKeySelector string
103103

104+
// SourceKeySelector is a string template for selecting the join key from
105+
// a enrollment plugin's instance.Description.
106+
EnrollmentKeySelector string
107+
104108
// SyncInterval is the time interval between reconciliation. Syntax
105109
// is go's time.Duration string representation (e.g. 1m, 30s)
106110
SyncInterval types.Duration

pkg/plugin/group/scaled.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (s *scaledGroup) Destroy(inst instance.Description, ctx instance.Context) e
133133
func (s *scaledGroup) List() ([]instance.Description, error) {
134134
settings := s.latestSettings()
135135

136-
return settings.instancePlugin.DescribeInstances(s.memberTags, false)
136+
return settings.instancePlugin.DescribeInstances(s.memberTags, true)
137137
}
138138

139139
func (s *scaledGroup) Label() error {

0 commit comments

Comments
 (0)