// manifestSender sends a JSON-encoded response to the client including the // list of files contained in the charm bundle. func (h *charmsHandler) manifestSender(w http.ResponseWriter, r *http.Request, bundle *charm.Bundle) { manifest, err := bundle.Manifest() if err != nil { http.Error( w, fmt.Sprintf("unable to read archive in %q: %v", bundle.Path, err), http.StatusInternalServerError) return } h.sendJSON(w, http.StatusOK, ¶ms.CharmsResponse{Files: manifest.SortedValues()}) }
// SetCharm adds and removes charms in s. The affected charm is identified by // charmURL, which must be revisioned. If bundle is nil, the charm will be // removed; otherwise, it will be stored. It is an error to store a bundle // under a charmURL that does not share its name and revision. func (s *MockCharmStore) SetCharm(charmURL *charm.URL, bundle *charm.Bundle) error { base := charmURL.WithRevision(-1).String() if charmURL.Revision < 0 { return fmt.Errorf("bad charm url revision") } if bundle == nil { delete(s.charms[base], charmURL.Revision) return nil } bundleRev := bundle.Revision() bundleName := bundle.Meta().Name if bundleName != charmURL.Name || bundleRev != charmURL.Revision { return fmt.Errorf("charm url %s mismatch with bundle %s-%d", charmURL, bundleName, bundleRev) } if _, found := s.charms[base]; !found { s.charms[base] = map[int]*charm.Bundle{} } s.charms[base][charmURL.Revision] = bundle 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.Bundle, 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.ReadDir(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.BundleTo(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 }