func (s *serviceSuite) TestClientServiceSetCharmUnsupportedSeriesForce(c *gc.C) { curl, _ := s.UploadCharmMultiSeries(c, "~who/multi-series", "multi-series") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", Series: "precise", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharmMultiSeries(c, "~who/multi-series2", "multi-series2") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), ForceSeries: true, }) c.Assert(err, jc.ErrorIsNil) svc, err := s.State.Service("service") c.Assert(err, jc.ErrorIsNil) ch, _, err := svc.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(ch.URL().String(), gc.Equals, "cs:~who/multi-series2-0") }
func (s *serviceSuite) TestClientServiceSetCharmLegacy(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharm(c, "trusty/dummy-1", "dummy") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) // Even with forceSeries = true, we can't change a charm where // the series is sepcified in the URL. err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), ForceSeries: true, }) c.Assert(err, gc.ErrorMatches, "cannot change a service's series") }
func (s *serviceSuite) TestClientServiceSetCharmForceUnits(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", NumUnits: 3, }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharm(c, "precise/wordpress-3", "wordpress") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), ForceUnits: true, }) c.Assert(err, jc.ErrorIsNil) // Ensure that the charm is marked as forced. service, err := s.State.Service("service") c.Assert(err, jc.ErrorIsNil) charm, force, err := service.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(charm.URL().String(), gc.Equals, curl.String()) c.Assert(force, jc.IsTrue) }
func (s *serviceSuite) TestClientServiceDeploySubordinate(c *gc.C) { curl, ch := s.UploadCharm(c, "utopic/logging-47", "logging") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service-name", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) service, err := s.State.Service("service-name") c.Assert(err, jc.ErrorIsNil) charm, force, err := service.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(force, jc.IsFalse) c.Assert(charm.URL(), gc.DeepEquals, curl) c.Assert(charm.Meta(), gc.DeepEquals, ch.Meta()) c.Assert(charm.Config(), gc.DeepEquals, ch.Config()) units, err := service.AllUnits() c.Assert(err, jc.ErrorIsNil) c.Assert(units, gc.HasLen, 0) }
func (s *serviceSuite) setupServiceDeploy(c *gc.C, args string) (*charm.URL, charm.Charm, constraints.Value) { curl, ch := s.UploadCharm(c, "precise/dummy-42", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) cons := constraints.MustParse(args) return curl, ch, cons }
func (s *serviceSuite) setupServiceUpdate(c *gc.C) string { s.deployServiceForUpdateTests(c) curl, _ := s.UploadCharm(c, "precise/wordpress-3", "wordpress") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) return curl.String() }
func (s *serviceSuite) TestClientServiceDeployWithPlacement(c *gc.C) { curl, ch := s.UploadCharm(c, "precise/dummy-42", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) var cons constraints.Value args := params.ServiceDeploy{ ServiceName: "service", CharmUrl: curl.String(), NumUnits: 1, Constraints: cons, Placement: []*instance.Placement{ {"deadbeef-0bad-400d-8000-4b1d0d06f00d", "valid"}, }, ToMachineSpec: "will be ignored", } results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{args}}, ) c.Assert(err, jc.ErrorIsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{{Error: nil}}, }) svc := apiservertesting.AssertPrincipalServiceDeployed(c, s.State, "service", curl, false, ch, cons) units, err := svc.AllUnits() c.Assert(err, jc.ErrorIsNil) c.Assert(units, gc.HasLen, 1) }
func (s *serviceSuite) setupServiceSetCharm(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", NumUnits: 3, }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharm(c, "precise/wordpress-3", "wordpress") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) }
func (s *serviceSuite) TestClientServiceSetCharmUnsupportedSeries(c *gc.C) { curl, _ := s.UploadCharmMultiSeries(c, "~who/multi-series", "multi-series") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", Series: "precise", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharmMultiSeries(c, "~who/multi-series", "multi-series2") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), }) c.Assert(err, gc.ErrorMatches, "cannot upgrade charm, only these series are supported: trusty, wily") }
func (s *serviceSuite) TestClientServiceAddCharmErrors(c *gc.C) { for url, expect := range map[string]string{ "wordpress": "charm URL must include revision", "cs:wordpress": "charm URL must include revision", "cs:precise/wordpress": "charm URL must include revision", "cs:precise/wordpress-999999": `cannot retrieve "cs:precise/wordpress-999999": charm not found`, } { c.Logf("test %s", url) err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{ URL: url, }) c.Check(err, gc.ErrorMatches, expect) } }
func (s *serviceSuite) TestClientServiceSetCharmWrongOS(c *gc.C) { curl, _ := s.UploadCharmMultiSeries(c, "~who/multi-series", "multi-series") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", Series: "precise", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) curl, _ = s.UploadCharmMultiSeries(c, "~who/multi-series-windows", "multi-series-windows") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), ForceSeries: true, }) c.Assert(err, gc.ErrorMatches, `cannot upgrade charm, OS "Ubuntu" not supported by charm`) }
func (s *serviceSuite) TestClientServiceUpdateAllParams(c *gc.C) { s.deployServiceForUpdateTests(c) curl, _ := s.UploadCharm(c, "precise/wordpress-3", "wordpress") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) // Update all the service attributes. minUnits := 3 cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, jc.ErrorIsNil) args := params.ServiceUpdate{ ServiceName: "service", CharmUrl: curl.String(), ForceCharmUrl: true, MinUnits: &minUnits, SettingsStrings: map[string]string{"blog-title": "string-title"}, SettingsYAML: "service:\n blog-title: yaml-title\n", Constraints: &cons, } err = s.serviceApi.ServiceUpdate(args) c.Assert(err, jc.ErrorIsNil) // Ensure the service has been correctly updated. service, err := s.State.Service("service") c.Assert(err, jc.ErrorIsNil) // Check the charm. ch, force, err := service.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(ch.URL().String(), gc.Equals, curl.String()) c.Assert(force, jc.IsTrue) // Check the minimum number of units. c.Assert(service.MinUnits(), gc.Equals, minUnits) // Check the settings: also ensure the YAML settings take precedence // over strings ones. expectedSettings := charm.Settings{"blog-title": "yaml-title"} obtainedSettings, err := service.ConfigSettings() c.Assert(err, jc.ErrorIsNil) c.Assert(obtainedSettings, gc.DeepEquals, expectedSettings) // Check the constraints. obtainedConstraints, err := service.Constraints() c.Assert(err, jc.ErrorIsNil) c.Assert(obtainedConstraints, gc.DeepEquals, cons) }
func (s *serviceSuite) TestClientServiceDeployConfigError(c *gc.C) { // TODO(fwereade): test Config/ConfigYAML handling directly on srvClient. // Can't be done cleanly until it's extracted similarly to Machiner. curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service-name", NumUnits: 1, ConfigYAML: "service-name:\n skill-level: fred", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.ErrorMatches, `option "skill-level" expected int, got "fred"`) _, err = s.State.Service("service-name") c.Assert(err, jc.Satisfies, errors.IsNotFound) }
func (s *serviceSuite) TestClientServiceDeployServiceOwner(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", NumUnits: 3, }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) service, err := s.State.Service("service") c.Assert(err, jc.ErrorIsNil) c.Assert(service.GetOwnerTag(), gc.Equals, s.authorizer.GetAuthTag().String()) }
func (s *serviceSuite) TestClientServiceDeployConfig(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service-name", NumUnits: 1, ConfigYAML: "service-name:\n username: fred", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) service, err := s.State.Service("service-name") c.Assert(err, jc.ErrorIsNil) settings, err := service.ConfigSettings() c.Assert(err, jc.ErrorIsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"username": "******"}) }
func (s *serviceSuite) TestClientServiceDeployWithInvalidPlacement(c *gc.C) { curl, _ := s.UploadCharm(c, "precise/dummy-42", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) var cons constraints.Value args := params.ServiceDeploy{ ServiceName: "service", CharmUrl: curl.String(), NumUnits: 1, Constraints: cons, Placement: []*instance.Placement{ {"deadbeef-0bad-400d-8000-4b1d0d06f00d", "invalid"}, }, } results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{args}}, ) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error.Error(), gc.Matches, ".* invalid placement is invalid") }
func (s *serviceSuite) TestClientSpecializeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) { repo := &testModeCharmRepo{} s.PatchValue(&service.NewCharmStore, func(p charmrepo.NewCharmStoreParams) charmrepo.Interface { p.URL = s.Srv.URL repo.CharmStore = charmrepo.NewCharmStore(p) return repo }) attrs := map[string]interface{}{"test-mode": true} err := s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, jc.ErrorIsNil) // Check that the store's test mode is enabled when calling ServiceDeploy. curl, _ := s.UploadCharm(c, "trusty/dummy-1", "dummy") err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service", NumUnits: 3, }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) c.Assert(repo.testMode, jc.IsTrue) // Check that the store's test mode is enabled when calling ServiceSetCharm. curl, _ = s.UploadCharm(c, "trusty/wordpress-2", "wordpress") err = s.serviceApi.ServiceSetCharm(params.ServiceSetCharm{ ServiceName: "service", CharmUrl: curl.String(), }) c.Assert(repo.testMode, jc.IsTrue) // Check that the store's test mode is enabled when calling AddCharm. curl, _ = s.UploadCharm(c, "utopic/riak-42", "riak") err = s.APIState.Client().AddCharm(curl) c.Assert(err, jc.ErrorIsNil) c.Assert(repo.testMode, jc.IsTrue) }
func (s *serviceSuite) TestClientServiceDeployToMachine(c *gc.C) { curl, ch := s.UploadCharm(c, "precise/dummy-0", "dummy") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, jc.ErrorIsNil) results, err := s.serviceApi.ServicesDeploy(params.ServicesDeploy{ Services: []params.ServiceDeploy{{ CharmUrl: curl.String(), ServiceName: "service-name", NumUnits: 1, ConfigYAML: "service-name:\n username: fred", }}}) c.Assert(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) service, err := s.State.Service("service-name") c.Assert(err, jc.ErrorIsNil) charm, force, err := service.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(force, jc.IsFalse) c.Assert(charm.URL(), gc.DeepEquals, curl) c.Assert(charm.Meta(), gc.DeepEquals, ch.Meta()) c.Assert(charm.Config(), gc.DeepEquals, ch.Config()) errs, err := s.APIState.UnitAssigner().AssignUnits([]names.UnitTag{names.NewUnitTag("service-name/0")}) c.Assert(errs, gc.DeepEquals, []error{nil}) c.Assert(err, jc.ErrorIsNil) units, err := service.AllUnits() c.Assert(err, jc.ErrorIsNil) c.Assert(units, gc.HasLen, 1) mid, err := units[0].AssignedMachineId() c.Assert(err, jc.ErrorIsNil) c.Assert(mid, gc.Equals, machine.Id()) }
func (s *serviceSuite) TestAddCharmWithAuthorization(c *gc.C) { // Upload a new charm to the charm store. curl, _ := s.UploadCharm(c, "cs:~restricted/precise/wordpress-3", "wordpress") // Change permissions on the new charm such that only bob // can read from it. s.DischargeUser = "******" err := s.Srv.NewClient().Put("/"+curl.Path()+"/meta/perm/read", []string{"bob"}) c.Assert(err, jc.ErrorIsNil) // Try to add a charm to the environment without authorization. s.DischargeUser = "" err = s.APIState.Client().AddCharm(curl) c.Assert(err, gc.ErrorMatches, `cannot retrieve charm "cs:~restricted/precise/wordpress-3": cannot get archive: cannot get discharge from ".*": third party refused discharge: cannot discharge: discharge denied`) tryAs := func(user string) error { client := csclient.New(csclient.Params{ URL: s.Srv.URL(), }) s.DischargeUser = user var m *macaroon.Macaroon err = client.Get("/delegatable-macaroon", &m) c.Assert(err, gc.IsNil) return service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) } // Try again with authorization for the wrong user. err = tryAs("joe") c.Assert(err, gc.ErrorMatches, `cannot retrieve charm "cs:~restricted/precise/wordpress-3": cannot get archive: unauthorized: access denied for user "joe"`) // Try again with the correct authorization this time. err = tryAs("bob") c.Assert(err, gc.IsNil) // Verify that it has actually been uploaded. _, err = s.State.Charm(curl) c.Assert(err, gc.IsNil) }
func (s *serviceSuite) checkClientServiceUpdateSetCharm(c *gc.C, forceCharmUrl bool) { s.deployServiceForUpdateTests(c) curl, _ := s.UploadCharm(c, "precise/wordpress-3", "wordpress") err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()}) c.Assert(err, jc.ErrorIsNil) // Update the charm for the service. args := params.ServiceUpdate{ ServiceName: "service", CharmUrl: curl.String(), ForceCharmUrl: forceCharmUrl, } err = s.serviceApi.ServiceUpdate(args) c.Assert(err, jc.ErrorIsNil) // Ensure the charm has been updated and and the force flag correctly set. service, err := s.State.Service("service") c.Assert(err, jc.ErrorIsNil) ch, force, err := service.Charm() c.Assert(err, jc.ErrorIsNil) c.Assert(ch.URL().String(), gc.Equals, curl.String()) c.Assert(force, gc.Equals, forceCharmUrl) }
// AddCharmWithAuthorization adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). // // The authorization macaroon, args.CharmStoreMacaroon, may be // omitted, in which case this call is equivalent to AddCharm. func (c *Client) AddCharmWithAuthorization(args params.AddCharmWithAuthorization) error { return service.AddCharmWithAuthorization(c.api.state, args) }
func (c *Client) AddCharm(args params.CharmURL) error { return service.AddCharmWithAuthorization(c.api.state, params.AddCharmWithAuthorization{ URL: args.URL, }) }