func (s *charmsSuite) TestUploadRepackagesNestedArchives(c *gc.C) { // Make a clone of the dummy charm in a nested directory. rootDir := c.MkDir() dirPath := filepath.Join(rootDir, "subdir1", "subdir2") err := os.MkdirAll(dirPath, 0755) c.Assert(err, gc.IsNil) dir := charmtesting.Charms.ClonedDir(dirPath, "dummy") // Now tweak the path the dir thinks it is in and bundle it. dir.Path = rootDir tempFile, err := ioutil.TempFile(c.MkDir(), "charm") c.Assert(err, gc.IsNil) defer tempFile.Close() defer os.Remove(tempFile.Name()) err = dir.BundleTo(tempFile) c.Assert(err, gc.IsNil) // Try reading it as a bundle - should fail due to nested dirs. _, err = charm.ReadBundle(tempFile.Name()) c.Assert(err, gc.ErrorMatches, "bundle file not found: metadata.yaml") // Now try uploading it - should succeeed and be repackaged. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, tempFile.Name()) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-1") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 1) c.Assert(sch.IsUploaded(), jc.IsTrue) // Get it from the storage and try to read it as a bundle - it // should succeed, because it was repackaged during upload to // strip nested dirs. archiveName := strings.TrimPrefix(sch.BundleURL().RequestURI(), "/dummyenv/private/") storage, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) reader, err := storage.Get(archiveName) c.Assert(err, gc.IsNil) defer reader.Close() data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) downloadedFile, err := ioutil.TempFile(c.MkDir(), "downloaded") c.Assert(err, gc.IsNil) defer downloadedFile.Close() defer os.Remove(downloadedFile.Name()) err = ioutil.WriteFile(downloadedFile.Name(), data, 0644) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundle(downloadedFile.Name()) c.Assert(err, gc.IsNil) c.Assert(bundle.Revision(), jc.DeepEquals, sch.Revision()) c.Assert(bundle.Meta(), jc.DeepEquals, sch.Meta()) c.Assert(bundle.Config(), jc.DeepEquals, sch.Config()) }
func (s *backupSuite) TestErrorWhenStoragePutFails(c *gc.C) { f := s.makeTempFile(c) defer f.Close() stor, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) dummy.Poison(stor, "/backups/foo", fmt.Errorf("blam")) err = uploadBackupToStorage(s.State, f) c.Assert(err, gc.ErrorMatches, "blam") }
func (s *charmsSuite) TestUploadRespectsLocalRevision(c *gc.C) { // Make a dummy charm dir with revision 123. dir := charmtesting.Charms.ClonedDir(c.MkDir(), "dummy") dir.SetDiskRevision(123) // Now bundle the dir. tempFile, err := ioutil.TempFile(c.MkDir(), "charm") c.Assert(err, gc.IsNil) defer tempFile.Close() defer os.Remove(tempFile.Name()) err = dir.BundleTo(tempFile) c.Assert(err, gc.IsNil) // Now try uploading it and ensure the revision persists. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, tempFile.Name()) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-123") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 123) c.Assert(sch.IsUploaded(), jc.IsTrue) // First rewind the reader, which was reset but BundleTo() above. _, err = tempFile.Seek(0, 0) c.Assert(err, gc.IsNil) // Finally, verify the SHA256 and uploaded URL. expectedSHA256, _, err := utils.ReadSHA256(tempFile) c.Assert(err, gc.IsNil) name := charm.Quote(expectedURL.String()) storage, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) expectedUploadURL, err := storage.URL(name) c.Assert(err, gc.IsNil) c.Assert(sch.BundleURL().String(), gc.Equals, expectedUploadURL) c.Assert(sch.BundleSha256(), gc.Equals, expectedSHA256) reader, err := storage.Get(name) c.Assert(err, gc.IsNil) defer reader.Close() downloadedSHA256, _, err := utils.ReadSHA256(reader) c.Assert(err, gc.IsNil) c.Assert(downloadedSHA256, gc.Equals, expectedSHA256) }
func (s *backupSuite) TestBackupCalledAndFileServedAndStored(c *gc.C) { testGetMongoConnectionInfo := func(thisState *state.State) *authentication.MongoInfo { info := &authentication.MongoInfo{ Password: "******", Tag: names.NewMachineTag("0"), } info.Addrs = append(info.Addrs, "localhost:80") return info } var b happyBackup s.PatchValue(&apiserver.Backup, b.Backup) s.PatchValue(&apiserver.GetMongoConnectionInfo, testGetMongoConnectionInfo) resp, err := s.authRequest(c, "POST", s.backupURL(c), "", nil) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Check(b.tempDir, gc.NotNil) _, err = os.Stat(b.tempDir) c.Check(err, jc.Satisfies, os.IsNotExist) c.Check(b.mongoPassword, gc.Equals, "foobar") c.Check(b.username, gc.Equals, "machine-0") c.Check(b.address, gc.Equals, "localhost:80") c.Check(resp.StatusCode, gc.Equals, 200) c.Check(resp.Header.Get("Digest"), gc.Equals, "SHA=some-sha") c.Check(resp.Header.Get("Content-Disposition"), gc.Equals, "attachment; filename=\"testBackupFile\"") c.Check(resp.Header.Get("Content-Type"), gc.Equals, "application/octet-stream") body, _ := ioutil.ReadAll(resp.Body) c.Check(body, jc.DeepEquals, []byte("foobarbam")) stor, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) storReader, err := stor.Get("/backups/testBackupFile") c.Assert(err, gc.IsNil) bodyFromStorage, _ := ioutil.ReadAll(storReader) c.Check(bodyFromStorage, jc.DeepEquals, []byte("foobarbam")) }
// downloadCharm downloads the given charm name from the provider storage and // saves the corresponding zip archive to the given charmArchivePath. func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error { // Get the provider storage. storage, err := environs.GetStorage(h.state) if err != nil { return errors.Annotate(err, "cannot access provider storage") } // Use the storage to retrieve and save the charm archive. reader, err := storage.Get(name) if err != nil { return errors.Annotate(err, "charm not found in the provider storage") } defer reader.Close() data, err := ioutil.ReadAll(reader) if err != nil { return errors.Annotate(err, "cannot read charm data") } // In order to avoid races, the archive is saved in a temporary file which // is then atomically renamed. The temporary file is created in the // charm cache directory so that we can safely assume the rename source and // target live in the same file system. cacheDir := filepath.Dir(charmArchivePath) if err = os.MkdirAll(cacheDir, 0755); err != nil { return errors.Annotate(err, "cannot create the charms cache") } tempCharmArchive, err := ioutil.TempFile(cacheDir, "charm") if err != nil { return errors.Annotate(err, "cannot create charm archive temp file") } defer tempCharmArchive.Close() if err = ioutil.WriteFile(tempCharmArchive.Name(), data, 0644); err != nil { return errors.Annotate(err, "error processing charm archive download") } if err = os.Rename(tempCharmArchive.Name(), charmArchivePath); err != nil { defer os.Remove(tempCharmArchive.Name()) return errors.Annotate(err, "error renaming the charm archive") } return nil }
// repackageAndUploadCharm expands the given charm archive to a // temporary directoy, repackages it with the given curl's revision, // then uploads it to providr storage, and finally updates the state. func (h *charmsHandler) repackageAndUploadCharm(archive *charm.CharmArchive, curl *charm.URL) error { // Create a temp dir to contain the extracted charm // dir and the repackaged archive. tempDir, err := ioutil.TempDir("", "charm-download") if err != nil { return errors.Annotate(err, "cannot create temp directory") } defer os.RemoveAll(tempDir) extractPath := filepath.Join(tempDir, "extracted") repackagedPath := filepath.Join(tempDir, "repackaged.zip") repackagedArchive, err := os.Create(repackagedPath) if err != nil { return errors.Annotate(err, "cannot repackage uploaded charm") } defer repackagedArchive.Close() // Expand and repack it with the revision specified by curl. archive.SetRevision(curl.Revision) if err := archive.ExpandTo(extractPath); err != nil { return errors.Annotate(err, "cannot extract uploaded charm") } charmDir, err := charm.ReadCharmDir(extractPath) if err != nil { return errors.Annotate(err, "cannot read extracted charm") } // Bundle the charm and calculate its sha256 hash at the // same time. hash := sha256.New() err = charmDir.ArchiveTo(io.MultiWriter(hash, repackagedArchive)) if err != nil { return errors.Annotate(err, "cannot repackage uploaded charm") } bundleSHA256 := hex.EncodeToString(hash.Sum(nil)) size, err := repackagedArchive.Seek(0, 2) if err != nil { return errors.Annotate(err, "cannot get charm file size") } // Now upload to provider storage. if _, err := repackagedArchive.Seek(0, 0); err != nil { return errors.Annotate(err, "cannot rewind the charm file reader") } storage, err := environs.GetStorage(h.state) if err != nil { return errors.Annotate(err, "cannot access provider storage") } name := charm.Quote(curl.String()) if err := storage.Put(name, repackagedArchive, size); err != nil { return errors.Annotate(err, "cannot upload charm to provider storage") } storageURL, err := storage.URL(name) if err != nil { return errors.Annotate(err, "cannot get storage URL for charm") } bundleURL, err := url.Parse(storageURL) if err != nil { return errors.Annotate(err, "cannot parse storage URL") } // And finally, update state. _, err = h.state.UpdateUploadedCharm(archive, curl, bundleURL, bundleSHA256) if err != nil { return errors.Annotate(err, "cannot update uploaded charm in state") } return nil }
backups backups.Backups } // NewAPI creates a new instance of the Backups API facade. func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*API, error) { if !authorizer.AuthClient() { return nil, errors.Trace(common.ErrPerm) } stor, err := newBackupsStorage(st) if err != nil { return nil, errors.Trace(err) } b := API{ st: st, backups: backups.NewBackups(stor), } return &b, nil } var newBackupsStorage = func(st *state.State) (filestorage.FileStorage, error) { envStor, err := environs.GetStorage(st) if err != nil { return nil, errors.Trace(err) } storage := state.NewBackupsStorage(st, envStor) return storage, nil }