// DeployService takes a charm and various parameters and deploys it. func DeployService(st ServiceDeployer, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && len(args.Placement) == 0 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { env, err := st.Environment() if err != nil { return nil, errors.Trace(err) } args.ServiceOwner = env.Owner().String() } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { return nil, fmt.Errorf("use of --networks is deprecated. Please use spaces") } if len(args.Placement) == 0 { args.Placement, err = makePlacement(args.ToMachineSpec) if err != nil { return nil, errors.Trace(err) } } asa := state.AddServiceArgs{ Name: args.ServiceName, Series: args.Series, Owner: args.ServiceOwner, Charm: args.Charm, Networks: args.Networks, Storage: stateStorageConstraints(args.Storage), Settings: settings, NumUnits: args.NumUnits, Placement: args.Placement, } if !args.Charm.Meta().Subordinate { asa.Constraints = args.Constraints } // TODO(dimitern): In a follow-up drop Networks and use spaces // constraints for this when possible. return st.AddService(asa) }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st ServiceDeployer, args DeployServiceParams) (*state.Service, error) { settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { env, err := st.Model() if err != nil { return nil, errors.Trace(err) } args.ServiceOwner = env.Owner().String() } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { return nil, fmt.Errorf("use of --networks is deprecated. Please use spaces") } effectiveBindings, err := getEffectiveBindingsForCharmMeta(args.Charm.Meta(), args.EndpointBindings) if err != nil { return nil, errors.Annotate(err, "cannot determine effective service endpoint bindings") } asa := state.AddServiceArgs{ Name: args.ServiceName, Series: args.Series, Owner: args.ServiceOwner, Charm: args.Charm, Networks: args.Networks, Storage: stateStorageConstraints(args.Storage), Settings: settings, NumUnits: args.NumUnits, Placement: args.Placement, Resources: args.Resources, EndpointBindings: effectiveBindings, } if !args.Charm.Meta().Subordinate { asa.Constraints = args.Constraints } // TODO(dimitern): In a follow-up drop Networks and use spaces // constraints for this when possible. return st.AddService(asa) }
func (s *CommonProvisionerSuite) checkStartInstanceCustom(c *gc.C, m *state.Machine, secret string, cons constraints.Value, networks []string, networkInfo []network.Info, waitInstanceId bool) (inst instance.Instance) { s.BackingState.StartSync() for { select { case o := <-s.op: switch o := o.(type) { case dummy.OpStartInstance: inst = o.Instance if waitInstanceId { s.waitInstanceId(c, m, inst.Id()) } // Check the instance was started with the expected params. c.Assert(o.MachineId, gc.Equals, m.Id()) nonceParts := strings.SplitN(o.MachineNonce, ":", 2) c.Assert(nonceParts, gc.HasLen, 2) c.Assert(nonceParts[0], gc.Equals, names.NewMachineTag("0").String()) c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString) c.Assert(o.Secret, gc.Equals, secret) c.Assert(o.Networks, jc.DeepEquals, networks) c.Assert(o.NetworkInfo, jc.DeepEquals, networkInfo) // All provisioned machines in this test suite have // their hardware characteristics attributes set to // the same values as the constraints due to the dummy // environment being used. if !constraints.IsEmpty(&cons) { c.Assert(o.Constraints, gc.DeepEquals, cons) hc, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{ Arch: cons.Arch, Mem: cons.Mem, RootDisk: cons.RootDisk, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, Tags: cons.Tags, }) } return default: c.Logf("ignoring unexpected operation %#v", o) } case <-time.After(2 * time.Second): c.Fatalf("provisioner did not start an instance") return } } return }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st ServiceDeployer, args DeployServiceParams) (*state.Service, error) { settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { env, err := st.Model() if err != nil { return nil, errors.Trace(err) } args.ServiceOwner = env.Owner().String() } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). effectiveBindings := getEffectiveBindingsForCharmMeta(args.Charm.Meta(), args.EndpointBindings) asa := state.AddServiceArgs{ Name: args.ServiceName, Series: args.Series, Owner: args.ServiceOwner, Charm: args.Charm, Channel: args.Channel, Storage: stateStorageConstraints(args.Storage), Settings: settings, NumUnits: args.NumUnits, Placement: args.Placement, Resources: args.Resources, EndpointBindings: effectiveBindings, } if !args.Charm.Meta().Subordinate { asa.Constraints = args.Constraints } return st.AddService(asa) }
// DeployApplication takes a charm and various parameters and deploys it. func DeployApplication(st ApplicationDeployer, args DeployApplicationParams) (*state.Application, error) { settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 { return nil, fmt.Errorf("subordinate application must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate application must be deployed without constraints") } } // TODO(fwereade): transactional State.AddApplication including settings, constraints // (minimumUnitCount, initialMachineIds?). effectiveBindings := getEffectiveBindingsForCharmMeta(args.Charm.Meta(), args.EndpointBindings) asa := state.AddApplicationArgs{ Name: args.ApplicationName, Series: args.Series, Charm: args.Charm, Channel: args.Channel, Storage: stateStorageConstraints(args.Storage), Settings: settings, NumUnits: args.NumUnits, Placement: args.Placement, Resources: args.Resources, EndpointBindings: effectiveBindings, } if !args.Charm.Meta().Subordinate { asa.Constraints = args.Constraints } return st.AddApplication(asa) }
// EnableHASingle applies a single ControllersServersSpec specification to the current environment. // Exported so it can be called by the legacy client API in the client package. func EnableHASingle(st *state.State, spec params.ControllersSpec) (params.ControllersChanges, error) { if !st.IsController() { return params.ControllersChanges{}, errors.New("unsupported with hosted models") } // Check if changes are allowed and the command may proceed. blockChecker := common.NewBlockChecker(st) if err := blockChecker.ChangeAllowed(); err != nil { return params.ControllersChanges{}, errors.Trace(err) } // Validate the environment tag if present. if spec.ModelTag != "" { tag, err := names.ParseModelTag(spec.ModelTag) if err != nil { return params.ControllersChanges{}, errors.Errorf("invalid model tag: %v", err) } if _, err := st.FindEntity(tag); err != nil { return params.ControllersChanges{}, err } } series := spec.Series if series == "" { ssi, err := st.ControllerInfo() if err != nil { return params.ControllersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.ControllersChanges{}, errors.Errorf("internal error, failed to find any voting machines") } templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.ControllersChanges{}, err } series = templateMachine.Series() } if constraints.IsEmpty(&spec.Constraints) { // No constraints specified, so we'll use the constraints off // a running controller. controllerInfo, err := st.ControllerInfo() if err != nil { return params.ControllersChanges{}, err } // We'll sort the controller ids to find the smallest. // This will typically give the initial bootstrap machine. var controllerIds []int for _, id := range controllerInfo.MachineIds { idNum, err := strconv.Atoi(id) if err != nil { logger.Warningf("ignoring non numeric controller id %v", id) continue } controllerIds = append(controllerIds, idNum) } if len(controllerIds) == 0 { errors.Errorf("internal error, failed to find any controllers") } sort.Ints(controllerIds) // Load the controller machine and get its constraints. controllerId := controllerIds[0] controller, err := st.Machine(strconv.Itoa(controllerId)) if err != nil { return params.ControllersChanges{}, errors.Annotatef(err, "reading controller id %v", controllerId) } spec.Constraints, err = controller.Constraints() if err != nil { return params.ControllersChanges{}, errors.Annotatef(err, "reading constraints for controller id %v", controllerId) } } changes, err := st.EnableHA(spec.NumControllers, spec.Constraints, series, spec.Placement) if err != nil { return params.ControllersChanges{}, err } return controllersChanges(changes), nil }
func (s *CommonProvisionerSuite) checkStartInstanceCustom( c *gc.C, m *state.Machine, secret string, cons constraints.Value, networks []string, networkInfo []network.InterfaceInfo, subnetsToZones map[network.Id][]string, volumes []storage.Volume, secureServerConnection bool, checkPossibleTools coretools.List, waitInstanceId bool, ) ( inst instance.Instance, ) { s.BackingState.StartSync() for { select { case o := <-s.op: switch o := o.(type) { case dummy.OpStartInstance: inst = o.Instance if waitInstanceId { s.waitInstanceId(c, m, inst.Id()) } // Check the instance was started with the expected params. c.Assert(o.MachineId, gc.Equals, m.Id()) nonceParts := strings.SplitN(o.MachineNonce, ":", 2) c.Assert(nonceParts, gc.HasLen, 2) c.Assert(nonceParts[0], gc.Equals, names.NewMachineTag("0").String()) c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString) c.Assert(o.Secret, gc.Equals, secret) c.Assert(o.SubnetsToZones, jc.DeepEquals, subnetsToZones) c.Assert(o.Networks, jc.DeepEquals, networks) c.Assert(o.NetworkInfo, jc.DeepEquals, networkInfo) c.Assert(o.Volumes, jc.DeepEquals, volumes) c.Assert(o.AgentEnvironment["SECURE_STATESERVER_CONNECTION"], gc.Equals, strconv.FormatBool(secureServerConnection)) var jobs []multiwatcher.MachineJob for _, job := range m.Jobs() { jobs = append(jobs, job.ToParams()) } c.Assert(o.Jobs, jc.SameContents, jobs) if checkPossibleTools != nil { for _, t := range o.PossibleTools { url := fmt.Sprintf("https://%s/environment/%s/tools/%s", s.st.Addr(), coretesting.EnvironmentTag.Id(), t.Version) c.Check(t.URL, gc.Equals, url) t.URL = "" } for _, t := range checkPossibleTools { t.URL = "" } c.Assert(o.PossibleTools, gc.DeepEquals, checkPossibleTools) } // All provisioned machines in this test suite have // their hardware characteristics attributes set to // the same values as the constraints due to the dummy // environment being used. if !constraints.IsEmpty(&cons) { c.Assert(o.Constraints, gc.DeepEquals, cons) hc, err := m.HardwareCharacteristics() c.Assert(err, jc.ErrorIsNil) c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{ Arch: cons.Arch, Mem: cons.Mem, RootDisk: cons.RootDisk, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, Tags: cons.Tags, }) } return default: c.Logf("ignoring unexpected operation %#v", o) } case <-time.After(2 * time.Second): c.Fatalf("provisioner did not start an instance") return } } }
func (c *DeployCommand) deployCharm( curl *charm.URL, series string, ctx *cmd.Context, client *api.Client, deployer *serviceDeployer, ) (rErr error) { if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } state, err := c.NewAPIRoot() if err != nil { return errors.Trace(err) } httpClient, err := c.HTTPClient() if err != nil { return errors.Trace(err) } deployInfo := DeploymentInfo{ CharmURL: curl, ServiceName: serviceName, ModelUUID: client.ModelUUID(), } for _, step := range c.Steps { err = step.RunPre(state, httpClient, ctx, deployInfo) if err != nil { return err } } defer func() { for _, step := range c.Steps { err = step.RunPost(state, httpClient, ctx, deployInfo, rErr) if err != nil { rErr = err } } }() if err := deployer.serviceDeploy(serviceDeployParams{ curl.String(), serviceName, series, numUnits, string(configYAML), c.Constraints, c.Placement, c.Networks, c.Storage, }); err != nil { return err } state, err = c.NewAPIRoot() if err != nil { return errors.Trace(err) } httpClient, err = c.HTTPClient() if err != nil { return errors.Trace(err) } return err }
func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := c.NewAPIClient() if err != nil { return err } defer client.Close() conf, err := service.GetClientConfig(client) if err != nil { return err } if err := c.CheckProvider(conf); err != nil { return err } csClient, err := newCharmStoreClient() if err != nil { return errors.Trace(err) } defer csClient.jar.Save() curl, repo, err := resolveCharmURL(c.CharmName, csClient.params, ctx.AbsPath(c.RepoPath), conf) if err != nil { return errors.Trace(err) } curl, err = addCharmViaAPI(client, ctx, curl, repo, csClient) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } // If storage or placement is specified, we attempt to use a new API on the service facade. if len(c.Storage) > 0 || len(c.Placement) > 0 { notSupported := errors.New("cannot deploy charms with storage or placement: not supported by the API server") serviceClient, err := c.newServiceAPIClient() if err != nil { return notSupported } defer serviceClient.Close() for i, p := range c.Placement { if p.Scope == "env-uuid" { p.Scope = serviceClient.EnvironmentUUID() } c.Placement[i] = p } err = serviceClient.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.PlacementSpec, c.Placement, []string{}, c.Storage, ) if params.IsCodeNotImplemented(err) { return notSupported } return block.ProcessBlockedError(err, block.BlockChange) } if len(c.Networks) > 0 { ctx.Infof("use of --networks is deprecated and is ignored. Please use spaces to manage placement within networks") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.PlacementSpec) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } state, err := c.NewAPIRoot() if err != nil { return err } err = registerMeteredCharm(c.RegisterURL, state, csClient.jar, curl.String(), serviceName, client.EnvironmentUUID()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Warningf("current state server version does not support charm metering") return nil } return block.ProcessBlockedError(err, block.BlockChange) }
func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := c.NewAPIClient() if err != nil { return err } defer client.Close() conf, err := service.GetClientConfig(client) if err != nil { return err } if err := c.CheckProvider(conf); err != nil { return err } csClient, err := newCharmStoreClient() if err != nil { return errors.Trace(err) } defer csClient.jar.Save() curl, repo, err := resolveCharmURL(c.CharmName, csClient.params, ctx.AbsPath(c.RepoPath), conf) if err != nil { return errors.Trace(err) } curl, err = addCharmViaAPI(client, ctx, curl, repo, csClient) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } requestedNetworks, err := networkNamesToTags(parseNetworks(c.Networks)) if err != nil { return err } // We need to ensure network names are valid below, but we don't need them here. _, err = networkNamesToTags(c.Constraints.IncludeNetworks()) if err != nil { return err } _, err = networkNamesToTags(c.Constraints.ExcludeNetworks()) if err != nil { return err } haveNetworks := len(requestedNetworks) > 0 || c.Constraints.HaveNetworks() charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } // If storage is specified, we attempt to use a new API on the service facade. if len(c.Storage) > 0 { notSupported := errors.New("cannot deploy charms with storage: not supported by the API server") serviceClient, err := c.newServiceAPIClient() if err != nil { return notSupported } defer serviceClient.Close() err = serviceClient.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, requestedNetworks, c.Storage, ) if params.IsCodeNotImplemented(err) { return notSupported } return block.ProcessBlockedError(err, block.BlockChange) } err = client.ServiceDeployWithNetworks( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, requestedNetworks, ) if params.IsCodeNotImplemented(err) { if haveNetworks { return errors.New("cannot use --networks/--constraints networks=...: not supported by the API server") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec) } if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } state, err := c.NewAPIRoot() if err != nil { return err } err = registerMeteredCharm(c.RegisterURL, state, csClient.jar, curl.String(), serviceName, client.EnvironmentUUID()) if err != nil { return err } return block.ProcessBlockedError(err, block.BlockChange) }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { args.ServiceOwner = "user-admin" } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { conf, err := st.EnvironConfig() if err != nil { return nil, err } env, err := environs.New(conf) if err != nil { return nil, err } if !env.SupportNetworks() { return nil, fmt.Errorf("cannot deploy with networks: not suppored by the environment") } } service, err := st.AddService( args.ServiceName, args.ServiceOwner, args.Charm, args.Networks, ) if err != nil { return nil, err } if len(settings) > 0 { if err := service.UpdateConfigSettings(settings); err != nil { return nil, err } } if args.Charm.Meta().Subordinate { return service, nil } if !constraints.IsEmpty(&args.Constraints) { if err := service.SetConstraints(args.Constraints); err != nil { return nil, err } } if args.NumUnits > 0 { if _, err := AddUnits(st, service, args.NumUnits, args.ToMachineSpec); err != nil { return nil, err } } return service, nil }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && len(args.Placement) == 0 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { env, err := st.Environment() if err != nil { return nil, errors.Trace(err) } args.ServiceOwner = env.Owner().String() } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { conf, err := st.EnvironConfig() if err != nil { return nil, err } env, err := environs.New(conf) if err != nil { return nil, err } if _, ok := environs.SupportsNetworking(env); !ok { return nil, fmt.Errorf("cannot deploy with networks: not suppored by the environment") } } service, err := st.AddService( args.ServiceName, args.ServiceOwner, args.Charm, args.Networks, stateStorageConstraints(args.Storage), ) if err != nil { return nil, err } if len(settings) > 0 { if err := service.UpdateConfigSettings(settings); err != nil { return nil, err } } if args.Charm.Meta().Subordinate { return service, nil } if !constraints.IsEmpty(&args.Constraints) { if err := service.SetConstraints(args.Constraints); err != nil { return nil, err } } if args.NumUnits > 0 { var err error // We either have a machine spec or a placement directive. // Placement directives take precedence. if len(args.Placement) > 0 || args.ToMachineSpec == "" { _, err = AddUnitsWithPlacement(st, service, args.NumUnits, args.Placement) } else { _, err = AddUnits(st, service, args.NumUnits, args.ToMachineSpec) } if err != nil { return nil, err } } return service, nil }
func (c *deployCommand) Run(ctx *cmd.Context) error { client, err := c.NewAPIClient() if err != nil { return err } defer client.Close() conf, err := service.GetClientConfig(client) if err != nil { return err } if err := c.CheckProvider(conf); err != nil { return err } httpClient, err := c.HTTPClient() if err != nil { return errors.Trace(err) } csClient := newCharmStoreClient(httpClient) repoPath := ctx.AbsPath(c.RepoPath) // Handle local bundle paths. f, err := os.Open(c.CharmOrBundle) if err == nil { defer f.Close() info, err := f.Stat() if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if info.IsDir() { return errors.New("deployment of bundle directories not yet supported") } bundleData, err := charm.ReadBundleData(f) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if err := deployBundle(bundleData, client, csClient, repoPath, conf, ctx); err != nil { return block.ProcessBlockedError(err, block.BlockChange) } ctx.Infof("deployment of bundle %q completed", f.Name()) return nil } else if !os.IsNotExist(err) { logger.Warningf("cannot open %q: %v; falling back to using charm repository", c.CharmOrBundle, err) } curl, repo, err := resolveCharmStoreEntityURL(c.CharmOrBundle, csClient.params, repoPath, conf) if err != nil { return errors.Trace(err) } // Handle bundle URLs. if curl.Series == "bundle" { // Deploy a bundle entity. bundle, err := repo.GetBundle(curl) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if err := deployBundle(bundle.Data(), client, csClient, repoPath, conf, ctx); err != nil { return block.ProcessBlockedError(err, block.BlockChange) } ctx.Infof("deployment of bundle %q completed", curl) return nil } curl, err = addCharmViaAPI(client, curl, repo, csClient) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } ctx.Infof("Added charm %q to the environment.", curl) if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } // If storage or placement is specified, we attempt to use a new API on the service facade. if len(c.Storage) > 0 || len(c.Placement) > 0 { notSupported := errors.New("cannot deploy charms with storage or placement: not supported by the API server") serviceClient, err := c.newServiceAPIClient() if err != nil { return notSupported } defer serviceClient.Close() for i, p := range c.Placement { if p.Scope == "env-uuid" { p.Scope = serviceClient.EnvironmentUUID() } c.Placement[i] = p } err = serviceClient.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.PlacementSpec, c.Placement, []string{}, c.Storage, ) if params.IsCodeNotImplemented(err) { return notSupported } return block.ProcessBlockedError(err, block.BlockChange) } if len(c.Networks) > 0 { ctx.Infof("use of --networks is deprecated and is ignored. Please use spaces to manage placement within networks") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.PlacementSpec) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } state, err := c.NewAPIRoot() if err != nil { return err } err = registerMeteredCharm(c.RegisterURL, state, httpClient, curl.String(), serviceName, client.EnvironmentUUID()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Warningf("current state server version does not support charm metering") return nil } return block.ProcessBlockedError(err, block.BlockChange) }
func (c *DeployCommand) deployCharm(args deployCharmArgs) (rErr error) { charmInfo, err := args.client.CharmInfo(args.id.URL.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(args.ctx) if err != nil { return err } } state, err := c.NewAPIRoot() if err != nil { return errors.Trace(err) } bakeryClient, err := c.BakeryClient() if err != nil { return errors.Trace(err) } deployInfo := DeploymentInfo{ CharmID: args.id, ServiceName: serviceName, ModelUUID: args.client.ModelUUID(), } for _, step := range c.Steps { err = step.RunPre(state, bakeryClient, args.ctx, deployInfo) if err != nil { return err } } defer func() { for _, step := range c.Steps { err = step.RunPost(state, bakeryClient, args.ctx, deployInfo, rErr) if err != nil { rErr = err } } }() if args.id.URL != nil && args.id.URL.Schema != "local" && len(charmInfo.Meta.Terms) > 0 { args.ctx.Infof("Deployment under prior agreement to terms: %s", strings.Join(charmInfo.Meta.Terms, " ")) } ids, err := handleResources(c, c.Resources, serviceName, args.id, args.csMac, charmInfo.Meta.Resources) if err != nil { return errors.Trace(err) } params := serviceDeployParams{ charmID: args.id, serviceName: serviceName, series: args.series, numUnits: numUnits, configYAML: string(configYAML), constraints: c.Constraints, placement: c.Placement, storage: c.Storage, spaceBindings: c.Bindings, resources: ids, } return args.deployer.serviceDeploy(params) }
func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() attrs, err := client.EnvironmentGet() if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } curl, err := resolveCharmURL(c.CharmName, client, conf) if err != nil { return err } repo, err := charm.InferRepository(curl.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) curl, err = addCharmViaAPI(client, ctx, curl, repo) if err != nil { return err } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } requestedNetworks, err := networkNamesToTags(parseNetworks(c.Networks)) if err != nil { return err } // We need to ensure network names are valid below, but we don't need them here. _, err = networkNamesToTags(c.Constraints.IncludeNetworks()) if err != nil { return err } _, err = networkNamesToTags(c.Constraints.ExcludeNetworks()) if err != nil { return err } haveNetworks := len(requestedNetworks) > 0 || c.Constraints.HaveNetworks() charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } err = client.ServiceDeployWithNetworks( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, requestedNetworks, ) if params.IsCodeNotImplemented(err) { if haveNetworks { return errors.New("cannot use --networks/--constraints networks=...: not supported by the API server") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec) } return err }
func (c *DeployCommand) deployCharm( id charmstore.CharmID, csMac *macaroon.Macaroon, series string, ctx *cmd.Context, apiRoot DeployAPI, ) (rErr error) { charmInfo, err := apiRoot.CharmInfo(id.URL.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate application") } if numUnits == 1 && c.PlacementSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate application") } } serviceName := c.ApplicationName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return errors.Trace(err) } } bakeryClient, err := c.BakeryClient() if err != nil { return errors.Trace(err) } uuid, ok := apiRoot.ModelUUID() if !ok { return errors.New("API connection is controller-only (should never happen)") } deployInfo := DeploymentInfo{ CharmID: id, ApplicationName: serviceName, ModelUUID: uuid, CharmInfo: charmInfo, } for _, step := range c.Steps { err = step.RunPre(apiRoot, bakeryClient, ctx, deployInfo) if err != nil { return errors.Trace(err) } } defer func() { for _, step := range c.Steps { err = errors.Trace(step.RunPost(apiRoot, bakeryClient, ctx, deployInfo, rErr)) if err != nil { rErr = err } } }() if id.URL != nil && id.URL.Schema != "local" && len(charmInfo.Meta.Terms) > 0 { ctx.Infof("Deployment under prior agreement to terms: %s", strings.Join(charmInfo.Meta.Terms, " ")) } ids, err := resourceadapters.DeployResources( serviceName, id, csMac, c.Resources, charmInfo.Meta.Resources, apiRoot, ) if err != nil { return errors.Trace(err) } return errors.Trace(apiRoot.Deploy(application.DeployArgs{ CharmID: id, Cons: c.Constraints, ApplicationName: serviceName, Series: series, NumUnits: numUnits, ConfigYAML: string(configYAML), Placement: c.Placement, Storage: c.Storage, Resources: ids, EndpointBindings: c.Bindings, })) }