func (s *Server) serveCharm(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.URL.Path, "/charm/") { panic("serveCharm: bad url") } curl, err := charm.ParseURL("cs:" + r.URL.Path[len("/charm/"):]) if err != nil { w.WriteHeader(http.StatusNotFound) return } info, rc, err := s.store.OpenCharm(curl) if err == ErrNotFound { w.WriteHeader(http.StatusNotFound) return } if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("store: cannot open charm %q: %v", curl, err) return } if statsEnabled(r) { go s.store.IncCounter(charmStatsKey(curl, "charm-bundle")) } defer rc.Close() w.Header().Set("Connection", "close") // No keep-alive for now. w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.FormatInt(info.BundleSize(), 10)) _, err = io.Copy(w, rc) if err != nil { log.Printf("store: failed to stream charm %q: %v", curl, err) } }
// ServiceSetCharm sets the charm for a given service. func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error { service, err := c.api.state.Service(args.ServiceName) if err != nil { return err } curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return err } if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } conn, err := juju.NewConnFromState(c.api.state) if err != nil { return err } ch, err := conn.PutCharm(curl, CharmStore, false) if err != nil { return err } return service.SetCharm(ch, args.Force) }
// ReadCharmURL reads the charm identity file from the supplied GitDir. func ReadCharmURL(d *GitDir) (*charm.URL, error) { path := filepath.Join(d.path, ".juju-charm") surl := "" if err := utils.ReadYaml(path, &surl); err != nil { return nil, err } return charm.ParseURL(surl) }
func (s *URLSuite) TestParseURL(c *C) { for _, t := range urlTests { url, err := charm.ParseURL(t.s) comment := Commentf("ParseURL(%q)", t.s) if t.err != "" { c.Check(err.Error(), Matches, t.err, comment) } else { c.Check(url, DeepEquals, t.url, comment) c.Check(t.url.String(), Equals, t.s) } } }
func (s *URLSuite) TestInferURLNoDefaultSeries(c *C) { for _, t := range inferNoDefaultSeriesTests { inferred, err := charm.InferURL(t.vague, "") if t.exact == "" { c.Assert(err, ErrorMatches, fmt.Sprintf("cannot infer charm URL for %q: no series provided", t.vague)) } else { parsed, err := charm.ParseURL(t.exact) c.Assert(err, IsNil) c.Assert(inferred, DeepEquals, parsed, Commentf(`InferURL(%q, "")`, t.vague)) } } }
func (s *UniterSuite) TestSubordinateDying(c *C) { // Create a test context for later use. ctx := &context{ st: s.State, path: filepath.Join(s.dataDir, "agents", "unit-u-0"), dataDir: s.dataDir, charms: coretesting.ResponseMap{}, } defer os.RemoveAll(ctx.path) // Create the subordinate service. dir := coretesting.Charms.ClonedDir(c.MkDir(), "series", "logging") curl, err := charm.ParseURL("cs:series/logging") c.Assert(err, IsNil) curl = curl.WithRevision(dir.Revision()) step(c, ctx, addCharm{dir, curl}) ctx.svc, err = s.State.AddService("u", ctx.sch) c.Assert(err, IsNil) // Create the principal service and add a relation. wps, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress")) c.Assert(err, IsNil) wpu, err := wps.AddUnit() c.Assert(err, IsNil) eps, err := s.State.InferEndpoints([]string{"wordpress", "u"}) c.Assert(err, IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, IsNil) // Create the subordinate unit by entering scope as the principal. wpru, err := rel.Unit(wpu) c.Assert(err, IsNil) err = wpru.EnterScope(nil) c.Assert(err, IsNil) ctx.unit, err = s.State.Unit("u/0") c.Assert(err, IsNil) // Run the actual test. ctx.run(c, []stepper{ serveCharm{}, startUniter{}, waitAddresses{}, custom{func(c *C, ctx *context) { c.Assert(rel.Destroy(), IsNil) }}, waitUniterDead{}, }) }
func (s *Server) serveEvent(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/charm-event" { w.WriteHeader(http.StatusNotFound) return } r.ParseForm() response := map[string]*charm.EventResponse{} for _, url := range r.Form["charms"] { digest := "" if i := strings.Index(url, "@"); i >= 0 && i+1 < len(url) { digest = url[i+1:] url = url[:i] } c := &charm.EventResponse{} response[url] = c curl, err := charm.ParseURL(url) var event *CharmEvent if err == nil { event, err = s.store.CharmEvent(curl, digest) } var skey []string if err == nil { skey = charmStatsKey(curl, "charm-event") c.Kind = event.Kind.String() c.Revision = event.Revision c.Digest = event.Digest c.Errors = event.Errors c.Warnings = event.Warnings c.Time = event.Time.UTC().Format(time.RFC3339) } else { c.Errors = append(c.Errors, err.Error()) } if skey != nil && statsEnabled(r) { go s.store.IncCounter(skey) } } data, err := json.Marshal(response) if err == nil { w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) } if err != nil { log.Errorf("store: cannot write content: %v", err) w.WriteHeader(http.StatusInternalServerError) return } }
// CharmInfo returns information about the requested charm. func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { curl, err := charm.ParseURL(args.CharmURL) if err != nil { return api.CharmInfo{}, err } charm, err := c.api.state.Charm(curl) if err != nil { return api.CharmInfo{}, err } info := api.CharmInfo{ Revision: charm.Revision(), URL: curl.String(), Config: charm.Config(), Meta: charm.Meta(), } return info, nil }
// uniqueNameURLs returns the branch URL and the charm URL for the // provided Launchpad branch unique name. The unique name must be // in the form: // // ~<user>/charms/<series>/<charm name>/trunk // // For testing purposes, if name has a prefix preceding a string in // this format, the prefix is stripped out for computing the charm // URL, and the unique name is returned unchanged as the branch URL. func uniqueNameURLs(name string) (burl string, curl *charm.URL, err error) { u := strings.Split(name, "/") if len(u) > 5 { u = u[len(u)-5:] burl = name } else { burl = "lp:" + name } if len(u) < 5 || u[1] != "charms" || u[4] != "trunk" || len(u[0]) == 0 || u[0][0] != '~' { return "", nil, fmt.Errorf("unwanted branch name: %s", name) } curl, err = charm.ParseURL(fmt.Sprintf("cs:%s/%s/%s", u[0], u[2], u[3])) if err != nil { return "", nil, err } return burl, curl, nil }
func (s *URLSuite) TestInferURL(c *C) { for _, t := range inferTests { comment := Commentf("InferURL(%q, %q)", t.vague, "defseries") inferred, ierr := charm.InferURL(t.vague, "defseries") parsed, perr := charm.ParseURL(t.exact) if parsed != nil { c.Check(inferred, DeepEquals, parsed, comment) } else { expect := perr.Error() if t.vague != t.exact { expect = fmt.Sprintf("%s (URL inferred from %q)", expect, t.vague) } c.Check(ierr.Error(), Equals, expect, comment) } } u, err := charm.InferURL("~blah", "defseries") c.Assert(u, IsNil) c.Assert(err, ErrorMatches, "cannot infer charm URL with user but no schema: .*") }
func (s *Server) serveInfo(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/charm-info" { w.WriteHeader(http.StatusNotFound) return } r.ParseForm() response := map[string]*charm.InfoResponse{} for _, url := range r.Form["charms"] { c := &charm.InfoResponse{} response[url] = c curl, err := charm.ParseURL(url) var info *CharmInfo if err == nil { info, err = s.store.CharmInfo(curl) } var skey []string if err == nil { skey = charmStatsKey(curl, "charm-info") c.Sha256 = info.BundleSha256() c.Revision = info.Revision() c.Digest = info.Digest() } else { if err == ErrNotFound { skey = charmStatsKey(curl, "charm-missing") } c.Errors = append(c.Errors, err.Error()) } if skey != nil && statsEnabled(r) { go s.store.IncCounter(skey) } } data, err := json.Marshal(response) if err == nil { w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) } if err != nil { log.Printf("store: cannot write content: %v", err) w.WriteHeader(http.StatusInternalServerError) return } }
// serviceSetCharm sets the charm for the given service. func serviceSetCharm(state *state.State, service *state.Service, url string, force bool) error { curl, err := charm.ParseURL(url) if err != nil { return err } if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } conn, err := juju.NewConnFromState(state) if err != nil { return err } ch, err := conn.PutCharm(curl, CharmStore, false) if err != nil { return err } return service.SetCharm(ch, force) }
// ServiceDeploy fetches the charm from the charm store and deploys it. Local // charms are not supported. func (c *Client) ServiceDeploy(args params.ServiceDeploy) error { curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return err } if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } conn, err := juju.NewConnFromState(c.api.state) if err != nil { return err } ch, err := conn.PutCharm(curl, CharmStore, false) if err != nil { return err } var settings charm.Settings if len(args.ConfigYAML) > 0 { settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName) } else if len(args.Config) > 0 { settings, err = ch.Config().ParseSettingsStrings(args.Config) } if err != nil { return err } _, err = conn.DeployService(juju.DeployServiceParams{ ServiceName: args.ServiceName, Charm: ch, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, ToMachineSpec: args.ToMachineSpec, }) return err }