~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/constraints/validation_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package constraints_test
 
5
 
 
6
import (
 
7
        "regexp"
 
8
 
 
9
        jc "github.com/juju/testing/checkers"
 
10
        gc "gopkg.in/check.v1"
 
11
 
 
12
        "github.com/juju/juju/constraints"
 
13
)
 
14
 
 
15
type validationSuite struct{}
 
16
 
 
17
var _ = gc.Suite(&validationSuite{})
 
18
 
 
19
var validationTests = []struct {
 
20
        cons        string
 
21
        unsupported []string
 
22
        vocab       map[string][]interface{}
 
23
        reds        []string
 
24
        blues       []string
 
25
        err         string
 
26
}{
 
27
        {
 
28
                cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
 
29
        },
 
30
        {
 
31
                cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo",
 
32
                unsupported: []string{"tags"},
 
33
        },
 
34
        {
 
35
                cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 instance-type=foo",
 
36
                unsupported: []string{"cpu-power", "instance-type"},
 
37
        },
 
38
        {
 
39
                // Ambiguous constraint errors take precedence over unsupported errors.
 
40
                cons:        "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
 
41
                reds:        []string{"mem", "arch"},
 
42
                blues:       []string{"instance-type"},
 
43
                unsupported: []string{"cpu-cores"},
 
44
                err:         `ambiguous constraints: "instance-type" overlaps with "mem"`,
 
45
        },
 
46
        {
 
47
                cons: "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
 
48
                reds: []string{"mem", "arch"},
 
49
                err:  "",
 
50
        },
 
51
        {
 
52
                cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
 
53
                blues: []string{"mem", "arch"},
 
54
                err:   "",
 
55
        },
 
56
        {
 
57
                cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
 
58
                reds:  []string{"mem", "arch"},
 
59
                blues: []string{"instance-type"},
 
60
                err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
 
61
        },
 
62
        {
 
63
                cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
 
64
                reds:  []string{"instance-type"},
 
65
                blues: []string{"mem", "arch"},
 
66
                err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
 
67
        },
 
68
        {
 
69
                cons:  "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
 
70
                reds:  []string{"mem", "arch"},
 
71
                blues: []string{"instance-type"},
 
72
                err:   `ambiguous constraints: "instance-type" overlaps with "mem"`,
 
73
        },
 
74
        {
 
75
                cons:  "arch=amd64 mem=4G cpu-cores=4",
 
76
                vocab: map[string][]interface{}{"arch": {"amd64", "i386"}},
 
77
        },
 
78
        {
 
79
                cons:  "mem=4G cpu-cores=4",
 
80
                vocab: map[string][]interface{}{"cpu-cores": {2, 4, 8}},
 
81
        },
 
82
        {
 
83
                cons:  "mem=4G instance-type=foo",
 
84
                vocab: map[string][]interface{}{"instance-type": {"foo", "bar"}},
 
85
        },
 
86
        {
 
87
                cons:  "mem=4G tags=foo,bar",
 
88
                vocab: map[string][]interface{}{"tags": {"foo", "bar", "another"}},
 
89
        },
 
90
        {
 
91
                cons:  "arch=i386 mem=4G cpu-cores=4",
 
92
                vocab: map[string][]interface{}{"arch": {"amd64"}},
 
93
                err:   "invalid constraint value: arch=i386\nvalid values are:.*",
 
94
        },
 
95
        {
 
96
                cons:  "mem=4G cpu-cores=5",
 
97
                vocab: map[string][]interface{}{"cpu-cores": {2, 4, 8}},
 
98
                err:   "invalid constraint value: cpu-cores=5\nvalid values are:.*",
 
99
        },
 
100
        {
 
101
                cons:  "mem=4G instance-type=foo",
 
102
                vocab: map[string][]interface{}{"instance-type": {"bar"}},
 
103
                err:   "invalid constraint value: instance-type=foo\nvalid values are:.*",
 
104
        },
 
105
        {
 
106
                cons:  "mem=4G tags=foo,other",
 
107
                vocab: map[string][]interface{}{"tags": {"foo", "bar", "another"}},
 
108
                err:   "invalid constraint value: tags=other\nvalid values are:.*",
 
109
        },
 
110
        {
 
111
                cons: "arch=i386 mem=4G instance-type=foo",
 
112
                vocab: map[string][]interface{}{
 
113
                        "instance-type": {"foo", "bar"},
 
114
                        "arch":          {"amd64", "i386"}},
 
115
        },
 
116
        {
 
117
                cons:  "virt-type=bar",
 
118
                vocab: map[string][]interface{}{"virt-type": {"bar"}},
 
119
        },
 
120
}
 
121
 
 
122
func (s *validationSuite) TestValidation(c *gc.C) {
 
123
        for i, t := range validationTests {
 
124
                c.Logf("test %d", i)
 
125
                validator := constraints.NewValidator()
 
126
                validator.RegisterUnsupported(t.unsupported)
 
127
                validator.RegisterConflicts(t.reds, t.blues)
 
128
                for a, v := range t.vocab {
 
129
                        validator.RegisterVocabulary(a, v)
 
130
                }
 
131
                cons := constraints.MustParse(t.cons)
 
132
                unsupported, err := validator.Validate(cons)
 
133
                if t.err == "" {
 
134
                        c.Assert(err, jc.ErrorIsNil)
 
135
                        c.Assert(unsupported, jc.SameContents, t.unsupported)
 
136
                } else {
 
137
                        c.Assert(err, gc.ErrorMatches, t.err)
 
138
                }
 
139
        }
 
140
}
 
141
 
 
142
var mergeTests = []struct {
 
143
        desc         string
 
144
        consFallback string
 
145
        cons         string
 
146
        unsupported  []string
 
147
        reds         []string
 
148
        blues        []string
 
149
        expected     string
 
150
}{
 
151
        {
 
152
                desc: "empty all round",
 
153
        }, {
 
154
                desc:     "container with empty fallback",
 
155
                cons:     "container=lxd",
 
156
                expected: "container=lxd",
 
157
        }, {
 
158
                desc:         "container from fallback",
 
159
                consFallback: "container=lxd",
 
160
                expected:     "container=lxd",
 
161
        }, {
 
162
                desc:     "arch with empty fallback",
 
163
                cons:     "arch=amd64",
 
164
                expected: "arch=amd64",
 
165
        }, {
 
166
                desc:         "arch with ignored fallback",
 
167
                cons:         "arch=amd64",
 
168
                consFallback: "arch=i386",
 
169
                expected:     "arch=amd64",
 
170
        }, {
 
171
                desc:         "arch from fallback",
 
172
                consFallback: "arch=i386",
 
173
                expected:     "arch=i386",
 
174
        }, {
 
175
                desc:     "instance type with empty fallback",
 
176
                cons:     "instance-type=foo",
 
177
                expected: "instance-type=foo",
 
178
        }, {
 
179
                desc:         "instance type with ignored fallback",
 
180
                cons:         "instance-type=foo",
 
181
                consFallback: "instance-type=bar",
 
182
                expected:     "instance-type=foo",
 
183
        }, {
 
184
                desc:         "instance type from fallback",
 
185
                consFallback: "instance-type=foo",
 
186
                expected:     "instance-type=foo",
 
187
        }, {
 
188
                desc:     "cpu-cores with empty fallback",
 
189
                cons:     "cpu-cores=2",
 
190
                expected: "cpu-cores=2",
 
191
        }, {
 
192
                desc:         "cpu-cores with ignored fallback",
 
193
                cons:         "cpu-cores=4",
 
194
                consFallback: "cpu-cores=8",
 
195
                expected:     "cpu-cores=4",
 
196
        }, {
 
197
                desc:         "cpu-cores from fallback",
 
198
                consFallback: "cpu-cores=8",
 
199
                expected:     "cpu-cores=8",
 
200
        }, {
 
201
                desc:     "cpu-power with empty fallback",
 
202
                cons:     "cpu-power=100",
 
203
                expected: "cpu-power=100",
 
204
        }, {
 
205
                desc:         "cpu-power with ignored fallback",
 
206
                cons:         "cpu-power=100",
 
207
                consFallback: "cpu-power=200",
 
208
                expected:     "cpu-power=100",
 
209
        }, {
 
210
                desc:         "cpu-power from fallback",
 
211
                consFallback: "cpu-power=200",
 
212
                expected:     "cpu-power=200",
 
213
        }, {
 
214
                desc:     "tags with empty fallback",
 
215
                cons:     "tags=foo,bar",
 
216
                expected: "tags=foo,bar",
 
217
        }, {
 
218
                desc:         "tags with ignored fallback",
 
219
                cons:         "tags=foo,bar",
 
220
                consFallback: "tags=baz",
 
221
                expected:     "tags=foo,bar",
 
222
        }, {
 
223
                desc:         "tags from fallback",
 
224
                consFallback: "tags=foo,bar",
 
225
                expected:     "tags=foo,bar",
 
226
        }, {
 
227
                desc:         "tags inital empty",
 
228
                cons:         "tags=",
 
229
                consFallback: "tags=foo,bar",
 
230
                expected:     "tags=",
 
231
        }, {
 
232
                desc:     "mem with empty fallback",
 
233
                cons:     "mem=4G",
 
234
                expected: "mem=4G",
 
235
        }, {
 
236
                desc:         "mem with ignored fallback",
 
237
                cons:         "mem=4G",
 
238
                consFallback: "mem=8G",
 
239
                expected:     "mem=4G",
 
240
        }, {
 
241
                desc:         "mem from fallback",
 
242
                consFallback: "mem=8G",
 
243
                expected:     "mem=8G",
 
244
        }, {
 
245
                desc:     "root-disk with empty fallback",
 
246
                cons:     "root-disk=4G",
 
247
                expected: "root-disk=4G",
 
248
        }, {
 
249
                desc:         "root-disk with ignored fallback",
 
250
                cons:         "root-disk=4G",
 
251
                consFallback: "root-disk=8G",
 
252
                expected:     "root-disk=4G",
 
253
        }, {
 
254
                desc:         "root-disk from fallback",
 
255
                consFallback: "root-disk=8G",
 
256
                expected:     "root-disk=8G",
 
257
        }, {
 
258
                desc:         "non-overlapping mix",
 
259
                cons:         "root-disk=8G mem=4G arch=amd64",
 
260
                consFallback: "cpu-power=1000 cpu-cores=4",
 
261
                expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
 
262
        }, {
 
263
                desc:         "overlapping mix",
 
264
                cons:         "root-disk=8G mem=4G arch=amd64",
 
265
                consFallback: "cpu-power=1000 cpu-cores=4 mem=8G",
 
266
                expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
 
267
        }, {
 
268
                desc:         "fallback only, no conflicts",
 
269
                consFallback: "root-disk=8G cpu-cores=4 instance-type=foo",
 
270
                reds:         []string{"mem", "arch"},
 
271
                blues:        []string{"instance-type"},
 
272
                expected:     "root-disk=8G cpu-cores=4 instance-type=foo",
 
273
        }, {
 
274
                desc:     "no fallback, no conflicts",
 
275
                cons:     "root-disk=8G cpu-cores=4 instance-type=foo",
 
276
                reds:     []string{"mem", "arch"},
 
277
                blues:    []string{"instance-type"},
 
278
                expected: "root-disk=8G cpu-cores=4 instance-type=foo",
 
279
        }, {
 
280
                desc:         "conflict value from override",
 
281
                consFallback: "root-disk=8G instance-type=foo",
 
282
                cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
 
283
                reds:         []string{"mem", "arch"},
 
284
                blues:        []string{"instance-type"},
 
285
                expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
 
286
        }, {
 
287
                desc:         "unsupported attributes ignored",
 
288
                consFallback: "root-disk=8G instance-type=foo",
 
289
                cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
 
290
                reds:         []string{"mem", "arch"},
 
291
                blues:        []string{"instance-type"},
 
292
                unsupported:  []string{"instance-type"},
 
293
                expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
 
294
        }, {
 
295
                desc:         "red conflict masked from fallback",
 
296
                consFallback: "root-disk=8G mem=4G",
 
297
                cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
 
298
                reds:         []string{"mem", "arch"},
 
299
                blues:        []string{"instance-type"},
 
300
                expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
 
301
        }, {
 
302
                desc:         "second red conflict masked from fallback",
 
303
                consFallback: "root-disk=8G arch=amd64",
 
304
                cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
 
305
                reds:         []string{"mem", "arch"},
 
306
                blues:        []string{"instance-type"},
 
307
                expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
 
308
        }, {
 
309
                desc:         "blue conflict masked from fallback",
 
310
                consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
 
311
                cons:         "root-disk=8G mem=4G",
 
312
                reds:         []string{"mem", "arch"},
 
313
                blues:        []string{"instance-type"},
 
314
                expected:     "root-disk=8G cpu-cores=4 mem=4G",
 
315
        }, {
 
316
                desc:         "both red conflicts used, blue mased from fallback",
 
317
                consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
 
318
                cons:         "root-disk=8G arch=amd64 mem=4G",
 
319
                reds:         []string{"mem", "arch"},
 
320
                blues:        []string{"instance-type"},
 
321
                expected:     "root-disk=8G cpu-cores=4 arch=amd64 mem=4G",
 
322
        },
 
323
}
 
324
 
 
325
func (s *validationSuite) TestMerge(c *gc.C) {
 
326
        for i, t := range mergeTests {
 
327
                c.Logf("test %d: %s", i, t.desc)
 
328
                validator := constraints.NewValidator()
 
329
                validator.RegisterConflicts(t.reds, t.blues)
 
330
                consFallback := constraints.MustParse(t.consFallback)
 
331
                cons := constraints.MustParse(t.cons)
 
332
                merged, err := validator.Merge(consFallback, cons)
 
333
                c.Assert(err, jc.ErrorIsNil)
 
334
                expected := constraints.MustParse(t.expected)
 
335
                c.Check(merged, gc.DeepEquals, expected)
 
336
        }
 
337
}
 
338
 
 
339
func (s *validationSuite) TestMergeError(c *gc.C) {
 
340
        validator := constraints.NewValidator()
 
341
        validator.RegisterConflicts([]string{"instance-type"}, []string{"mem"})
 
342
        consFallback := constraints.MustParse("instance-type=foo mem=4G")
 
343
        cons := constraints.MustParse("cpu-cores=2")
 
344
        _, err := validator.Merge(consFallback, cons)
 
345
        c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "instance-type" overlaps with "mem"`)
 
346
        _, err = validator.Merge(cons, consFallback)
 
347
        c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "instance-type" overlaps with "mem"`)
 
348
}
 
349
 
 
350
func (s *validationSuite) TestUpdateVocabulary(c *gc.C) {
 
351
        validator := constraints.NewValidator()
 
352
        attributeName := "arch"
 
353
        originalValues := []string{"amd64"}
 
354
        validator.RegisterVocabulary(attributeName, originalValues)
 
355
 
 
356
        cons := constraints.MustParse("arch=amd64")
 
357
        _, err := validator.Validate(cons)
 
358
        c.Assert(err, jc.ErrorIsNil)
 
359
 
 
360
        cons2 := constraints.MustParse("arch=ppc64el")
 
361
        _, err = validator.Validate(cons2)
 
362
        c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`invalid constraint value: arch=ppc64el
 
363
valid values are: [amd64]`))
 
364
 
 
365
        additionalValues := []string{"ppc64el"}
 
366
        validator.UpdateVocabulary(attributeName, additionalValues)
 
367
 
 
368
        _, err = validator.Validate(cons)
 
369
        c.Assert(err, jc.ErrorIsNil)
 
370
        _, err = validator.Validate(cons2)
 
371
        c.Assert(err, jc.ErrorIsNil)
 
372
}