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 *BundleSuite) TestManifest(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) manifest, err := bundle.Manifest() c.Assert(err, gc.IsNil) c.Assert(manifest, jc.DeepEquals, set.NewStrings(dummyManifest...)) }
func (s *BundleSuite) TestBundleRevisionFile(c *gc.C) { charmDir := charmtesting.Charms.ClonedDirPath(c.MkDir(), "dummy") revPath := filepath.Join(charmDir, "revision") // Missing revision file err := os.Remove(revPath) c.Assert(err, gc.IsNil) bundle := extBundleDir(c, charmDir) c.Assert(bundle.Revision(), gc.Equals, 0) // Missing revision file with old revision in metadata file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0) c.Assert(err, gc.IsNil) _, err = file.Write([]byte("\nrevision: 1234\n")) c.Assert(err, gc.IsNil) bundle = extBundleDir(c, charmDir) c.Assert(bundle.Revision(), gc.Equals, 1234) // Revision file with bad content err = ioutil.WriteFile(revPath, []byte("garbage"), 0666) c.Assert(err, gc.IsNil) path := extBundleDirPath(c, charmDir) bundle, err = charm.ReadBundle(path) c.Assert(err, gc.ErrorMatches, "invalid revision file") c.Assert(bundle, gc.IsNil) }
func (s *BundleSuite) TestReadBundleWithoutActions(c *gc.C) { // Wordpress has config but no actions. path := charmtesting.Charms.BundlePath(c.MkDir(), "wordpress") bundle, err := charm.ReadBundle(path) c.Assert(err, gc.IsNil) // A lacking actions.yaml file still causes a proper // Actions value to be returned. c.Assert(bundle.Actions().ActionSpecs, gc.HasLen, 0) }
func (s *BundleSuite) TestReadBundleWithoutConfig(c *gc.C) { // Technically varnish has no config AND no actions. // Perhaps we should make this more orthogonal? path := charmtesting.Charms.BundlePath(c.MkDir(), "varnish") bundle, err := charm.ReadBundle(path) c.Assert(err, gc.IsNil) // A lacking config.yaml file still causes a proper // Config value to be returned. c.Assert(bundle.Config().Options, gc.HasLen, 0) }
// Read returns a charm bundle from the directory. If no bundle exists yet, // one will be downloaded and validated and copied into the directory before // being returned. Downloads will be aborted if a value is received on abort. func (d *BundlesDir) Read(info BundleInfo, abort <-chan struct{}) (Bundle, error) { path := d.bundlePath(info) if _, err := os.Stat(path); err != nil { if !os.IsNotExist(err) { return nil, err } else if err = d.download(info, abort); err != nil { return nil, err } } return charm.ReadBundle(path) }
// sendBundleContent uses the given bundleContentSenderFunc to send a response // related to the charm archive located in the given archivePath. func sendBundleContent(w http.ResponseWriter, r *http.Request, archivePath string, sender bundleContentSenderFunc) { bundle, err := charm.ReadBundle(archivePath) if err != nil { http.Error( w, fmt.Sprintf("unable to read archive in %q: %v", archivePath, err), http.StatusInternalServerError) return } // The bundleContentSenderFunc will set up and send an appropriate response. sender(w, r, bundle) }
func (s *BundleSuite) TestExpandTo(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) checkDummy(c, dir, path) }
func (s *BundleSuite) TestManifestNoRevision(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) dirPath := c.MkDir() err = bundle.ExpandTo(dirPath) c.Assert(err, gc.IsNil) err = os.Remove(filepath.Join(dirPath, "revision")) c.Assert(err, gc.IsNil) bundle = extBundleDir(c, dirPath) manifest, err := bundle.Manifest() c.Assert(err, gc.IsNil) c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyManifest...)) }
func (s *BundlesDirSuite) AddCharm(c *gc.C) (*uniter.Charm, *state.Charm, []byte) { curl := corecharm.MustParseURL("cs:quantal/dummy-1") surl, err := url.Parse(s.URL("/some/charm.bundle")) c.Assert(err, gc.IsNil) bunpath := charmtesting.Charms.BundlePath(c.MkDir(), "dummy") bun, err := corecharm.ReadBundle(bunpath) c.Assert(err, gc.IsNil) bundata, hash := readHash(c, bunpath) sch, err := s.State.AddCharm(bun, curl, surl, hash) c.Assert(err, gc.IsNil) apiCharm, err := s.uniter.Charm(sch.URL()) c.Assert(err, gc.IsNil) return apiCharm, sch, bundata }
// processPost handles a charm upload POST request after authentication. func (h *charmsHandler) processPost(r *http.Request) (*charm.URL, error) { query := r.URL.Query() series := query.Get("series") if series == "" { return nil, fmt.Errorf("expected series=URL argument") } // Make sure the content type is zip. contentType := r.Header.Get("Content-Type") if contentType != "application/zip" { return nil, fmt.Errorf("expected Content-Type: application/zip, got: %v", contentType) } tempFile, err := ioutil.TempFile("", "charm") if err != nil { return nil, fmt.Errorf("cannot create temp file: %v", err) } defer tempFile.Close() defer os.Remove(tempFile.Name()) if _, err := io.Copy(tempFile, r.Body); err != nil { return nil, fmt.Errorf("error processing file upload: %v", err) } err = h.processUploadedArchive(tempFile.Name()) if err != nil { return nil, err } archive, err := charm.ReadBundle(tempFile.Name()) if err != nil { return nil, fmt.Errorf("invalid charm archive: %v", err) } // We got it, now let's reserve a charm URL for it in state. archiveURL := &charm.URL{ Reference: charm.Reference{ Schema: "local", Name: archive.Meta().Name, Revision: archive.Revision(), }, Series: series, } preparedURL, err := h.state.PrepareLocalCharmUpload(archiveURL) if err != nil { return nil, err } // Now we need to repackage it with the reserved URL, upload it to // provider storage and update the state. err = h.repackageAndUploadCharm(archive, preparedURL) if err != nil { return nil, err } // All done. return preparedURL, nil }
func (s *BundleSuite) TestBundleSetRevision(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) c.Assert(bundle.Revision(), gc.Equals, 1) bundle.SetRevision(42) c.Assert(bundle.Revision(), gc.Equals, 42) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 42) }
func (br *bundleReader) AddCustomBundle(c *gc.C, url *corecharm.URL, customize func(path string)) charm.BundleInfo { base := c.MkDir() dirpath := charmtesting.Charms.ClonedDirPath(base, "dummy") if customize != nil { customize(dirpath) } dir, err := corecharm.ReadDir(dirpath) c.Assert(err, gc.IsNil) err = dir.SetDiskRevision(url.Revision) c.Assert(err, gc.IsNil) bunpath := filepath.Join(base, "bundle") file, err := os.Create(bunpath) c.Assert(err, gc.IsNil) defer file.Close() err = dir.BundleTo(file) c.Assert(err, gc.IsNil) bundle, err := corecharm.ReadBundle(bunpath) c.Assert(err, gc.IsNil) return br.AddBundle(c, url, bundle) }
func (s *BundleSuite) TestExpandToSetsHooksExecutable(c *gc.C) { charmDir := charmtesting.Charms.ClonedDir(c.MkDir(), "all-hooks") // Bundle manually, so we can check ExpandTo(), unaffected // by BundleTo()'s behavior bundlePath := filepath.Join(c.MkDir(), "bundle.charm") s.prepareBundle(c, charmDir, bundlePath) bundle, err := charm.ReadBundle(bundlePath) c.Assert(err, gc.IsNil) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) _, err = charm.ReadDir(path) c.Assert(err, gc.IsNil) for name := range bundle.Meta().Hooks() { hookName := string(name) info, err := os.Stat(filepath.Join(path, "hooks", hookName)) c.Assert(err, gc.IsNil) perm := info.Mode() & 0777 c.Assert(perm&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName)) } }
func (s *BundleSuite) TestReadBundle(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) checkDummy(c, bundle, s.bundlePath) }
func extBundleDir(c *gc.C, dirpath string) *charm.Bundle { path := extBundleDirPath(c, dirpath) bundle, err := charm.ReadBundle(path) c.Assert(err, gc.IsNil) return bundle }