~jameinel/juju-core/scale-testing

« back to all changes in this revision

Viewing changes to state/apiserver/charms_test.go

[r=dimitern],[bug=1067979] state;apiserver: Fix a race - lp bug #1067979

This introduces some changes to how charm store
charms are added through the API (in state and
to provider storage). Now PrepareStoreCharmUpload
is called before trying to download the charm,
repackage it and upload it to storage, in order
to reserve a charm URL in state with pending
state. Added a test that demonstrates multiple
concurrent deployments of the same charm does
not cause the race issues, like mentioned in
the bug.

A few drive-by fixes brought up during review:
* Added ReadSHA256 and ReadFileSHA256 helpers in
  utils, and changed most places where hashes
  are calculated to use them.
* Charms are now uploaded to storage with a
  randomly generated archive names with the
  format "<charm-name>-<revision>-<uuid>".
  This allows multiple concurrent uploads
  to happen safely, and at the end AddCharm
  in the API checks to see if the charm info
  is already updated in state and if so, deletes
  the duplicated upload.
* Added GetEnvironStorage helper to environs/testing.
* Fixed potential compatibility issues with older
  versions and the recently added PendingUpload
  and Placeholder fields of the charm document.

Also tested multiple concurrent deployments with
the local provider manually and updated the bug
accordingly.

https://codereview.appspot.com/53210044/

R=fwereade

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
package apiserver_test
5
5
 
6
6
import (
7
 
        "crypto/sha256"
8
 
        "encoding/hex"
9
7
        "encoding/json"
10
8
        "fmt"
11
9
        "io"
17
15
        gc "launchpad.net/gocheck"
18
16
 
19
17
        "launchpad.net/juju-core/charm"
 
18
        envtesting "launchpad.net/juju-core/environs/testing"
20
19
        jujutesting "launchpad.net/juju-core/juju/testing"
21
20
        "launchpad.net/juju-core/state"
22
21
        "launchpad.net/juju-core/state/api/params"
23
 
        "launchpad.net/juju-core/state/apiserver"
24
22
        coretesting "launchpad.net/juju-core/testing"
25
23
        jc "launchpad.net/juju-core/testing/checkers"
26
24
        "launchpad.net/juju-core/utils"
131
129
        c.Assert(sch.Revision(), gc.Equals, 2)
132
130
        c.Assert(sch.IsUploaded(), jc.IsTrue)
133
131
        // No more checks for these two here, because they
134
 
        // are verified in TestUploadRequiresSingleUploadedFile.
 
132
        // are verified in TestUploadRespectsLocalRevision.
135
133
        c.Assert(sch.BundleURL(), gc.Not(gc.Equals), "")
136
134
        c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), "")
137
135
}
164
162
        c.Assert(err, gc.IsNil)
165
163
 
166
164
        // Finally, verify the SHA256 and uploaded URL.
167
 
        expectedSHA256, _, err := getSHA256(tempFile)
 
165
        expectedSHA256, _, err := utils.ReadSHA256(tempFile)
168
166
        c.Assert(err, gc.IsNil)
169
167
        name := charm.Quote(expectedURL.String())
170
 
        storage, err := apiserver.GetEnvironStorage(s.State)
 
168
        storage, err := envtesting.GetEnvironStorage(s.State)
171
169
        c.Assert(err, gc.IsNil)
172
170
        expectedUploadURL, err := storage.URL(name)
173
171
        c.Assert(err, gc.IsNil)
174
172
 
175
173
        c.Assert(sch.BundleURL().String(), gc.Equals, expectedUploadURL)
176
174
        c.Assert(sch.BundleSha256(), gc.Equals, expectedSHA256)
 
175
 
 
176
        reader, err := storage.Get(name)
 
177
        c.Assert(err, gc.IsNil)
 
178
        defer reader.Close()
 
179
        downloadedSHA256, _, err := utils.ReadSHA256(reader)
 
180
        c.Assert(err, gc.IsNil)
 
181
        c.Assert(downloadedSHA256, gc.Equals, expectedSHA256)
177
182
}
178
183
 
179
184
func (s *charmsSuite) charmsURI(c *gc.C, query string) string {
230
235
        }
231
236
        c.Check(resp.StatusCode, gc.Equals, expCode)
232
237
}
233
 
 
234
 
func getSHA256(source io.ReadSeeker) (string, int64, error) {
235
 
        hash := sha256.New()
236
 
        size, err := io.Copy(hash, source)
237
 
        if err != nil {
238
 
                return "", 0, err
239
 
        }
240
 
        digest := hex.EncodeToString(hash.Sum(nil))
241
 
        return digest, size, nil
242
 
}