Merge lp:~julian-edwards/juju-core/maas-uuid-file-prefix into lp:~go-bot/juju-core/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 1989
Proposed branch: lp:~julian-edwards/juju-core/maas-uuid-file-prefix
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 200 lines (+62/-9)
3 files modified
provider/maas/environ_test.go (+7/-2)
provider/maas/storage.go (+22/-2)
provider/maas/storage_test.go (+33/-5)
To merge this branch: bzr merge lp:~julian-edwards/juju-core/maas-uuid-file-prefix
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+191336@code.launchpad.net

Commit message

Use the environment's UUID as a file prefix for all files in MAAS private storage to prevent different environments from overlapping.

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

Seems to be missing a test for having no environment-uuid at all? Otherwise LGTM (modulo what it's building on...).

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On Wednesday 16 Oct 2013 05:51:10 you wrote:
> Seems to be missing a test for having no environment-uuid at all? Otherwise
> LGTM (modulo what it's building on...).

That's what TesttprefixWithPrivateNamespaceIgnoresEmptyUUID is for.

Thanks for the review.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'provider/maas/environ_test.go'
2--- provider/maas/environ_test.go 2013-10-15 16:15:39 +0000
3+++ provider/maas/environ_test.go 2013-10-16 05:32:45 +0000
4@@ -4,6 +4,7 @@
5 package maas
6
7 import (
8+ "bytes"
9 "encoding/base64"
10 "fmt"
11 "io/ioutil"
12@@ -475,7 +476,9 @@
13 // Add a dummy file to storage so we can use that to check the
14 // obtained source later.
15 data := makeRandomBytes(10)
16- suite.testMAASObject.TestServer.NewFile("filename", data)
17+ stor := NewStorage(env)
18+ err := stor.Put("filename", bytes.NewBuffer([]byte(data)), int64(len(data)))
19+ c.Assert(err, gc.IsNil)
20 sources, err := imagemetadata.GetMetadataSources(env)
21 c.Assert(err, gc.IsNil)
22 c.Assert(len(sources), gc.Equals, 2)
23@@ -490,7 +493,9 @@
24 // Add a dummy file to storage so we can use that to check the
25 // obtained source later.
26 data := makeRandomBytes(10)
27- suite.testMAASObject.TestServer.NewFile("tools/filename", data)
28+ stor := NewStorage(env)
29+ err := stor.Put("tools/filename", bytes.NewBuffer([]byte(data)), int64(len(data)))
30+ c.Assert(err, gc.IsNil)
31 sources, err := envtools.GetMetadataSources(env)
32 c.Assert(err, gc.IsNil)
33 c.Assert(len(sources), gc.Equals, 1)
34
35=== modified file 'provider/maas/storage.go'
36--- provider/maas/storage.go 2013-09-18 22:54:32 +0000
37+++ provider/maas/storage.go 2013-10-16 05:32:45 +0000
38@@ -11,6 +11,7 @@
39 "io/ioutil"
40 "net/url"
41 "sort"
42+ "strings"
43 "sync"
44
45 "launchpad.net/gomaasapi"
46@@ -88,8 +89,21 @@
47 return obj, nil
48 }
49
50+// All filenames need to be namespaced so they are private to this environment.
51+// This prevents different environments from interfering with each other.
52+// We're using the environment's UUID here.
53+func (stor *maasStorage) prefixWithPrivateNamespace(name string) string {
54+ env := stor.getSnapshot().environUnlocked
55+ prefix := env.ecfg().maasEnvironmentUUID()
56+ if prefix != "" {
57+ return prefix + "-" + name
58+ }
59+ return name
60+}
61+
62 // Get is specified in the StorageReader interface.
63 func (stor *maasStorage) Get(name string) (io.ReadCloser, error) {
64+ name = stor.prefixWithPrivateNamespace(name)
65 fileObj, err := stor.retrieveFileObject(name)
66 if err != nil {
67 return nil, err
68@@ -108,6 +122,7 @@
69 // extractFilenames returns the filenames from a "list" operation on the
70 // MAAS API, sorted by name.
71 func (stor *maasStorage) extractFilenames(listResult gomaasapi.JSONObject) ([]string, error) {
72+ privatePrefix := stor.prefixWithPrivateNamespace("")
73 list, err := listResult.GetArray()
74 if err != nil {
75 return nil, err
76@@ -122,7 +137,8 @@
77 if err != nil {
78 return nil, err
79 }
80- result[index] = filename
81+ // When listing files we need to return them without our special prefix.
82+ result[index] = strings.TrimPrefix(filename, privatePrefix)
83 }
84 sort.Strings(result)
85 return result, nil
86@@ -130,6 +146,7 @@
87
88 // List is specified in the StorageReader interface.
89 func (stor *maasStorage) List(prefix string) ([]string, error) {
90+ prefix = stor.prefixWithPrivateNamespace(prefix)
91 params := make(url.Values)
92 params.Add("prefix", prefix)
93 snapshot := stor.getSnapshot()
94@@ -142,6 +159,7 @@
95
96 // URL is specified in the StorageReader interface.
97 func (stor *maasStorage) URL(name string) (string, error) {
98+ name = stor.prefixWithPrivateNamespace(name)
99 fileObj, err := stor.retrieveFileObject(name)
100 if err != nil {
101 return "", err
102@@ -174,6 +192,7 @@
103
104 // Put is specified in the StorageWriter interface.
105 func (stor *maasStorage) Put(name string, r io.Reader, length int64) error {
106+ name = stor.prefixWithPrivateNamespace(name)
107 data, err := ioutil.ReadAll(io.LimitReader(r, length))
108 if err != nil {
109 return err
110@@ -187,6 +206,7 @@
111
112 // Remove is specified in the StorageWriter interface.
113 func (stor *maasStorage) Remove(name string) error {
114+ name = stor.prefixWithPrivateNamespace(name)
115 // The only thing that can go wrong here, really, is that the file
116 // does not exist. But deletion is idempotent: deleting a file that
117 // is no longer there anyway is success, not failure.
118@@ -200,7 +220,7 @@
119 if err != nil {
120 return err
121 }
122- // Remove all the objects in parallel so that we incur less round-trips.
123+ // Remove all the objects in parallel so that we incur fewer round-trips.
124 // If we're in danger of having hundreds of objects,
125 // we'll want to change this to limit the number
126 // of concurrent operations.
127
128=== modified file 'provider/maas/storage_test.go'
129--- provider/maas/storage_test.go 2013-10-15 16:15:39 +0000
130+++ provider/maas/storage_test.go 2013-10-16 05:32:45 +0000
131@@ -29,8 +29,10 @@
132 // makeStorage creates a MAAS storage object for the running test.
133 func (s *storageSuite) makeStorage(name string) *maasStorage {
134 maasobj := s.testMAASObject.MAASObject
135- env := maasEnviron{name: name, maasClientUnlocked: &maasobj}
136- return NewStorage(&env).(*maasStorage)
137+ env := s.makeEnviron()
138+ env.name = name
139+ env.maasClientUnlocked = &maasobj
140+ return NewStorage(env).(*maasStorage)
141 }
142
143 // makeRandomBytes returns an array of arbitrary byte values.
144@@ -50,7 +52,10 @@
145 // Or don't, if you want consistent (and debuggable) results.
146 func (s *storageSuite) fakeStoredFile(stor storage.Storage, name string) gomaasapi.MAASObject {
147 data := makeRandomBytes(rand.Intn(10))
148- return s.testMAASObject.TestServer.NewFile(name, data)
149+ // The filename must be prefixed with the private namespace as we're
150+ // bypassing the Put() method that would normally do that.
151+ prefixFilename := stor.(*maasStorage).prefixWithPrivateNamespace("") + name
152+ return s.testMAASObject.TestServer.NewFile(prefixFilename, data)
153 }
154
155 func (s *storageSuite) TestGetSnapshotCreatesClone(c *gc.C) {
156@@ -93,7 +98,8 @@
157 fileContent, err := file.GetField("content")
158 c.Assert(err, gc.IsNil)
159
160- obj, err := stor.retrieveFileObject(filename)
161+ prefixFilename := stor.prefixWithPrivateNamespace(filename)
162+ obj, err := stor.retrieveFileObject(prefixFilename)
163 c.Assert(err, gc.IsNil)
164
165 uri, err := obj.GetField("anon_resource_uri")
166@@ -117,7 +123,8 @@
167 err := stor.Put(filename, bytes.NewReader(data), int64(len(data)))
168 c.Assert(err, gc.IsNil)
169
170- obj, err := stor.retrieveFileObject(filename)
171+ prefixFilename := stor.prefixWithPrivateNamespace(filename)
172+ obj, err := stor.retrieveFileObject(prefixFilename)
173 c.Assert(err, gc.IsNil)
174
175 base64Content, err := obj.GetField("content")
176@@ -390,3 +397,24 @@
177 c.Assert(err, gc.IsNil)
178 c.Assert(listing, gc.DeepEquals, []string{})
179 }
180+
181+func (s *storageSuite) TestprefixWithPrivateNamespacePrefixesWithUUID(c *gc.C) {
182+ sstor := NewStorage(s.makeEnviron())
183+ stor := sstor.(*maasStorage)
184+ uuid := stor.environUnlocked.ecfg().maasEnvironmentUUID()
185+ c.Assert(uuid, gc.Not(gc.Equals), "")
186+ expectedPrefix := uuid + "-"
187+ const name = "myname"
188+ expectedResult := expectedPrefix + name
189+ c.Assert(stor.prefixWithPrivateNamespace(name), gc.Equals, expectedResult)
190+}
191+
192+func (s *storageSuite) TesttprefixWithPrivateNamespaceIgnoresEmptyUUID(c *gc.C) {
193+ sstor := NewStorage(s.makeEnviron())
194+ stor := sstor.(*maasStorage)
195+ ecfg := stor.environUnlocked.ecfg()
196+ ecfg.attrs["environment-uuid"] = ""
197+
198+ const name = "myname"
199+ c.Assert(stor.prefixWithPrivateNamespace(name), gc.Equals, name)
200+}

Subscribers

People subscribed via source and target branches

to status/vote changes: