func (s *DeployLocalSuite) TestDeployWithFewerPlacement(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, jc.ErrorIsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 3, Placement: []*instance.Placement{ {Scope: s.State.EnvironUUID(), Directive: "valid"}, }, }) c.Assert(err, jc.ErrorIsNil) s.assertConstraints(c, service, serviceCons) units, err := service.AllUnits() c.Assert(err, jc.ErrorIsNil) c.Assert(units, gc.HasLen, 3) // Check each of the newly added units. s.assertAssignedUnit(c, units[0], "0", constraints.MustParse("mem=2G cpu-cores=2")) s.assertAssignedUnit(c, units[1], "1", constraints.MustParse("mem=2G cpu-cores=2")) s.assertAssignedUnit(c, units[2], "2", constraints.MustParse("mem=2G cpu-cores=2")) }
func (s *DeployLocalSuite) TestDeployWithBoundRelationNamesAndExtraBindingsNames(c *gc.C) { wordpressCharm := s.addWordpressCharmWithExtraBindings(c) _, err := s.State.AddSpace("db", "", nil, false) c.Assert(err, jc.ErrorIsNil) _, err = s.State.AddSpace("public", "", nil, false) c.Assert(err, jc.ErrorIsNil) _, err = s.State.AddSpace("internal", "", nil, false) c.Assert(err, jc.ErrorIsNil) service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: wordpressCharm, EndpointBindings: map[string]string{ "": "public", "db": "db", "db-client": "db", "admin-api": "internal", }, }) c.Assert(err, jc.ErrorIsNil) s.assertBindings(c, service, map[string]string{ "url": "public", "logging-dir": "public", "monitoring-port": "public", "db": "db", "cache": "public", "db-client": "db", "admin-api": "internal", "cluster": "public", "foo-bar": "public", // like for relations, uses the service-default. }) }
func (s *DeployLocalSuite) TestDeployWithImplicitBindings(c *gc.C) { wordpressCharm := s.addWordpressCharmWithExtraBindings(c) service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: wordpressCharm, EndpointBindings: nil, }) c.Assert(err, jc.ErrorIsNil) s.assertBindings(c, service, map[string]string{ // relation names "url": "", "logging-dir": "", "monitoring-port": "", "db": "", "cache": "", "cluster": "", // extra-bindings names "db-client": "", "admin-api": "", "foo-bar": "", }) }
func (s *DeployLocalSuite) TestDeployWithSomeSpecifiedBindings(c *gc.C) { wordpressCharm := s.addWordpressCharm(c) _, err := s.State.AddSpace("db", "", nil, false) c.Assert(err, jc.ErrorIsNil) _, err = s.State.AddSpace("public", "", nil, false) c.Assert(err, jc.ErrorIsNil) service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: wordpressCharm, EndpointBindings: map[string]string{ "": "public", "db": "db", }, }) c.Assert(err, jc.ErrorIsNil) s.assertBindings(c, service, map[string]string{ "url": "public", "logging-dir": "public", "monitoring-port": "public", "db": "db", "cache": "public", }) }
func (s *DeployLocalSuite) TestDeploy(c *gc.C) { f := &fakeDeployer{State: s.State} serviceCons := constraints.MustParse("cpu-cores=2") placement := []*instance.Placement{ {Scope: s.State.ModelUUID(), Directive: "valid"}, {Scope: "#", Directive: "0"}, {Scope: "lxc", Directive: "1"}, {Scope: "lxc", Directive: ""}, } _, err := juju.DeployService(f, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 4, Placement: placement, }) c.Assert(err, jc.ErrorIsNil) c.Assert(f.args.Name, gc.Equals, "bob") c.Assert(f.args.Charm, gc.DeepEquals, s.charm) c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons) c.Assert(f.args.NumUnits, gc.Equals, 4) c.Assert(f.args.Placement, gc.DeepEquals, placement) }
func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") envCons := constraints.MustParse("mem=2G") err = s.State.SetEnvironConstraints(envCons) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 1, ToMachineSpec: fmt.Sprintf("%s:0", instance.LXC), }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) // The newly created container will use the constraints. id, err := units[0].AssignedMachineId() c.Assert(err, gc.IsNil) machine, err = s.State.Machine(id) c.Assert(err, gc.IsNil) machineCons, err := machine.Constraints() c.Assert(err, gc.IsNil) unitCons, err := units[0].Constraints() c.Assert(err, gc.IsNil) c.Assert(machineCons, gc.DeepEquals, *unitCons) }
func (s *DeployLocalSuite) TestDeployWithPlacement(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, jc.ErrorIsNil) c.Assert(machine.Id(), gc.Equals, "0") err = s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, jc.ErrorIsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 3, Placement: []*instance.Placement{ {Scope: s.State.EnvironUUID(), Directive: "valid"}, {Scope: "#", Directive: "0"}, {Scope: "lxc", Directive: "1"}, }, ToMachineSpec: "will be ignored", }) c.Assert(err, jc.ErrorIsNil) s.assertConstraints(c, service, serviceCons) units, err := service.AllUnits() c.Assert(err, jc.ErrorIsNil) c.Assert(units, gc.HasLen, 3) // Check each of the newly added units. s.assertAssignedUnit(c, units[0], "1", constraints.MustParse("mem=2G cpu-cores=2")) s.assertAssignedUnit(c, units[1], "0", constraints.Value{}) s.assertAssignedUnit(c, units[2], "1/lxc/0", constraints.MustParse("mem=2G cpu-cores=2")) }
// ServiceDeploy fetches the charm from the charm store and deploys it. // AddCharm or AddLocalCharm should be called to add the charm // before calling ServiceDeploy, although for backward compatibility // this is not necessary until 1.16 support is removed. func (c *Client) ServiceDeploy(args params.ServiceDeploy) error { curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return err } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } // Try to find the charm URL in state first. ch, err := c.api.state.Charm(curl) if errors.IsNotFound(err) { // Remove this whole if block when 1.16 compatibility is dropped. if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } err = c.AddCharm(params.CharmURL{args.CharmUrl}) if err != nil { return err } ch, err = c.api.state.Charm(curl) if err != nil { return err } } else 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 { // Parse config in a compatile way (see function comment). settings, err = parseSettingsCompatible(ch, args.Config) } if err != nil { return err } // Convert network tags to names for any given networks. requestedNetworks, err := networkTagsToNames(args.Networks) if err != nil { return err } _, err = juju.DeployService(c.api.state, juju.DeployServiceParams{ ServiceName: args.ServiceName, // TODO(dfc) ServiceOwner should be a tag ServiceOwner: c.api.auth.GetAuthTag().String(), Charm: ch, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, ToMachineSpec: args.ToMachineSpec, Networks: requestedNetworks, }) return err }
func (s *DeployLocalSuite) TestDeployOwnerTag(c *gc.C) { s.Factory.MakeUser(factory.UserParams{Username: "******"}) service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bobwithowner", Charm: s.charm, ServiceOwner: "user-foobar", }) c.Assert(err, gc.IsNil) c.Assert(service.GetOwnerTag(), gc.Equals, "user-foobar") }
func (s *DeployLocalSuite) TestDeployMinimal(c *gc.C) { service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, }) c.Assert(err, gc.IsNil) s.assertCharm(c, service, s.charm.URL()) s.assertSettings(c, service, charm.Settings{}) s.assertConstraints(c, service, constraints.Value{}) s.assertMachines(c, service, constraints.Value{}) c.Assert(service.GetOwnerTag(), gc.Equals, "user-admin") }
func (s *DeployLocalSuite) TestDeployWithForceMachineRejectsTooManyUnits(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") _, err = juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, NumUnits: 2, ToMachineSpec: "0", }) c.Assert(err, gc.ErrorMatches, "cannot use --num-units with --to") }
func (s *DeployLocalSuite) TestDeployConstraints(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) }
func (s *DeployLocalSuite) TestDeploySettingsError(c *gc.C) { _, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, ConfigSettings: charm.Settings{ "skill-level": 99.01, }, }) c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got 99.01`) _, err = s.State.Service("bob") c.Assert(err, jc.Satisfies, errors.IsNotFound) }
func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, jc.ErrorIsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 2, }) c.Assert(err, jc.ErrorIsNil) s.assertConstraints(c, service, serviceCons) s.assertMachines(c, service, constraints.MustParse("mem=2G cpu-cores=2"), "0", "1") }
func (s *DeployLocalSuite) TestDeploySeries(c *gc.C) { f := &fakeDeployer{State: s.State} _, err := juju.DeployService(f, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Series: "aseries", }) c.Assert(err, jc.ErrorIsNil) c.Assert(f.args.Name, gc.Equals, "bob") c.Assert(f.args.Charm, gc.DeepEquals, s.charm) c.Assert(f.args.Series, gc.Equals, "aseries") }
func (s *DeployLocalSuite) TestDeploySettings(c *gc.C) { service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, ConfigSettings: charm.Settings{ "title": "banana cupcakes", "skill-level": 9901, }, }) c.Assert(err, gc.IsNil) s.assertSettings(c, service, charm.Settings{ "title": "banana cupcakes", "skill-level": int64(9901), }) }
func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) { f := &fakeDeployer{State: s.State} serviceCons := constraints.MustParse("cpu-cores=2") _, err := juju.DeployService(f, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 2, }) c.Assert(err, jc.ErrorIsNil) c.Assert(f.args.Name, gc.Equals, "bob") c.Assert(f.args.Charm, gc.DeepEquals, s.charm) c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons) c.Assert(f.args.NumUnits, gc.Equals, 2) }
func (s *DeployLocalSuite) TestDeployResources(c *gc.C) { f := &fakeDeployer{State: s.State} _, err := juju.DeployService(f, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, EndpointBindings: map[string]string{ "": "public", "db": "db", }, Resources: map[string]string{"foo": "bar"}, }) c.Assert(err, jc.ErrorIsNil) c.Assert(f.args.Name, gc.Equals, "bob") c.Assert(f.args.Charm, gc.DeepEquals, s.charm) c.Assert(f.args.Resources, gc.DeepEquals, map[string]string{"foo": "bar"}) }
func (s *DeployLocalSuite) TestDeployForceMachineId(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") err = s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 1, ToMachineSpec: "0", }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) s.assertMachines(c, service, constraints.Value{}, "0") }
func (s *DeployLocalSuite) TestDeployWithImplicitBindings(c *gc.C) { wordpressCharm := s.addWordpressCharm(c) service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: wordpressCharm, EndpointBindings: nil, }) c.Assert(err, jc.ErrorIsNil) s.assertBindings(c, service, map[string]string{ "url": "", "logging-dir": "", "monitoring-port": "", "db": "", "cache": "", }) }
func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) { f := &fakeDeployer{State: s.State} serviceCons := constraints.MustParse("cpu-cores=2") _, err := juju.DeployService(f, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 1, Placement: []*instance.Placement{instance.MustParsePlacement(fmt.Sprintf("%s:0", instance.LXC))}, }) c.Assert(err, jc.ErrorIsNil) c.Assert(f.args.Name, gc.Equals, "bob") c.Assert(f.args.Charm, gc.DeepEquals, s.charm) c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons) c.Assert(f.args.NumUnits, gc.Equals, 1) c.Assert(f.args.Placement, gc.HasLen, 1) c.Assert(*f.args.Placement[0], gc.Equals, instance.Placement{Scope: string(instance.LXC), Directive: "0"}) }
// DeployService fetches the charm from the charm store and deploys it. // The logic has been factored out into a common function which is called by // both the legacy API on the client facade, as well as the new service facade. func deployService(st *state.State, owner string, args params.ServiceDeploy) error { curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return errors.Trace(err) } if curl.Revision < 0 { return errors.Errorf("charm url must include revision") } // Do a quick but not complete validation check before going any further. for _, p := range args.Placement { if p.Scope != instance.MachineScope { continue } _, err = st.Machine(p.Directive) if err != nil { return errors.Annotatef(err, `cannot deploy "%v" to machine %v`, args.ServiceName, p.Directive) } } // Try to find the charm URL in state first. ch, err := st.Charm(curl) if errors.IsNotFound(err) { // Clients written to expect 1.16 compatibility require this next block. if curl.Schema != "cs" { return errors.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if err = AddCharmWithAuthorization(st, params.AddCharmWithAuthorization{ URL: args.CharmUrl, }); err == nil { ch, err = st.Charm(curl) } } if err != nil { return errors.Trace(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 { // Parse config in a compatible way (see function comment). settings, err = parseSettingsCompatible(ch, args.Config) } if err != nil { return errors.Trace(err) } // Convert network tags to names for any given networks. requestedNetworks, err := networkTagsToNames(args.Networks) if err != nil { return errors.Trace(err) } _, err = jjj.DeployService(st, jjj.DeployServiceParams{ ServiceName: args.ServiceName, Series: args.Series, // TODO(dfc) ServiceOwner should be a tag ServiceOwner: owner, Charm: ch, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, Placement: args.Placement, Networks: requestedNetworks, Storage: args.Storage, }) return errors.Trace(err) }