Esempio n. 1
0
// CharmInfo retrieves the CharmInfo value for the charm at url.
func (s *Store) CharmInfo(url *charm.URL) (info *CharmInfo, err error) {
	session := s.session.Copy()
	defer session.Close()

	log.Debugf("store: Retrieving charm info for %s", url)
	rev := url.Revision
	url = url.WithRevision(-1)

	charms := session.Charms()
	var cdoc charmDoc
	var qdoc interface{}
	if rev == -1 {
		qdoc = bson.D{{"urls", url}}
	} else {
		qdoc = bson.D{{"urls", url}, {"revision", rev}}
	}
	err = charms.Find(qdoc).Sort("-revision").One(&cdoc)
	if err != nil {
		log.Errorf("store: Failed to find charm %s: %v", url, err)
		return nil, ErrNotFound
	}
	info = &CharmInfo{
		cdoc.Revision,
		cdoc.Digest,
		cdoc.Sha256,
		cdoc.Size,
		cdoc.FileId,
		cdoc.Meta,
		cdoc.Config,
	}
	return info, nil
}
Esempio n. 2
0
// PutCharm uploads the given charm to provider storage, and adds a
// state.Charm to the state.  The charm is not uploaded if a charm with
// the same URL already exists in the state.
// If bumpRevision is true, the charm must be a local directory,
// and the revision number will be incremented before pushing.
func (conn *Conn) PutCharm(curl *charm.URL, repo charm.Repository, bumpRevision bool) (*state.Charm, error) {
	if curl.Revision == -1 {
		rev, err := repo.Latest(curl)
		if err != nil {
			return nil, fmt.Errorf("cannot get latest charm revision: %v", err)
		}
		curl = curl.WithRevision(rev)
	}
	ch, err := repo.Get(curl)
	if err != nil {
		return nil, fmt.Errorf("cannot get charm: %v", err)
	}
	if bumpRevision {
		chd, ok := ch.(*charm.Dir)
		if !ok {
			return nil, fmt.Errorf("cannot increment revision of charm %q: not a directory", curl)
		}
		if err = chd.SetDiskRevision(chd.Revision() + 1); err != nil {
			return nil, fmt.Errorf("cannot increment revision of charm %q: %v", curl, err)
		}
		curl = curl.WithRevision(chd.Revision())
	}
	if sch, err := conn.State.Charm(curl); err == nil {
		return sch, nil
	}
	return conn.addCharm(curl, ch)
}
Esempio n. 3
0
// DeleteCharm deletes the charms matching url. If no revision is specified,
// all revisions of the charm are deleted.
func (s *Store) DeleteCharm(url *charm.URL) ([]*CharmInfo, error) {
	logger.Debugf("deleting charm %s", url)
	infos, err := s.getRevisions(url, 0)
	if err != nil {
		return nil, err
	}
	if len(infos) == 0 {
		return nil, ErrNotFound
	}
	session := s.session.Copy()
	defer session.Close()
	var deleted []*CharmInfo
	for _, info := range infos {
		err := session.Charms().Remove(
			bson.D{{"urls", url.WithRevision(-1)}, {"revision", info.Revision()}})
		if err != nil {
			logger.Errorf("failed to delete metadata for charm %s: %v", url, err)
			return deleted, err
		}
		err = session.CharmFS().RemoveId(info.fileId)
		if err != nil {
			logger.Errorf("failed to delete GridFS file for charm %s: %v", url, err)
			return deleted, err
		}
		deleted = append(deleted, info)
	}
	return deleted, err
}
Esempio n. 4
0
// Latest implements charm.Repository.Latest.
func (s *MockCharmStore) Latest(charmURL *charm.URL) (int, error) {
	charmURL = charmURL.WithRevision(-1)
	base, rev := s.interpret(charmURL)
	if _, found := s.charms[base][rev]; !found {
		return 0, fmt.Errorf("charm not found in mock store: %s", charmURL)
	}
	return rev, nil
}
Esempio n. 5
0
func (conn *Conn) addCharm(curl *charm.URL, ch charm.Charm) (*state.Charm, error) {
	var f *os.File
	name := charm.Quote(curl.String())
	switch ch := ch.(type) {
	case *charm.Dir:
		var err error
		if f, err = ioutil.TempFile("", name); err != nil {
			return nil, err
		}
		defer os.Remove(f.Name())
		defer f.Close()
		err = ch.BundleTo(f)
		if err != nil {
			return nil, fmt.Errorf("cannot bundle charm: %v", err)
		}
		if _, err := f.Seek(0, 0); err != nil {
			return nil, err
		}
	case *charm.Bundle:
		var err error
		if f, err = os.Open(ch.Path); err != nil {
			return nil, fmt.Errorf("cannot read charm bundle: %v", err)
		}
		defer f.Close()
	default:
		return nil, fmt.Errorf("unknown charm type %T", ch)
	}
	h := sha256.New()
	size, err := io.Copy(h, f)
	if err != nil {
		return nil, err
	}
	digest := hex.EncodeToString(h.Sum(nil))
	if _, err := f.Seek(0, 0); err != nil {
		return nil, err
	}
	storage := conn.Environ.Storage()
	log.Infof("writing charm to storage [%d bytes]", size)
	if err := storage.Put(name, f, size); err != nil {
		return nil, fmt.Errorf("cannot put charm: %v", err)
	}
	ustr, err := storage.URL(name)
	if err != nil {
		return nil, fmt.Errorf("cannot get storage URL for charm: %v", err)
	}
	u, err := url.Parse(ustr)
	if err != nil {
		return nil, fmt.Errorf("cannot parse storage URL: %v", err)
	}
	log.Infof("adding charm to state")
	sch, err := conn.State.AddCharm(ch, curl, u, digest)
	if err != nil {
		return nil, fmt.Errorf("cannot add charm: %v", err)
	}
	return sch, nil
}
Esempio n. 6
0
// interpret extracts from charmURL information relevant to both Latest and
// Get. The returned "base" is always the string representation of the
// unrevisioned part of charmURL; the "rev" wil be taken from the charmURL if
// available, and will otherwise be the revision of the latest charm in the
// store with the same "base".
func (s *MockCharmStore) interpret(charmURL *charm.URL) (base string, rev int) {
	base, rev = charmURL.WithRevision(-1).String(), charmURL.Revision
	if rev == -1 {
		for candidate := range s.charms[base] {
			if candidate > rev {
				rev = candidate
			}
		}
	}
	return
}
Esempio n. 7
0
func handleEvent(ctx *cmd.Context, curl *charm.URL, event *charm.EventResponse) error {
	switch event.Kind {
	case "published":
		curlRev := curl.WithRevision(event.Revision)
		log.Infof("charm published at %s as %s", event.Time, curlRev)
		fmt.Fprintln(ctx.Stdout, curlRev)
	case "publish-error":
		return fmt.Errorf("charm could not be published: %s", strings.Join(event.Errors, "; "))
	default:
		return fmt.Errorf("unknown event kind %q for charm %s", event.Kind, curl)
	}
	return nil
}
Esempio n. 8
0
// 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
}
Esempio n. 9
0
// getRevisions returns at most the last n revisions for charm at url,
// in descending revision order. For limit n=0, all revisions are returned.
func (s *Store) getRevisions(url *charm.URL, n int) ([]*CharmInfo, error) {
	session := s.session.Copy()
	defer session.Close()

	logger.Debugf("retrieving charm info for %s", url)
	rev := url.Revision
	url = url.WithRevision(-1)

	charms := session.Charms()
	var cdocs []charmDoc
	var qdoc interface{}
	if rev == -1 {
		qdoc = bson.D{{"urls", url}}
	} else {
		qdoc = bson.D{{"urls", url}, {"revision", rev}}
	}
	q := charms.Find(qdoc).Sort("-revision")
	if n > 0 {
		q = q.Limit(n)
	}
	if err := q.All(&cdocs); err != nil {
		logger.Errorf("failed to find charm %s: %v", url, err)
		return nil, ErrNotFound
	}
	var infos []*CharmInfo
	for _, cdoc := range cdocs {
		infos = append(infos, &CharmInfo{
			cdoc.Revision,
			cdoc.Digest,
			cdoc.Sha256,
			cdoc.Size,
			cdoc.FileId,
			cdoc.Meta,
			cdoc.Config,
		})
	}
	return infos, nil
}
Esempio n. 10
0
func (s *LocalRepoSuite) checkNotFoundErr(c *C, err error, charmURL *charm.URL) {
	expect := `charm not found in "` + s.repo.Path + `": ` + charmURL.String()
	c.Check(err, ErrorMatches, expect)
}
Esempio n. 11
0
// Run connects to the specified environment and starts the charm
// upgrade process.
func (c *UpgradeCharmCommand) Run(ctx *cmd.Context) error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()
	service, err := conn.State.Service(c.ServiceName)
	if err != nil {
		return err
	}
	oldURL, _ := service.CharmURL()
	var newURL *charm.URL
	if c.SwitchURL != "" {
		// A new charm URL was explicitly specified.
		conf, err := conn.State.EnvironConfig()
		if err != nil {
			return err
		}
		newURL, err = charm.InferURL(c.SwitchURL, conf.DefaultSeries())
		if err != nil {
			return err
		}
	} else {
		// No new URL specified, but revision might have been.
		newURL = oldURL.WithRevision(c.Revision)
	}
	repo, err := charm.InferRepository(newURL, ctx.AbsPath(c.RepoPath))
	if err != nil {
		return err
	}
	// If no explicit revision was set with either SwitchURL
	// or Revision flags, discover the latest.
	explicitRevision := true
	if newURL.Revision == -1 {
		explicitRevision = false
		latest, err := repo.Latest(newURL)
		if err != nil {
			return err
		}
		newURL = newURL.WithRevision(latest)
	}
	bumpRevision := false
	if *newURL == *oldURL {
		if explicitRevision {
			return fmt.Errorf("already running specified charm %q", newURL)
		}
		// Only try bumping the revision when necessary (local dir charm).
		if _, isLocal := repo.(*charm.LocalRepository); !isLocal {
			// TODO(dimitern): If the --force flag is set to something
			// different to before, we might actually want to allow this
			// case (and the other error below). LP bug #1174287
			return fmt.Errorf("already running latest charm %q", newURL)
		}
		// This is a local repository.
		if ch, err := repo.Get(newURL); err != nil {
			return err
		} else if _, bumpRevision = ch.(*charm.Dir); !bumpRevision {
			// Only bump the revision when it's a directory.
			return fmt.Errorf("cannot increment revision of charm %q: not a directory", newURL)
		}
	}
	sch, err := conn.PutCharm(newURL, repo, bumpRevision)
	if err != nil {
		return err
	}
	return service.SetCharm(sch, c.Force)
}
Esempio n. 12
0
// WriteCharmURL writes a charm identity file into the directory.
func WriteCharmURL(d *GitDir, url *charm.URL) error {
	return utils.WriteYaml(filepath.Join(d.path, ".juju-charm"), url.String())
}