Merge lp:~axwalk/juju-core/instances-arch-preference into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 2741
Proposed branch: lp:~axwalk/juju-core/instances-arch-preference
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 349 lines (+160/-66)
4 files modified
environs/instances/image.go (+44/-19)
environs/instances/image_test.go (+94/-47)
juju/arch/arch.go (+15/-0)
juju/arch/arch_test.go (+7/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/instances-arch-preference
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+219839@code.launchpad.net

Commit message

Fix arch preference algorithm

Change the instance architecture preference
selection to widest word-size, then first
arch name by alphabetical order. For tie-
breakers, choose the instance type based on
provider-specific ordering.

In the future we may want to extend the
ordering to include cost. For now, we rely
on the providers ordering instance types by
their own preference (no change here).

https://codereview.appspot.com/94530045/

Description of the change

Fix arch preference algorithm

Change the instance architecture preference
selection to widest word-size, then first
arch name by alphabetical order. For tie-
breakers, choose the instance type based on
provider-specific ordering.

In the future we may want to extend the
ordering to include cost. For now, we rely
on the providers ordering instance types by
their own preference (no change here).

https://codereview.appspot.com/94530045/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+219839_code.launchpad.net,

Message:
Please take a look.

Description:
Fix arch preference algorithm

Change the instance architecture preference
selection to widest word-size, then first
arch name by alphabetical order. For tie-
breakers, choose the instance type based on
provider-specific ordering.

In the future we may want to extend the
ordering to include cost. For now, we rely
on the providers ordering instance types by
their own preference (no change here).

https://code.launchpad.net/~axwalk/juju-core/instances-arch-preference/+merge/219839

(do not edit description out of merge proposal)

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

Affected files (+76, -58 lines):
   A [revision details]
   M environs/instances/image.go
   M environs/instances/image_test.go
   M juju/arch/arch.go
   M juju/arch/arch_test.go

Revision history for this message
William Reade (fwereade) wrote :

I feel like we're missing some tests here. Otherwise looking good.

https://codereview.appspot.com/94530045/diff/1/environs/instances/image_test.go
File environs/instances/image_test.go (right):

https://codereview.appspot.com/94530045/diff/1/environs/instances/image_test.go#newcode194
environs/instances/image_test.go:194: },
Could use a few more tests here, I think.

https://codereview.appspot.com/94530045/diff/1/juju/arch/arch.go
File juju/arch/arch.go (right):

https://codereview.appspot.com/94530045/diff/1/juju/arch/arch.go#newcode36
juju/arch/arch.go:36: PPC64: {64},
Nice, thanks for this.

https://codereview.appspot.com/94530045/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Please take a look.

https://codereview.appspot.com/94530045/diff/1/environs/instances/image_test.go
File environs/instances/image_test.go (right):

https://codereview.appspot.com/94530045/diff/1/environs/instances/image_test.go#newcode194
environs/instances/image_test.go:194: },
On 2014/05/16 21:12:38, fwereade wrote:
> Could use a few more tests here, I think.

Done.

https://codereview.appspot.com/94530045/

Revision history for this message
Ian Booth (wallyworld) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'environs/instances/image.go'
2--- environs/instances/image.go 2014-05-09 13:24:50 +0000
3+++ environs/instances/image.go 2014-05-19 03:30:40 +0000
4@@ -5,6 +5,7 @@
5
6 import (
7 "fmt"
8+ "sort"
9
10 "launchpad.net/juju-core/constraints"
11 "launchpad.net/juju-core/environs/imagemetadata"
12@@ -44,6 +45,8 @@
13 type InstanceSpec struct {
14 InstanceType InstanceType
15 Image Image
16+ // order is used to sort InstanceSpec based on the input InstanceTypes.
17+ order int
18 }
19
20 // FindInstanceSpec returns an InstanceSpec satisfying the supplied InstanceConstraint.
21@@ -83,13 +86,17 @@
22 for _, itype := range matchingTypes {
23 for _, image := range possibleImages {
24 if image.match(itype) {
25- specs = append(specs, &InstanceSpec{itype, image})
26+ specs = append(specs, &InstanceSpec{
27+ InstanceType: itype,
28+ Image: image,
29+ order: len(specs),
30+ })
31 }
32 }
33 }
34-
35- if spec := preferredSpec(specs); spec != nil {
36- return spec, nil
37+ if len(specs) > 0 {
38+ sort.Sort(byArch(specs))
39+ return specs[0], nil
40 }
41
42 names := make([]string, len(matchingTypes))
43@@ -99,21 +106,39 @@
44 return nil, fmt.Errorf("no %q images in %s matching instance types %v", ic.Series, ic.Region, names)
45 }
46
47-// preferredSpec will if possible return a spec with arch matching that
48-// of the host machine.
49-func preferredSpec(specs []*InstanceSpec) *InstanceSpec {
50- if len(specs) > 1 {
51- hostArch := arch.HostArch()
52- for _, spec := range specs {
53- if spec.Image.Arch == hostArch {
54- return spec
55- }
56- }
57- }
58- if len(specs) > 0 {
59- return specs[0]
60- }
61- return nil
62+// byArch sorts InstanceSpecs first by descending word-size, then
63+// alphabetically by name, and choose the first spec in the sequence.
64+type byArch []*InstanceSpec
65+
66+func (a byArch) Len() int {
67+ return len(a)
68+}
69+
70+func (a byArch) Less(i, j int) bool {
71+ iArchName := a[i].Image.Arch
72+ jArchName := a[j].Image.Arch
73+ iArch := arch.Info[iArchName]
74+ jArch := arch.Info[jArchName]
75+ // Wider word-size first.
76+ switch {
77+ case iArch.WordSize > jArch.WordSize:
78+ return true
79+ case iArch.WordSize < jArch.WordSize:
80+ return false
81+ }
82+ // Alphabetically by arch name.
83+ switch {
84+ case iArchName < jArchName:
85+ return true
86+ case iArchName > jArchName:
87+ return false
88+ }
89+ // If word-size and name the same, keep stable.
90+ return a[i].order < a[j].order
91+}
92+
93+func (a byArch) Swap(i, j int) {
94+ a[i], a[j] = a[j], a[i]
95 }
96
97 // Image holds the attributes that vary amongst relevant images for
98
99=== modified file 'environs/instances/image_test.go'
100--- environs/instances/image_test.go 2014-05-09 13:24:50 +0000
101+++ environs/instances/image_test.go 2014-05-19 03:30:40 +0000
102@@ -11,7 +11,6 @@
103 "launchpad.net/juju-core/constraints"
104 "launchpad.net/juju-core/environs/imagemetadata"
105 "launchpad.net/juju-core/environs/simplestreams"
106- "launchpad.net/juju-core/juju/arch"
107 "launchpad.net/juju-core/testing/testbase"
108 "launchpad.net/juju-core/utils"
109 )
110@@ -91,10 +90,10 @@
111 }
112 }
113 },
114- "com.ubuntu.cloud:server:12.04:arm": {
115+ "com.ubuntu.cloud:server:12.04:armhf": {
116 "release": "precise",
117 "version": "12.04",
118- "arch": "arm",
119+ "arch": "armhf",
120 "versions": {
121 "20121218": {
122 "items": {
123@@ -117,7 +116,57 @@
124 "id": "ami-00000036"
125 }
126 },
127- "pubname": "ubuntu-precise-12.04-arm-server-20121218",
128+ "pubname": "ubuntu-precise-12.04-armhf-server-20121218",
129+ "label": "release"
130+ }
131+ }
132+ },
133+ "com.ubuntu.cloud:server:12.04:i386": {
134+ "release": "precise",
135+ "version": "12.04",
136+ "arch": "i386",
137+ "versions": {
138+ "20121218": {
139+ "items": {
140+ "apne1pe": {
141+ "root_store": "ebs",
142+ "virt": "pv",
143+ "region": "ap-northeast-1",
144+ "id": "ami-b79b09b6"
145+ },
146+ "test1pe": {
147+ "root_store": "ebs",
148+ "virt": "pv",
149+ "region": "test",
150+ "id": "ami-b79b09b7"
151+ }
152+ },
153+ "pubname": "ubuntu-precise-12.04-i386-server-20121218",
154+ "label": "release"
155+ }
156+ }
157+ },
158+ "com.ubuntu.cloud:server:12.04:ppc64": {
159+ "release": "precise",
160+ "version": "12.04",
161+ "arch": "ppc64",
162+ "versions": {
163+ "20121218": {
164+ "items": {
165+ "apne1pe": {
166+ "root_store": "ebs",
167+ "virt": "pv",
168+ "region": "ap-northeast-1",
169+ "id": "ami-b79b09b8"
170+ },
171+ "test1pe": {
172+ "root_store": "ebs",
173+ "virt": "pv",
174+ "region": "test",
175+ "id": "ami-b79b09b9"
176+ }
177+ },
178+ "pubname": "ubuntu-precise-12.04-ppc64-server-20121218",
179 "label": "release"
180 }
181 }
182@@ -167,10 +216,10 @@
183
184 func (p *instanceSpecTestParams) init() {
185 if p.arches == nil {
186- p.arches = []string{"amd64", "arm"}
187+ p.arches = []string{"amd64", "armhf"}
188 }
189 if p.instanceTypes == nil {
190- p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64", "arm"}}}
191+ p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64", "armhf"}}}
192 p.instanceTypeId = "1"
193 p.instanceTypeName = "it-1"
194 }
195@@ -187,6 +236,42 @@
196 },
197 },
198 {
199+ desc: "prefer amd64 over i386",
200+ region: "test",
201+ imageId: "ami-00000033",
202+ arches: []string{"amd64", "i386"},
203+ instanceTypes: []InstanceType{
204+ {Id: "1", Name: "it-1", Arches: []string{"i386", "amd64"}, VirtType: &pv, Mem: 512},
205+ },
206+ },
207+ {
208+ desc: "prefer armhf over i386 (first alphabetical wins)",
209+ region: "test",
210+ imageId: "ami-00000034",
211+ arches: []string{"armhf", "i386"},
212+ instanceTypes: []InstanceType{
213+ {Id: "1", Name: "it-1", Arches: []string{"armhf", "i386"}, VirtType: &pv, Mem: 512},
214+ },
215+ },
216+ {
217+ desc: "prefer ppc64 over i386 (64-bit trumps 32-bit, regardless of alphabetical order)",
218+ region: "test",
219+ imageId: "ami-b79b09b9",
220+ arches: []string{"ppc64", "i386"},
221+ instanceTypes: []InstanceType{
222+ {Id: "1", Name: "it-1", Arches: []string{"i386", "ppc64"}, VirtType: &pv, Mem: 512},
223+ },
224+ },
225+ {
226+ desc: "prefer amd64 over arm64 (first 64-bit alphabetical wins)",
227+ region: "test",
228+ imageId: "ami-00000033",
229+ arches: []string{"arm64", "amd64"},
230+ instanceTypes: []InstanceType{
231+ {Id: "1", Name: "it-1", Arches: []string{"arm64", "amd64"}, VirtType: &pv, Mem: 512},
232+ },
233+ },
234+ {
235 desc: "explicit release stream",
236 region: "test",
237 stream: "released",
238@@ -243,7 +328,7 @@
239 {
240 desc: "no image exists in metadata",
241 region: "invalid-region",
242- err: `no "precise" images in invalid-region with arches \[amd64 arm\]`,
243+ err: `no "precise" images in invalid-region with arches \[amd64 armhf\]`,
244 },
245 {
246 desc: "no valid instance types",
247@@ -307,44 +392,6 @@
248 }
249 }
250
251-func (s *imageSuite) TestPreferredSpec(c *gc.C) {
252- type prefTest struct {
253- desc string
254- specs []*InstanceSpec
255- expected *InstanceSpec
256- }
257-
258- s.PatchValue(&arch.HostArch, func() string { return arch.ARM64 })
259-
260- amd64 := &InstanceSpec{Image: Image{Arch: arch.AMD64}}
261- i386 := &InstanceSpec{Image: Image{Arch: arch.I386}}
262- arm64 := &InstanceSpec{Image: Image{Arch: arch.ARM64}}
263-
264- prefTests := []prefTest{
265- {
266- "choose hostarch (arm64) over other arches",
267- []*InstanceSpec{i386, arm64, amd64},
268- arm64,
269- },
270- {
271- "choose first image if no arm64",
272- []*InstanceSpec{i386, amd64},
273- i386,
274- },
275- {
276- "choose only image only one there",
277- []*InstanceSpec{amd64},
278- amd64,
279- },
280- }
281-
282- for n, test := range prefTests {
283- c.Logf("PreferredSpec test %d: %s", n, test.desc)
284- actual := preferredSpec(test.specs)
285- c.Assert(actual, gc.Equals, test.expected)
286- }
287-}
288-
289 var imageMatchtests = []struct {
290 image Image
291 itype InstanceType
292@@ -356,14 +403,14 @@
293 match: true,
294 }, {
295 image: Image{Arch: "amd64"},
296- itype: InstanceType{Arches: []string{"amd64", "arm"}},
297+ itype: InstanceType{Arches: []string{"amd64", "armhf"}},
298 match: true,
299 }, {
300 image: Image{Arch: "amd64", VirtType: hvm},
301 itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm},
302 match: true,
303 }, {
304- image: Image{Arch: "arm"},
305+ image: Image{Arch: "armhf"},
306 itype: InstanceType{Arches: []string{"amd64"}},
307 }, {
308 image: Image{Arch: "amd64", VirtType: hvm},
309
310=== modified file 'juju/arch/arch.go'
311--- juju/arch/arch.go 2014-05-08 06:58:42 +0000
312+++ juju/arch/arch.go 2014-05-19 03:30:40 +0000
313@@ -27,6 +27,21 @@
314 PPC64,
315 }
316
317+// Info records the information regarding each architecture recognised by Juju.
318+var Info = map[string]ArchInfo{
319+ AMD64: {64},
320+ I386: {32},
321+ ARM: {32},
322+ ARM64: {64},
323+ PPC64: {64},
324+}
325+
326+// ArchInfo is a struct containing information about a supported architecture.
327+type ArchInfo struct {
328+ // WordSize is the architecture's word size, in bits.
329+ WordSize int
330+}
331+
332 // archREs maps regular expressions for matching
333 // `uname -m` to architectures recognised by Juju.
334 var archREs = []struct {
335
336=== modified file 'juju/arch/arch_test.go'
337--- juju/arch/arch_test.go 2014-05-08 06:58:42 +0000
338+++ juju/arch/arch_test.go 2014-05-19 03:30:40 +0000
339@@ -52,3 +52,10 @@
340 }
341 c.Assert(arch.IsSupportedArch("invalid"), jc.IsFalse)
342 }
343+
344+func (s *archSuite) TestArchInfo(c *gc.C) {
345+ for _, a := range arch.AllSupportedArches {
346+ _, ok := arch.Info[a]
347+ c.Assert(ok, jc.IsTrue)
348+ }
349+}

Subscribers

People subscribed via source and target branches

to status/vote changes: