Merge lp:~dimitern/juju-core/460-network-constraints into lp:~go-bot/juju-core/trunk

Proposed by Dimiter Naydenov
Status: Rejected
Rejected by: Dimiter Naydenov
Proposed branch: lp:~dimitern/juju-core/460-network-constraints
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 765 lines (+269/-131)
7 files modified
cmd/juju/deploy.go (+42/-15)
cmd/juju/deploy_test.go (+8/-5)
cmd/juju/help_topics.go (+7/-0)
constraints/constraints.go (+68/-10)
constraints/constraints_test.go (+96/-63)
state/constraints.go (+3/-0)
state/constraintsvalidation_test.go (+45/-38)
To merge this branch: bzr merge lp:~dimitern/juju-core/460-network-constraints
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+221390@code.launchpad.net

Description of the change

various: Introduce networks constraints

Following the removal of --exclude-networks argument
from juju deploy, now we're introducting a complete
way to specify networks with constraints:

--constraints networks=net1,net2,^net3,^net4

Means "find a machine with net1 and net2 networks,
but not with net3 or net4 networks".

Changes are in 3 places: constraints package, state
(for saving/loading) and cmd/juju/deploy for the CLI.

Now, to specify networks to include/exclude on deploy:
juju deploy svc --networks=logging,db --constraints networks=^dmz,storage

Means: "deploy svc on machines with logging, db, and storage
networks, but not with dmz network".

Only the those, specified with --networks will be enabled
on the machine, once the networker worker is done. The ones
specified with constraints are used as a filter for instance
selection.

https://codereview.appspot.com/102830044/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Reviewers: mp+221390_code.launchpad.net,

Message:
Please take a look.

Description:
various: Introduce networks constraints

Following the removal of --exclude-networks argument
from juju deploy, now we're introducting a complete
way to specify networks with constraints:

--constraints networks=net1,net2,^net3,^net4

Means "find a machine with net1 and net2 networks,
but not with net3 or net4 networks".

Changes are in 3 places: constraints package, state
(for saving/loading) and cmd/juju/deploy for the CLI.

Now, to specify networks to include/exclude on deploy:
juju deploy svc --networks=logging,db --constraints
networks=^dmz,storage

Means: "deploy svc on machines with logging, db, and storage
networks, but not with dmz network".

Only the those, specified with --networks will be enabled
on the machine, once the networker worker is done. The ones
specified with constraints are used as a filter for instance
selection.

https://code.launchpad.net/~dimitern/juju-core/460-network-constraints/+merge/221390

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/102830044/

Affected files (+271, -131 lines):
   A [revision details]
   M cmd/juju/deploy.go
   M cmd/juju/deploy_test.go
   M cmd/juju/help_topics.go
   M constraints/constraints.go
   M constraints/constraints_test.go
   M state/constraints.go
   M state/constraintsvalidation_test.go

Revision history for this message
William Reade (fwereade) wrote :
Download full text (3.8 KiB)

If you dropped the changes to cmd/juju I'd approve this (with just a
little bit of extra checking of the values when you parse network
constraints).

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go
File cmd/juju/deploy.go (right):

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode95
cmd/juju/deploy.go:95: juju deploy mysql --networks=storage,mynet
--constraints ^logging,db
--constraints networks=^logging,db

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode191
cmd/juju/deploy.go:191: includeNetworks :=
mergeNetworks(parseNetworks(c.Networks),
c.Constraints.IncludeNetworks())
This is definitely wrong. This'd put the ones in constraints into the
set that got configured on the machine, instead of the ones used *only*
for machine *selection*, as they should be.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode192
cmd/juju/deploy.go:192: excludeNetworks := mergeNetworks(nil,
c.Constraints.ExcludeNetworks())
And I'm pretty sure this is wrong too. The information's already in
constraints and doesn't need to be duplicated.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode195
cmd/juju/deploy.go:195: env, err := environs.New(conf)
I'm a bit concerned about this. I know we currently need the environ to
talk to the charm store, but I think this will change as we move the
charm store into the environment; I'd prefer not to add more
dependencies on it at this layer (apart from everything else, we can't
assume that the average user has permission to look at the env config).
Just do the checking in the API server (which you have to do anyway, you
might get any request from the GUI/other client).

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode240
cmd/juju/deploy.go:240: excludeNetworks,
includeNetworks could stay, if it had the right information in it, but
I'm certain we don't want this. If we haven't released a stable version
with --include/exclude-networks, we should just drop this field. What
are dev versions for, if not for making mistakes? ;p

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode297
cmd/juju/deploy.go:297: parts := strings.Split(networksValue, ",")
cmd.StringsValue

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode314
cmd/juju/deploy.go:314: // It's not a tag, convert it.
We should *always* turn the user's strings into tags. If they try to
specify network-foo, send network-network-foo over the API.

https://codereview.appspot.com/102830044/diff/1/constraints/constraints.go
File constraints/constraints.go (right):

https://codereview.appspot.com/102830044/diff/1/constraints/constraints.go#newcode514
constraints/constraints.go:514: return &items, nil
I'm wondering whether we should be restricting the values here, at least
in the networks case. Remember these are meant to refer to juju network
names, not provider ones (where we can't control validity).

Tags may be a bit different; not sure if we can sanely restrict them,
even if we'll get surprising results if they ever contain ",", but I
think we can and should keep network names locked down.

ht...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (5.1 KiB)

Sorry, what I meant by "follow-up" is actually a prerequisite:
https://codereview.appspot.com/93670046

What I've said below is done, it's there.

I'll repropose this later with the changes to cmd/juju mostly.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go
File cmd/juju/deploy.go (right):

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode95
cmd/juju/deploy.go:95: juju deploy mysql --networks=storage,mynet
--constraints ^logging,db
On 2014/05/29 18:22:36, fwereade wrote:
> --constraints networks=^logging,db

Done in a follow-up.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode191
cmd/juju/deploy.go:191: includeNetworks :=
mergeNetworks(parseNetworks(c.Networks),
c.Constraints.IncludeNetworks())
On 2014/05/29 18:22:36, fwereade wrote:
> This is definitely wrong. This'd put the ones in constraints into the
set that
> got configured on the machine, instead of the ones used *only* for
machine
> *selection*, as they should be.

Taking your suggestion, I'm dropping cmd/juju changes from this branch
and will propose it as a follow-up, fixing these issues.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode192
cmd/juju/deploy.go:192: excludeNetworks := mergeNetworks(nil,
c.Constraints.ExcludeNetworks())
On 2014/05/29 18:22:36, fwereade wrote:
> And I'm pretty sure this is wrong too. The information's already in
constraints
> and doesn't need to be duplicated.

Done in a follow-up.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode195
cmd/juju/deploy.go:195: env, err := environs.New(conf)
On 2014/05/29 18:22:36, fwereade wrote:
> I'm a bit concerned about this. I know we currently need the environ
to talk to
> the charm store, but I think this will change as we move the charm
store into
> the environment; I'd prefer not to add more dependencies on it at this
layer
> (apart from everything else, we can't assume that the average user has
> permission to look at the env config). Just do the checking in the API
server
> (which you have to do anyway, you might get any request from the
GUI/other
> client).

Check the follow-up. Doing this check here, in addition to a check in
the API server (sorry, I thought I had that there already - will add it
there as well) will allow us to bail out early, before the API call with
a nicer CLI-specific message, like "cannot use --networks: not supported
by the environmen" instead of "cannot deploy "wordpress" with networks:
not supported by the environment". I'm with having only the latter, less
specific error message.

https://codereview.appspot.com/102830044/diff/1/cmd/juju/deploy.go#newcode240
cmd/juju/deploy.go:240: excludeNetworks,
On 2014/05/29 18:22:36, fwereade wrote:
> includeNetworks could stay, if it had the right information in it, but
I'm
> certain we don't want this. If we haven't released a stable version
with
> --include/exclude-networks, we should just drop this field. What are
dev
> versions for, if not for making mistakes? ;p

Good point. In the follow-up, I'll drop that and modify
service.Networks() to return include/excludeNetworks as before, but
taking both requested networks and...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

This got migrated to github:

https://github.com/juju/juju/pull/13

Lots of changes done, suggestions applied.

https://codereview.appspot.com/102830044/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/deploy.go'
2--- cmd/juju/deploy.go 2014-05-28 15:07:03 +0000
3+++ cmd/juju/deploy.go 2014-05-29 14:17:10 +0000
4@@ -22,6 +22,7 @@
5 "launchpad.net/juju-core/names"
6 "launchpad.net/juju-core/state/api"
7 "launchpad.net/juju-core/state/api/params"
8+ "launchpad.net/juju-core/utils/set"
9 )
10
11 type DeployCommand struct {
12@@ -88,16 +89,20 @@
13 juju deploy mysql --to 24/lxc/3 (deploy to lxc container 3 on host machine 24)
14 juju deploy mysql --to lxc:25 (deploy to a new lxc container on host machine 25)
15
16- juju deploy mysql -n 5 --constraints mem=8G (deploy 5 instances of mysql with at least 8 GB of RAM each)
17+ juju deploy mysql -n 5 --constraints mem=8G
18+ (deploy 5 instances of mysql with at least 8 GB of RAM each)
19
20- juju deploy mysql --networks=storage,mynet
21+ juju deploy mysql --networks=storage,mynet --constraints ^logging,db
22+ (deploy mysql on machines with "storage", "mynet" and "db" networks,
23+ but not on machines with "logging" network)
24
25 Like constraints, service-specific network requirements can be
26 specified with the --networks argument, which takes a comma-delimited
27-list of provider-specific network names/labels.
28-These instruct juju to ensure to add all the networks specified with
29---networks to all new machines deployed to host units of the service.
30-Not supported on all providers.
31+list of juju-specific network names. Networks can also be specified with
32+constraints, but they only define what machine to pick, not what networks
33+to configure on it. The --networks argument instructs juju to add all the
34+networks specified with it to all new machines deployed to host units of
35+the service. Not supported on all providers.
36
37 See Also:
38 juju help constraints
39@@ -182,16 +187,17 @@
40 if c.BumpRevision {
41 ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
42 }
43- var includeNetworks []string
44- if c.Networks != "" {
45- includeNetworks = parseNetworks(c.Networks)
46
47+ includeNetworks := mergeNetworks(parseNetworks(c.Networks), c.Constraints.IncludeNetworks())
48+ excludeNetworks := mergeNetworks(nil, c.Constraints.ExcludeNetworks())
49+ haveNetworks := len(includeNetworks) > 0 || len(excludeNetworks) > 0
50+ if haveNetworks {
51 env, err := environs.New(conf)
52 if err != nil {
53 return err
54 }
55 if !env.SupportNetworks() {
56- return errors.New("cannot use --networks: not supported by the environment")
57+ return errors.New("cannot use --networks/--constraints networks=...: not supported by the environment")
58 }
59 }
60
61@@ -231,11 +237,11 @@
62 c.Constraints,
63 c.ToMachineSpec,
64 includeNetworks,
65- nil,
66+ excludeNetworks,
67 )
68 if params.IsCodeNotImplemented(err) {
69- if len(includeNetworks) > 0 {
70- return errors.New("cannot use --networks: not supported by the API server")
71+ if haveNetworks {
72+ return errors.New("cannot use --networks/--constraints networks=...: not supported by the API server")
73 }
74 err = client.ServiceDeploy(
75 curl.String(),
76@@ -282,15 +288,36 @@
77 return curl, nil
78 }
79
80-// parseNetworks returns a list of network tags by parsing the
81+// parseNetworks returns a list of networks by parsing the
82 // comma-delimited string value of --networks argument.
83 func parseNetworks(networksValue string) (networks []string) {
84+ if networksValue == "" {
85+ return networks
86+ }
87 parts := strings.Split(networksValue, ",")
88 for _, part := range parts {
89 network := strings.TrimSpace(part)
90 if network != "" {
91- networks = append(networks, names.NetworkTag(network))
92+ networks = append(networks, network)
93 }
94 }
95 return networks
96 }
97+
98+// mergeNetworks returns a sorted list of unique network tags, merging
99+// the given slices.
100+func mergeNetworks(fromNetworks, fromConstraints []string) []string {
101+ result := set.NewStrings()
102+ mergeFrom := func(networks []string) {
103+ for _, netName := range networks {
104+ if _, _, err := names.ParseTag(netName, names.NetworkTagKind); err != nil {
105+ // It's not a tag, convert it.
106+ netName = names.NetworkTag(netName)
107+ }
108+ result.Add(netName)
109+ }
110+ }
111+ mergeFrom(fromNetworks)
112+ mergeFrom(fromConstraints)
113+ return result.SortedValues()
114+}
115
116=== modified file 'cmd/juju/deploy_test.go'
117--- cmd/juju/deploy_test.go 2014-05-28 15:07:03 +0000
118+++ cmd/juju/deploy_test.go 2014-05-29 14:17:10 +0000
119@@ -161,25 +161,28 @@
120
121 func (s *DeploySuite) TestConstraints(c *gc.C) {
122 coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
123- err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2")
124+ err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2 networks=net1,^net2")
125 c.Assert(err, gc.IsNil)
126 curl := charm.MustParseURL("local:precise/dummy-1")
127 service, _ := s.AssertService(c, "dummy", curl, 1, 0)
128 cons, err := service.Constraints()
129 c.Assert(err, gc.IsNil)
130- c.Assert(cons, gc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2"))
131+ c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,^net2"))
132 }
133
134 func (s *DeploySuite) TestNetworks(c *gc.C) {
135 coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
136- err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ")
137+ err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ", "--constraints", "mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4")
138 c.Assert(err, gc.IsNil)
139 curl := charm.MustParseURL("local:precise/dummy-1")
140 service, _ := s.AssertService(c, "dummy", curl, 1, 0)
141 includeNetworks, excludeNetworks, err := service.Networks()
142 c.Assert(err, gc.IsNil)
143- c.Assert(includeNetworks, gc.DeepEquals, []string{"net1", "net2"})
144- c.Assert(excludeNetworks, gc.HasLen, 0)
145+ c.Assert(includeNetworks, jc.DeepEquals, []string{"net0", "net1", "net2"})
146+ c.Assert(excludeNetworks, jc.DeepEquals, []string{"net3", "net4"})
147+ cons, err := service.Constraints()
148+ c.Assert(err, gc.IsNil)
149+ c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4"))
150 }
151
152 func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) {
153
154=== modified file 'cmd/juju/help_topics.go'
155--- cmd/juju/help_topics.go 2014-05-15 10:12:36 +0000
156+++ cmd/juju/help_topics.go 2014-05-29 14:17:10 +0000
157@@ -324,6 +324,13 @@
158 Multiple tags must be delimited by a comma. Tags are currently only supported
159 by the MaaS environment.
160
161+networks
162+ Networks defines the list of networks to ensure are available or not on the
163+ machine. Both positive and negative network constraints can be specified, the
164+ later have a "^" prefix to the name. Multiple netwokrs must be delimited by a
165+ comma. Not supported on all providers. Example: networks=storage,db,^logging
166+ specifies to enable "storage" and "db" networks but not "logging" on the machine.
167+
168 Example:
169
170 juju add-machine --constraints "arch=amd64 mem=8G tags=foo,bar"
171
172=== modified file 'constraints/constraints.go'
173--- constraints/constraints.go 2014-05-13 04:50:10 +0000
174+++ constraints/constraints.go 2014-05-29 14:17:10 +0000
175@@ -27,6 +27,7 @@
176 RootDisk = "root-disk"
177 Tags = "tags"
178 InstanceType = "instance-type"
179+ Networks = "networks"
180 )
181
182 // Value describes a user's requirements of the hardware on which units
183@@ -70,6 +71,12 @@
184 // InstanceType, if not nil, indicates that the specified cloud instance type
185 // be used. Only valid for clouds which support instance types.
186 InstanceType *string `json:"instance-type,omitempty" yaml:"instance-type,omitempty"`
187+
188+ // Networks, if not nil, holds a list of juju network names that
189+ // should be available (or not) on the machine. Positive and
190+ // negative values are accepted, and the difference is the latter
191+ // have a "^" prefix to the name.
192+ Networks *[]string `json:"networks,omitempty" yaml:"networks,omitempty"`
193 }
194
195 // fieldNames records a mapping from the constraint tag to struct field name.
196@@ -107,6 +114,40 @@
197 return v.InstanceType != nil && *v.InstanceType != ""
198 }
199
200+// extractNetworks returns a list of positive (toInclude=true),
201+// negative (toExclude=true), or both from the networks constraint (if
202+// set).
203+func (v *Value) extractNetworks(toInclude, toExclude bool) []string {
204+ var nets []string
205+ if v.Networks != nil {
206+ for _, name := range *v.Networks {
207+ if strings.HasPrefix(name, "^") {
208+ if toExclude {
209+ nets = append(nets, strings.TrimPrefix(name, "^"))
210+ }
211+ } else {
212+ if toInclude {
213+ nets = append(nets, name)
214+ }
215+ }
216+ }
217+ }
218+ return nets
219+}
220+
221+// IncludeNetworks returns a list of networks to include when starting
222+// a machine, if specified.
223+func (v *Value) IncludeNetworks() []string {
224+ return v.extractNetworks(true, false)
225+}
226+
227+// ExcludeNetworks returns a list of networks to exclude when starting
228+// a machine, if specified. They are given in the networks constraint
229+// with a "^" prefix to the name, which is stripped before returning.
230+func (v *Value) ExcludeNetworks() []string {
231+ return v.extractNetworks(false, true)
232+}
233+
234 // String expresses a constraints.Value in the language in which it was specified.
235 func (v Value) String() string {
236 var strs []string
237@@ -143,6 +184,10 @@
238 s := strings.Join(*v.Tags, ",")
239 strs = append(strs, "tags="+s)
240 }
241+ if v.Networks != nil {
242+ s := strings.Join(*v.Networks, ",")
243+ strs = append(strs, "networks="+s)
244+ }
245 return strings.Join(strs, " ")
246 }
247
248@@ -270,6 +315,8 @@
249 err = v.setTags(str)
250 case InstanceType:
251 err = v.setInstanceType(str)
252+ case Networks:
253+ err = v.setNetworks(str)
254 default:
255 return fmt.Errorf("unknown constraint %q", name)
256 }
257@@ -309,7 +356,9 @@
258 case RootDisk:
259 v.RootDisk, err = parseUint64(vstr)
260 case Tags:
261- v.Tags, err = parseYamlTags(val)
262+ v.Tags, err = parseYamlStrings("tags", val)
263+ case Networks:
264+ v.Networks, err = parseYamlStrings("networks", val)
265 default:
266 return false
267 }
268@@ -397,7 +446,15 @@
269 if v.Tags != nil {
270 return fmt.Errorf("already set")
271 }
272- v.Tags = parseTags(str)
273+ v.Tags = parseCommaDelimited(str)
274+ return nil
275+}
276+
277+func (v *Value) setNetworks(str string) error {
278+ if v.Networks != nil {
279+ return fmt.Errorf("already set")
280+ }
281+ v.Networks = parseCommaDelimited(str)
282 return nil
283 }
284
285@@ -431,8 +488,9 @@
286 return &value, nil
287 }
288
289-// parseTags returns the tags in the value s. We expect the tags to be comma delimited strings.
290-func parseTags(s string) *[]string {
291+// parseCommaDelimited returns the items in the value s. We expect the
292+// tags to be comma delimited strings. It is used for tags and networks.
293+func parseCommaDelimited(s string) *[]string {
294 if s == "" {
295 return &[]string{}
296 }
297@@ -440,20 +498,20 @@
298 return &t
299 }
300
301-func parseYamlTags(val interface{}) (*[]string, error) {
302+func parseYamlStrings(entityName string, val interface{}) (*[]string, error) {
303 ifcs, ok := val.([]interface{})
304 if !ok {
305- return nil, fmt.Errorf("unexpected type passed to tags: %T", val)
306+ return nil, fmt.Errorf("unexpected type passed to %s: %T", entityName, val)
307 }
308- tags := make([]string, len(ifcs))
309+ items := make([]string, len(ifcs))
310 for n, ifc := range ifcs {
311 s, ok := ifc.(string)
312 if !ok {
313- return nil, fmt.Errorf("unexpected type passed as a tag: %T", ifc)
314+ return nil, fmt.Errorf("unexpected type passed as in %s: %T", entityName, ifc)
315 }
316- tags[n] = s
317+ items[n] = s
318 }
319- return &tags, nil
320+ return &items, nil
321 }
322
323 var mbSuffixes = map[string]float64{
324
325=== modified file 'constraints/constraints_test.go'
326--- constraints/constraints_test.go 2014-04-24 00:00:36 +0000
327+++ constraints/constraints_test.go 2014-05-29 14:17:10 +0000
328@@ -263,6 +263,21 @@
329 args: []string{"tags="},
330 },
331
332+ // networks
333+ {
334+ summary: "single network",
335+ args: []string{"networks=net1"},
336+ }, {
337+ summary: "multiple networks - positive",
338+ args: []string{"networks=net1,net2"},
339+ }, {
340+ summary: "multiple networks - positive and negative",
341+ args: []string{"networks=net1,^net2,net3,^net4"},
342+ }, {
343+ summary: "no networks",
344+ args: []string{"networks="},
345+ },
346+
347 // instance type
348 {
349 summary: "set instance type",
350@@ -277,12 +292,12 @@
351 summary: "kitchen sink together",
352 args: []string{
353 "root-disk=8G mem=2T arch=i386 cpu-cores=4096 cpu-power=9001 container=lxc " +
354- "tags=foo,bar instance-type=foo"},
355+ "tags=foo,bar networks=net1,^net2 instance-type=foo"},
356 }, {
357 summary: "kitchen sink separately",
358 args: []string{
359 "root-disk=8G", "mem=2T", "cpu-cores=4096", "cpu-power=9001", "arch=armhf",
360- "container=lxc", "tags=foo,bar", "instance-type=foo"},
361+ "container=lxc", "tags=foo,bar", "networks=net1,^net2", "instance-type=foo"},
362 },
363 }
364
365@@ -302,15 +317,26 @@
366 }
367 }
368
369-func (s *ConstraintsSuite) TestParseMissingTags(c *gc.C) {
370+func (s *ConstraintsSuite) TestParseMissingTagsAndNetworks(c *gc.C) {
371 con := constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G")
372 c.Check(con.Tags, gc.IsNil)
373+ c.Check(con.Networks, gc.IsNil)
374 }
375
376-func (s *ConstraintsSuite) TestParseNoTags(c *gc.C) {
377- con := constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G tags=")
378+func (s *ConstraintsSuite) TestParseNoTagsNoNetworks(c *gc.C) {
379+ con := constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G tags= networks=")
380 c.Assert(con.Tags, gc.Not(gc.IsNil))
381+ c.Assert(con.Networks, gc.Not(gc.IsNil))
382 c.Check(*con.Tags, gc.HasLen, 0)
383+ c.Check(*con.Networks, gc.HasLen, 0)
384+}
385+
386+func (s *ConstraintsSuite) TestIncludeExcludeNetworks(c *gc.C) {
387+ con := constraints.MustParse("networks=net1,^net2,net3,^net4")
388+ c.Assert(con.Networks, gc.Not(gc.IsNil))
389+ c.Check(*con.Networks, gc.HasLen, 4)
390+ c.Check(con.IncludeNetworks(), jc.SameContents, []string{"net1", "net3"})
391+ c.Check(con.ExcludeNetworks(), jc.SameContents, []string{"net2", "net4"})
392 }
393
394 func (s *ConstraintsSuite) TestIsEmpty(c *gc.C) {
395@@ -322,6 +348,8 @@
396 c.Check(&con, jc.Satisfies, constraints.IsEmpty)
397 con = constraints.MustParse("tags=")
398 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty)
399+ con = constraints.MustParse("networks=")
400+ c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty)
401 con = constraints.MustParse("mem=")
402 c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty)
403 con = constraints.MustParse("arch=")
404@@ -378,6 +406,9 @@
405 {"Tags1", constraints.Value{Tags: nil}},
406 {"Tags2", constraints.Value{Tags: &[]string{}}},
407 {"Tags3", constraints.Value{Tags: &[]string{"foo", "bar"}}},
408+ {"Networks1", constraints.Value{Networks: nil}},
409+ {"Networks2", constraints.Value{Networks: &[]string{}}},
410+ {"Networks3", constraints.Value{Networks: &[]string{"net1", "^net2"}}},
411 {"InstanceType1", constraints.Value{InstanceType: strp("")}},
412 {"InstanceType2", constraints.Value{InstanceType: strp("foo")}},
413 {"All", constraints.Value{
414@@ -388,6 +419,7 @@
415 Mem: uint64p(18000000000),
416 RootDisk: uint64p(24000000000),
417 Tags: &[]string{"foo", "bar"},
418+ Networks: &[]string{"net1", "^net2"},
419 InstanceType: strp("foo"),
420 }},
421 }
422@@ -399,7 +431,7 @@
423 val := constraints.ConstraintsValue{&cons}
424 err := val.Set(t.Value.String())
425 c.Check(err, gc.IsNil)
426- c.Check(cons, gc.DeepEquals, t.Value)
427+ c.Check(cons, jc.DeepEquals, t.Value)
428 }
429 }
430
431@@ -408,7 +440,7 @@
432 c.Logf("test %s", t.Name)
433 cons, err := constraints.Parse(t.Value.String())
434 c.Check(err, gc.IsNil)
435- c.Check(cons, gc.DeepEquals, t.Value)
436+ c.Check(cons, jc.DeepEquals, t.Value)
437 }
438 }
439
440@@ -420,7 +452,7 @@
441 var cons constraints.Value
442 err = json.Unmarshal(data, &cons)
443 c.Check(err, gc.IsNil)
444- c.Check(cons, gc.DeepEquals, t.Value)
445+ c.Check(cons, jc.DeepEquals, t.Value)
446 }
447 }
448
449@@ -432,7 +464,7 @@
450 var cons constraints.Value
451 err = goyaml.Unmarshal(data, &cons)
452 c.Check(err, gc.IsNil)
453- c.Check(cons, gc.DeepEquals, t.Value)
454+ c.Check(cons, jc.DeepEquals, t.Value)
455 }
456 }
457
458@@ -466,57 +498,53 @@
459 c.Check(cons.HasInstanceType(), jc.IsTrue)
460 }
461
462+const initialWithoutCons = "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 networks=net1,^net2 tags=foo container=lxc instance-type=bar"
463+
464 var withoutTests = []struct {
465 initial string
466 without []string
467 final string
468-}{
469- {
470- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
471- without: []string{"root-disk"},
472- final: "mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
473- },
474- {
475- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
476- without: []string{"mem"},
477- final: "root-disk=8G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
478- },
479- {
480- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
481- without: []string{"arch"},
482- final: "root-disk=8G mem=4G cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
483- },
484- {
485- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
486- without: []string{"cpu-power"},
487- final: "root-disk=8G mem=4G arch=amd64 cpu-cores=4 tags=foo container=lxc instance-type=bar",
488- },
489- {
490- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
491- without: []string{"cpu-cores"},
492- final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 tags=foo container=lxc instance-type=bar",
493- },
494- {
495- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
496- without: []string{"tags"},
497- final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 container=lxc instance-type=bar",
498- },
499- {
500- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
501- without: []string{"container"},
502- final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo instance-type=bar",
503- },
504- {
505- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
506- without: []string{"instance-type"},
507- final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc",
508- },
509- {
510- initial: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
511- without: []string{"root-disk", "mem", "arch"},
512- final: "cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
513- },
514-}
515+}{{
516+ initial: initialWithoutCons,
517+ without: []string{"root-disk"},
518+ final: "mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
519+}, {
520+ initial: initialWithoutCons,
521+ without: []string{"mem"},
522+ final: "root-disk=8G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
523+}, {
524+ initial: initialWithoutCons,
525+ without: []string{"arch"},
526+ final: "root-disk=8G mem=4G cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
527+}, {
528+ initial: initialWithoutCons,
529+ without: []string{"cpu-power"},
530+ final: "root-disk=8G mem=4G arch=amd64 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
531+}, {
532+ initial: initialWithoutCons,
533+ without: []string{"cpu-cores"},
534+ final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
535+}, {
536+ initial: initialWithoutCons,
537+ without: []string{"tags"},
538+ final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 networks=net1,^net2 container=lxc instance-type=bar",
539+}, {
540+ initial: initialWithoutCons,
541+ without: []string{"networks"},
542+ final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo container=lxc instance-type=bar",
543+}, {
544+ initial: initialWithoutCons,
545+ without: []string{"container"},
546+ final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 instance-type=bar",
547+}, {
548+ initial: initialWithoutCons,
549+ without: []string{"instance-type"},
550+ final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc",
551+}, {
552+ initial: initialWithoutCons,
553+ without: []string{"root-disk", "mem", "arch"},
554+ final: "cpu-power=1000 cpu-cores=4 tags=foo networks=net1,^net2 container=lxc instance-type=bar",
555+}}
556
557 func (s *ConstraintsSuite) TestWithout(c *gc.C) {
558 for i, t := range withoutTests {
559@@ -524,7 +552,7 @@
560 initial := constraints.MustParse(t.initial)
561 final, err := constraints.Without(initial, t.without...)
562 c.Assert(err, gc.IsNil)
563- c.Check(final, gc.DeepEquals, constraints.MustParse(t.final))
564+ c.Check(final, jc.DeepEquals, constraints.MustParse(t.final))
565 }
566 }
567
568@@ -537,7 +565,7 @@
569 func (s *ConstraintsSuite) TestAttributesWithValues(c *gc.C) {
570 for i, consStr := range []string{
571 "",
572- "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 instance-type=foo tags=foo,bar",
573+ "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 instance-type=foo tags=foo,bar networks=net1,^net2",
574 } {
575 c.Logf("test %d", i)
576 cons := constraints.MustParse(consStr)
577@@ -576,6 +604,11 @@
578 } else {
579 assertMissing("tags")
580 }
581+ if cons.Networks != nil {
582+ c.Check(obtained["networks"], gc.DeepEquals, *cons.Networks)
583+ } else {
584+ assertMissing("networks")
585+ }
586 if cons.InstanceType != nil {
587 c.Check(obtained["instance-type"], gc.Equals, *cons.InstanceType)
588 } else {
589@@ -590,13 +623,13 @@
590 expected []string
591 }{
592 {
593- cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
594- attrs: []string{"root-disk", "tags", "mem"},
595- expected: []string{"root-disk", "mem"},
596+ cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 networks=net1,^net2 cpu-cores=4",
597+ attrs: []string{"root-disk", "tags", "mem", "networks"},
598+ expected: []string{"root-disk", "mem", "networks"},
599 },
600 {
601 cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
602- attrs: []string{"tags"},
603+ attrs: []string{"tags", "networks"},
604 expected: []string{},
605 },
606 }
607@@ -606,6 +639,6 @@
608 c.Logf("test %d", i)
609 cons := constraints.MustParse(t.cons)
610 obtained := constraints.HasAny(cons, t.attrs...)
611- c.Check(obtained, gc.DeepEquals, t.expected)
612+ c.Check(obtained, jc.DeepEquals, t.expected)
613 }
614 }
615
616=== modified file 'state/constraints.go'
617--- state/constraints.go 2014-05-13 04:30:48 +0000
618+++ state/constraints.go 2014-05-29 14:17:10 +0000
619@@ -25,6 +25,7 @@
620 InstanceType *string
621 Container *instance.ContainerType
622 Tags *[]string `bson:",omitempty"`
623+ Networks *[]string `bson:",omitempty"`
624 }
625
626 func (doc constraintsDoc) value() constraints.Value {
627@@ -36,6 +37,7 @@
628 RootDisk: doc.RootDisk,
629 Container: doc.Container,
630 Tags: doc.Tags,
631+ Networks: doc.Networks,
632 InstanceType: doc.InstanceType,
633 }
634 }
635@@ -49,6 +51,7 @@
636 RootDisk: cons.RootDisk,
637 Container: cons.Container,
638 Tags: cons.Tags,
639+ Networks: cons.Networks,
640 InstanceType: cons.InstanceType,
641 }
642 }
643
644=== modified file 'state/constraintsvalidation_test.go'
645--- state/constraintsvalidation_test.go 2014-04-18 13:49:33 +0000
646+++ state/constraintsvalidation_test.go 2014-05-29 14:17:10 +0000
647@@ -4,6 +4,7 @@
648 package state_test
649
650 import (
651+ jc "github.com/juju/testing/checkers"
652 gc "launchpad.net/gocheck"
653
654 "launchpad.net/juju-core/constraints"
655@@ -39,50 +40,56 @@
656 consFallback string
657 cons string
658 expected string
659-}{
660- {
661- cons: "root-disk=8G mem=4G arch=amd64",
662- consFallback: "cpu-power=1000 cpu-cores=4",
663- expected: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
664- }, {
665- cons: "root-disk=8G mem=4G arch=amd64",
666- consFallback: "cpu-power=1000 cpu-cores=4 mem=8G",
667- expected: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
668- }, {
669- consFallback: "root-disk=8G cpu-cores=4 instance-type=foo",
670- expected: "root-disk=8G cpu-cores=4 instance-type=foo",
671- }, {
672- cons: "root-disk=8G cpu-cores=4 instance-type=foo",
673- expected: "root-disk=8G cpu-cores=4 instance-type=foo",
674- }, {
675- consFallback: "root-disk=8G instance-type=foo",
676- cons: "root-disk=8G cpu-cores=4 instance-type=bar",
677- expected: "root-disk=8G cpu-cores=4 instance-type=bar",
678- }, {
679- consFallback: "root-disk=8G mem=4G",
680- cons: "root-disk=8G cpu-cores=4 instance-type=bar",
681- expected: "root-disk=8G cpu-cores=4 instance-type=bar",
682- }, {
683- consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
684- cons: "root-disk=8G mem=4G",
685- expected: "root-disk=8G cpu-cores=4 mem=4G",
686- }, {
687- consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
688- cons: "root-disk=8G arch=amd64 mem=4G",
689- expected: "root-disk=8G cpu-cores=4 arch=amd64 mem=4G",
690- },
691-}
692+}{{
693+ cons: "root-disk=8G mem=4G arch=amd64",
694+ consFallback: "cpu-power=1000 cpu-cores=4",
695+ expected: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
696+}, {
697+ cons: "root-disk=8G mem=4G arch=amd64",
698+ consFallback: "cpu-power=1000 cpu-cores=4 mem=8G",
699+ expected: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
700+}, {
701+ consFallback: "root-disk=8G cpu-cores=4 instance-type=foo",
702+ expected: "root-disk=8G cpu-cores=4 instance-type=foo",
703+}, {
704+ cons: "root-disk=8G cpu-cores=4 instance-type=foo",
705+ expected: "root-disk=8G cpu-cores=4 instance-type=foo",
706+}, {
707+ consFallback: "root-disk=8G instance-type=foo",
708+ cons: "root-disk=8G cpu-cores=4 instance-type=bar",
709+ expected: "root-disk=8G cpu-cores=4 instance-type=bar",
710+}, {
711+ consFallback: "root-disk=8G mem=4G",
712+ cons: "root-disk=8G cpu-cores=4 instance-type=bar",
713+ expected: "root-disk=8G cpu-cores=4 instance-type=bar",
714+}, {
715+ consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
716+ cons: "root-disk=8G mem=4G",
717+ expected: "root-disk=8G cpu-cores=4 mem=4G",
718+}, {
719+ consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
720+ cons: "root-disk=8G arch=amd64 mem=4G",
721+ expected: "root-disk=8G cpu-cores=4 arch=amd64 mem=4G",
722+}, {
723+ consFallback: "cpu-cores=4 mem=4G networks=net1,^net3",
724+ cons: "networks=net2,^net4 mem=4G",
725+ expected: "cpu-cores=4 mem=4G networks=net2,^net4",
726+}, {
727+ consFallback: "cpu-cores=4 mem=2G networks=",
728+ cons: "mem=4G networks=net1,^net3",
729+ expected: "cpu-cores=4 mem=4G networks=net1,^net3",
730+}}
731
732 func (s *constraintsValidationSuite) TestMachineConstraints(c *gc.C) {
733 for i, t := range setConstraintsTests {
734- c.Logf("test %d", i)
735+ c.Logf("test %d: fallback: %q, cons: %q", i, t.consFallback, t.cons)
736 err := s.State.SetEnvironConstraints(constraints.MustParse(t.consFallback))
737 c.Check(err, gc.IsNil)
738 m, err := s.addOneMachine(c, constraints.MustParse(t.cons))
739 c.Check(err, gc.IsNil)
740 cons, err := m.Constraints()
741 c.Check(err, gc.IsNil)
742- c.Check(cons, gc.DeepEquals, constraints.MustParse(t.expected))
743+ c.Check(cons, jc.DeepEquals, constraints.MustParse(t.expected))
744 }
745 }
746
747@@ -90,15 +97,15 @@
748 charm := s.AddTestingCharm(c, "wordpress")
749 service := s.AddTestingService(c, "wordpress", charm)
750 for i, t := range setConstraintsTests {
751- c.Logf("test %d", i)
752+ c.Logf("test %d: fallback: %q, cons: %q", i, t.consFallback, t.cons)
753 err := s.State.SetEnvironConstraints(constraints.MustParse(t.consFallback))
754 c.Check(err, gc.IsNil)
755 err = service.SetConstraints(constraints.MustParse(t.cons))
756 c.Check(err, gc.IsNil)
757 u, err := service.AddUnit()
758 c.Check(err, gc.IsNil)
759- cons, err := state.UnitConstraints(u)
760+ ucons, err := state.UnitConstraints(u)
761 c.Check(err, gc.IsNil)
762- c.Check(*cons, gc.DeepEquals, constraints.MustParse(t.expected))
763+ c.Check(*ucons, jc.DeepEquals, constraints.MustParse(t.expected))
764 }
765 }

Subscribers

People subscribed via source and target branches

to status/vote changes: