func (s *MockStore) serveInfo(w http.ResponseWriter, r *http.Request) { if metadata := r.Header.Get("Juju-Metadata"); metadata != "" { s.Metadata = append(s.Metadata, metadata) logger.Infof("Juju metadata: " + metadata) } r.ParseForm() if r.Form.Get("stats") == "0" { s.InfoRequestCountNoStats += 1 } else { s.InfoRequestCount += 1 } response := map[string]*charm.InfoResponse{} for _, url := range r.Form["charms"] { cr := &charm.InfoResponse{} response[url] = cr charmURL, err := charm.ParseURL(url) if err == charm.ErrUnresolvedUrl { ref, _, err := charm.ParseReference(url) if err != nil { panic(err) } if s.DefaultSeries == "" { panic(fmt.Errorf("mock store lacks a default series cannot resolve charm URL: %q", url)) } charmURL = &charm.URL{Reference: ref, Series: s.DefaultSeries} } switch charmURL.Name { case "borken": cr.Errors = append(cr.Errors, "badness") case "terracotta": cr.Errors = append(cr.Errors, "cannot get revision") case "unwise": cr.Warnings = append(cr.Warnings, "foolishness") fallthrough default: if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok { if charmURL.Revision == -1 { cr.Revision = rev } else { cr.Revision = charmURL.Revision } cr.Sha256 = s.bundleSha256 cr.CanonicalURL = charmURL.String() } else { cr.Errors = append(cr.Errors, "entry not found") } } } data, err := json.Marshal(response) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) if err != nil { panic(err) } }
func (s *StoreSuite) TestSeriesSolver(c *gc.C) { for _, t := range seriesSolverCharms { url := charm.MustParseURL(fmt.Sprintf("cs:%s/%s", t.series, t.name)) urls := []*charm.URL{url} pub, err := s.store.CharmPublisher(urls, fmt.Sprintf("some-%s-%s-digest", t.series, t.name)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } // LTS, then non-LTS, reverse alphabetical order ref, _, err := charm.ParseReference("cs:wordpress") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 5) c.Check(series[0], gc.Equals, "trusty") c.Check(series[1], gc.Equals, "precise") c.Check(series[2], gc.Equals, "volumetric") c.Check(series[3], gc.Equals, "quantal") c.Check(series[4], gc.Equals, "oneiric") // Ensure that the full charm name matches, not just prefix ref, _, err = charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 1) c.Check(series[0], gc.Equals, "precise") // No LTS, reverse alphabetical order ref, _, err = charm.ParseReference("cs:zebra") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "zef") c.Check(series[1], gc.Equals, "def") }
// resolveCharmURL returns a resolved charm URL, given a charm location string. // If the series is not resolved, the environment default-series is used, or if // not set, the series is resolved with the state server. func resolveCharmURL(client *api.Client, url string, defaultSeries string) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } // If series is not set, use configured default series if series == "" { series = defaultSeries } // Otherwise, look up the best supported series for this charm if series == "" { return client.ResolveCharm(ref) } return &charm.URL{Reference: ref, Series: series}, nil }
func (s *URLSuite) TestParseUnresolved(c *gc.C) { for _, t := range inferNoDefaultSeriesTests { if t.resolved { url, err := charm.ParseURL(t.vague) c.Assert(err, gc.IsNil) c.Assert(url.Series, gc.Not(gc.Equals), "") } else { _, series, err := charm.ParseReference(t.vague) c.Assert(err, gc.IsNil) c.Assert(series, gc.Equals, "") _, err = charm.ParseURL(t.vague) c.Assert(err, gc.NotNil) c.Assert(err, gc.Equals, charm.ErrUnresolvedUrl) } } }
func (s *Server) resolveURL(url string) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } if series == "" { prefSeries, err := s.store.Series(ref) if err != nil { return nil, err } if len(prefSeries) == 0 { return nil, ErrNotFound } return &charm.URL{Reference: ref, Series: prefSeries[0]}, nil } return &charm.URL{Reference: ref, Series: series}, nil }
func (s *URLSuite) TestReferenceJSON(c *gc.C) { ref, _, err := charm.ParseReference("cs:series/name") c.Assert(err, gc.IsNil) data, err := json.Marshal(&ref) c.Assert(err, gc.IsNil) c.Check(string(data), gc.Equals, `"cs:name"`) var parsed charm.Reference err = json.Unmarshal(data, &parsed) c.Assert(err, gc.IsNil) c.Check(parsed, gc.DeepEquals, ref) // unmarshalling json gibberish and invalid charm reference strings for _, value := range []string{":{", `"cs:{}+<"`, `"cs:~_~/f00^^&^/baaaar$%-?"`} { err = json.Unmarshal([]byte(value), &parsed) c.Check(err, gc.NotNil) } }
// resolveCharmURL returns a resolved charm URL, given a charm location string. // If the series is not resolved, the environment default-series is used, or if // not set, the series is resolved with the state server. func resolveCharmURL(url string, client *api.Client, conf *config.Config) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } // If series is not set, use configured default series if series == "" { if defaultSeries, ok := conf.DefaultSeries(); ok { series = defaultSeries } } // Otherwise, look up the best supported series for this charm if series == "" { if ref.Schema == "local" { possibleUrl := &charm.URL{Reference: ref, Series: "precise"} logger.Errorf(`The series is not specified in the environment (default-series) or with the charm. Did you mean: %s`, possibleUrl.String()) return nil, fmt.Errorf("cannot resolve series for charm: %q", ref) } return client.ResolveCharm(ref) } return &charm.URL{Reference: ref, Series: series}, nil }
func (s *StoreSuite) TestMysqlSeriesSolver(c *gc.C) { for _, t := range mysqlSeriesCharms { var urls []*charm.URL for _, url := range t.urls { urls = append(urls, charm.MustParseURL(url)) } pub, err := s.store.CharmPublisher(urls, t.fakeDigest) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } ref, _, err := charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "precise") c.Check(series[1], gc.Equals, "oneiric") }
func (s *URLSuite) TestParseURL(c *gc.C) { for i, t := range urlTests { c.Logf("test %d", i) url, uerr := charm.ParseURL(t.s) ref, series, rerr := charm.ParseReference(t.s) comment := gc.Commentf("ParseURL(%q)", t.s) if t.url != nil && t.url.Series == "" { if t.err != "" { // Expected error should match c.Assert(rerr, gc.NotNil, comment) c.Check(rerr.Error(), gc.Matches, t.err, comment) } else { // Expected charm reference should match c.Check(ref, gc.DeepEquals, t.url.Reference, comment) c.Check(t.url.Reference.String(), gc.Equals, t.s) } if rerr != nil { // If ParseReference has an error, ParseURL should share it c.Check(uerr.Error(), gc.Equals, rerr.Error(), comment) } else { // Otherwise, ParseURL with an empty series should error unresolved. c.Check(uerr.Error(), gc.Equals, charm.ErrUnresolvedUrl.Error(), comment) } } else { if t.err != "" { c.Assert(uerr, gc.NotNil, comment) c.Check(uerr.Error(), gc.Matches, t.err, comment) c.Check(uerr.Error(), gc.Equals, rerr.Error(), comment) } else { c.Check(url.Series, gc.Equals, series, comment) c.Check(url, gc.DeepEquals, t.url, comment) c.Check(t.url.String(), gc.Equals, t.s) } } } }