// Login authenticates as the entity with the given name and password. // Subsequent requests on the state will act as that entity. This // method is usually called automatically by Open. The machine nonce // should be empty unless logging in as a machine agent. func (st *State) Login(tag, password, nonce string) error { err := st.loginV2(tag, password, nonce) if params.IsCodeNotImplemented(err) { err = st.loginV1(tag, password, nonce) if params.IsCodeNotImplemented(err) { // TODO (cmars): remove fallback once we can drop v0 compatibility return st.loginV0(tag, password, nonce) } } return err }
// AddMetricsBatches makes an api call to the uniter requesting it to store metrics batches in state. func (u *Unit) AddMetricBatches(batches []params.MetricBatch) (map[string]error, error) { p := params.MetricBatchParams{ Batches: make([]params.MetricBatchParam, len(batches)), } batchResults := make(map[string]error, len(batches)) for i, batch := range batches { p.Batches[i].Tag = u.tag.String() p.Batches[i].Batch = batch batchResults[batch.UUID] = nil } results := new(params.ErrorResults) err := u.st.facade.FacadeCall("AddMetricBatches", p, results) if params.IsCodeNotImplemented(err) { for _, batch := range batches { err = u.AddMetrics(batch.Metrics) if err != nil { batchResults[batch.UUID] = errors.Annotate(err, "failed to send metric batch") } } return batchResults, nil } else if err != nil { return nil, errors.Annotate(err, "failed to send metric batches") } for i, result := range results.Results { batchResults[batches[i].UUID] = result.Error } return batchResults, nil }
func containerManagerConfig( containerType instance.ContainerType, provisioner *apiprovisioner.State, agentConfig agent.Config, ) (container.ManagerConfig, error) { // Ask the provisioner for the container manager configuration. managerConfigResult, err := provisioner.ContainerManagerConfig( params.ContainerManagerConfigParams{Type: containerType}, ) if params.IsCodeNotImplemented(err) { // We currently don't support upgrading; // revert to the old configuration. managerConfigResult.ManagerConfig = container.ManagerConfig{container.ConfigName: container.DefaultNamespace} } if err != nil { return nil, err } // If a namespace is specified, that should instead be used as the config name. if namespace := agentConfig.Value(agent.Namespace); namespace != "" { managerConfigResult.ManagerConfig[container.ConfigName] = namespace } managerConfig := container.ManagerConfig(managerConfigResult.ManagerConfig) return managerConfig, nil }
// runRestore will implement the actual calls to the different Client parts // of restore. func (c *restoreCommand) runRestore(ctx *cmd.Context) error { client, closer, err := c.newClient() if err != nil { return errors.Trace(err) } defer closer() var target string var rErr error if c.filename != "" { target = c.filename archive, meta, err := getArchive(c.filename) if err != nil { return errors.Trace(err) } defer archive.Close() rErr = client.RestoreReader(archive, meta, c.newClient) } else { target = c.backupId rErr = client.Restore(c.backupId, c.newClient) } if params.IsCodeNotImplemented(rErr) { return errors.Errorf(restoreAPIIncompatibility) } if rErr != nil { return errors.Trace(rErr) } fmt.Fprintf(ctx.Stdout, "restore from %q completed\n", target) return nil }
// SetStatus sets the status of the service if the passed unitName, // corresponding to the calling unit, is of the leader. func (s *Service) SetStatus(unitName string, status params.Status, info string, data map[string]interface{}) error { //TODO(perrito666) bump api version for this? if s.st.facade.BestAPIVersion() < 2 { return errors.NotImplementedf("SetStatus") } tag := names.NewUnitTag(unitName) var result params.ErrorResults args := params.SetStatus{ Entities: []params.EntityStatusArgs{ { Tag: tag.String(), Status: status, Info: info, Data: data, }, }, } err := s.st.facade.FacadeCall("SetServiceStatus", args, &result) if err != nil { if params.IsCodeNotImplemented(err) { return errors.NotImplementedf("SetServiceStatus") } return errors.Trace(err) } return result.OneError() }
func (c *DestroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) { store, err := configstore.Default() if err != nil { return fmt.Errorf("cannot open environment info storage: %v", err) } environ, err := environs.NewFromName(c.envName, store) if err != nil { if environs.IsEmptyConfig(err) { // Delete the .jenv file and call it done. ctx.Infof("removing empty environment file") return environs.DestroyInfo(c.envName, store) } return err } if !c.assumeYes { fmt.Fprintf(ctx.Stdout, destroyEnvMsg, c.envName, environ.Config().Type()) scanner := bufio.NewScanner(ctx.Stdin) scanner.Scan() err := scanner.Err() if err != nil && err != io.EOF { return fmt.Errorf("Environment destruction aborted: %s", err) } answer := strings.ToLower(scanner.Text()) if answer != "y" && answer != "yes" { return errors.New("environment destruction aborted") } } // If --force is supplied, then don't attempt to use the API. // This is necessary to destroy broken environments, where the // API server is inaccessible or faulty. if !c.force { defer func() { if result == nil { return } logger.Errorf(`failed to destroy environment %q If the environment is unusable, then you may run juju destroy-environment --force to forcefully destroy the environment. Upon doing so, review your environment provider console for any resources that need to be cleaned up. `, c.envName) }() apiclient, err := juju.NewAPIClientFromName(c.envName) if err != nil { return fmt.Errorf("cannot connect to API: %v", err) } defer apiclient.Close() err = apiclient.DestroyEnvironment() if err != nil && !params.IsCodeNotImplemented(err) { return fmt.Errorf("destroying environment: %v", err) } } return environs.Destroy(environ, store) }
// getJoinedRelations finds out what relations the unit is *really* part of, // working around the fact that pre-1.19 (1.18.1?) unit agents don't write a // state dir for a relation until a remote unit joins. func (u *Uniter) getJoinedRelations() (map[int]*uniter.Relation, error) { var joinedRelationTags []string for { var err error joinedRelationTags, err = u.unit.JoinedRelations() if err == nil { break } if params.IsCodeNotImplemented(err) { logger.Infof("waiting for state server to be upgraded") select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case <-time.After(15 * time.Second): continue } } return nil, err } joinedRelations := make(map[int]*uniter.Relation) for _, tag := range joinedRelationTags { relation, err := u.st.Relation(tag) if err != nil { return nil, err } joinedRelations[relation.Id()] = relation } return joinedRelations, nil }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { inst, metadata, networkInfo, err := task.broker.StartInstance(startInstanceParams) if err != nil { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } nonce := startInstanceParams.MachineConfig.MachineNonce networks, ifaces := task.prepareNetworkAndInterfaces(networkInfo) err = machine.SetInstanceInfo(inst.Id(), nonce, metadata, networks, ifaces) if err != nil && params.IsCodeNotImplemented(err) { return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine) } else if err == nil { logger.Infof("started machine %s as instance %s with hardware %q, networks %v, interfaces %v", machine, inst.Id(), metadata, networks, ifaces) return nil } // We need to stop the instance right away here, set error status and go on. task.setErrorStatus("cannot register instance for machine %v: %v", machine, err) if err := task.broker.StopInstances(inst.Id()); err != nil { // We cannot even stop the instance, log the error and quit. return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine) } return nil }
// getControllerEnviron gets the bootstrap information required to destroy the // environment by first checking the config store, then querying the API if // the information is not in the store. func (c *destroyCommandBase) getControllerEnviron(info configstore.EnvironInfo, sysAPI destroyControllerAPI) (_ environs.Environ, err error) { bootstrapCfg := info.BootstrapConfig() if bootstrapCfg == nil { if sysAPI == nil { return nil, errors.New("unable to get bootstrap information from API") } bootstrapCfg, err = sysAPI.EnvironmentConfig() if params.IsCodeNotImplemented(err) { // Fallback to the client API. Better to encapsulate the logic for // old servers than worry about connecting twice. client, err := c.getClientAPI() if err != nil { return nil, errors.Trace(err) } defer client.Close() bootstrapCfg, err = client.EnvironmentGet() if err != nil { return nil, errors.Trace(err) } } else if err != nil { return nil, errors.Trace(err) } } cfg, err := config.New(config.NoDefaults, bootstrapCfg) if err != nil { return nil, errors.Trace(err) } return environs.New(cfg) }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { result, err := task.broker.StartInstance(startInstanceParams) if err != nil { // If this is a retryable error, we retry once if instance.IsRetryableCreationError(errors.Cause(err)) { logger.Infof("retryable error received on start instance - retrying instance creation") result, err = task.broker.StartInstance(startInstanceParams) if err != nil { return task.setErrorStatus("cannot start instance for machine after a retry %q: %v", machine, err) } } else { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } } inst := result.Instance hardware := result.Hardware nonce := startInstanceParams.InstanceConfig.MachineNonce networks, ifaces, err := task.prepareNetworkAndInterfaces(result.NetworkInfo) if err != nil { return task.setErrorStatus("cannot prepare network for machine %q: %v", machine, err) } volumes := volumesToApiserver(result.Volumes) volumeAttachments := volumeAttachmentsToApiserver(result.VolumeAttachments) // TODO(dimitern) In a newer Provisioner API version, change // SetInstanceInfo or add a new method that takes and saves in // state all the information available on a network.InterfaceInfo // for each interface, so we can later manage interfaces // dynamically at run-time. err = machine.SetInstanceInfo(inst.Id(), nonce, hardware, networks, ifaces, volumes, volumeAttachments) if err != nil && params.IsCodeNotImplemented(err) { return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine) } else if err == nil { logger.Infof( "started machine %s as instance %s with hardware %q, networks %v, interfaces %v, volumes %v, volume attachments %v, subnets to zones %v", machine, inst.Id(), hardware, networks, ifaces, volumes, volumeAttachments, startInstanceParams.SubnetsToZones, ) return nil } // We need to stop the instance right away here, set error status and go on. task.setErrorStatus("cannot register instance for machine %v: %v", machine, err) if err := task.broker.StopInstances(inst.Id()); err != nil { // We cannot even stop the instance, log the error and quit. return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine) } return nil }
// processDestroyError determines how to format error message based on its code. // Note that CodeNotImplemented errors have not be propogated in previous implementation. // This behaviour was preserved. func processDestroyError(err error) error { if err == nil || params.IsCodeNotImplemented(err) { return nil } if params.IsCodeOperationBlocked(err) { return err } return errors.Annotate(err, "destroying environment") }
func machineLoop(context machineContext, m machine, changed <-chan struct{}) error { // Use a short poll interval when initially waiting for // a machine's address and machine agent to start, and a long one when it already // has an address and the machine agent is started. pollInterval := ShortPoll pollInstance := true for { if pollInstance { instInfo, err := pollInstanceInfo(context, m) if err != nil && !params.IsCodeNotProvisioned(err) { // If the provider doesn't implement Addresses/Status now, // it never will until we're upgraded, so don't bother // asking any more. We could use less resources // by taking down the entire worker, but this is easier for now // (and hopefully the local provider will implement // Addresses/Status in the not-too-distant future), // so we won't need to worry about this case at all. if params.IsCodeNotImplemented(err) { pollInterval = 365 * 24 * time.Hour } else { return err } } machineStatus := params.StatusPending if err == nil { if statusInfo, err := m.Status(); err != nil { logger.Warningf("cannot get current machine status for machine %v: %v", m.Id(), err) } else { machineStatus = statusInfo.Status } } if len(instInfo.addresses) > 0 && instInfo.status != "" && machineStatus == params.StatusStarted { // We've got at least one address and a status and instance is started, so poll infrequently. pollInterval = LongPoll } else if pollInterval < LongPoll { // We have no addresses or not started - poll increasingly rarely // until we do. pollInterval = time.Duration(float64(pollInterval) * ShortPollBackoff) } pollInstance = false } select { case <-time.After(pollInterval): pollInstance = true case <-context.dying(): return nil case <-changed: if err := m.Refresh(); err != nil { return err } if m.Life() == params.Dead { return nil } } } }
// pollInstanceInfo checks the current provider addresses and status // for the given machine's instance, and sets them on the machine if they've changed. func pollInstanceInfo(context machineContext, m machine) (instInfo instanceInfo, err error) { instInfo = instanceInfo{} instId, err := m.InstanceId() // We can't ask the machine for its addresses if it isn't provisioned yet. if params.IsCodeNotProvisioned(err) { return instanceInfo{}, err } if err != nil { return instanceInfo{}, errors.Annotate(err, "cannot get machine's instance id") } instInfo, err = context.instanceInfo(instId) if err != nil { // TODO (anastasiamac 2016-02-01) This does not look like it needs to be removed now. if params.IsCodeNotImplemented(err) { return instanceInfo{}, err } logger.Warningf("cannot get instance info for instance %q: %v", instId, err) return instInfo, nil } if instStat, err := m.InstanceStatus(); err != nil { // This should never occur since the machine is provisioned. // But just in case, we reset polled status so we try again next time. logger.Warningf("cannot get current instance status for machine %v: %v", m.Id(), err) instInfo.status = instance.InstanceStatus{status.Unknown, ""} } else { // TODO(perrito666) add status validation. currentInstStatus := instance.InstanceStatus{ Status: status.Status(instStat.Status), Message: instStat.Info, } if instInfo.status != currentInstStatus { logger.Infof("machine %q instance status changed from %q to %q", m.Id(), currentInstStatus, instInfo.status) if err = m.SetInstanceStatus(instInfo.status.Status, instInfo.status.Message, nil); err != nil { logger.Errorf("cannot set instance status on %q: %v", m, err) return instanceInfo{}, err } } } if m.Life() != params.Dead { providerAddresses, err := m.ProviderAddresses() if err != nil { return instanceInfo{}, err } if !addressesEqual(providerAddresses, instInfo.addresses) { logger.Infof("machine %q has new addresses: %v", m.Id(), instInfo.addresses) if err := m.SetProviderAddresses(instInfo.addresses...); err != nil { logger.Errorf("cannot set addresses on %q: %v", m, err) return instanceInfo{}, err } } } return instInfo, nil }
// Run implements Command.Run func (c *destroyCommand) Run(ctx *cmd.Context) error { store, err := configstore.Default() if err != nil { return errors.Annotate(err, "cannot open system info storage") } cfgInfo, err := store.ReadInfo(c.systemName) if err != nil { return errors.Annotate(err, "cannot read system info") } // Verify that we're destroying a system apiEndpoint := cfgInfo.APIEndpoint() if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID { return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName) } if !c.assumeYes { if err = confirmDestruction(ctx, c.systemName); err != nil { return err } } // Attempt to connect to the API. If we can't, fail the destroy. Users will // need to use the system kill command if we can't connect. api, err := c.getSystemAPI() if err != nil { return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot connect to API"), ctx, nil) } defer api.Close() // Obtain bootstrap / system environ information systemEnviron, err := c.getSystemEnviron(cfgInfo, api) if err != nil { return errors.Annotate(err, "cannot obtain bootstrap information") } // Attempt to destroy the system. err = api.DestroySystem(c.destroyEnvs, false) if params.IsCodeNotImplemented(err) { // Fall back to using the client endpoint to destroy the system, // sending the info we were already able to collect. return c.destroySystemViaClient(ctx, cfgInfo, systemEnviron, store) } if err != nil { return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot destroy system"), ctx, api) } return environs.Destroy(systemEnviron, store) }
// UnitStatusHistory retrieves the last <size> results of <kind:combined|agent|workload> status // for <unitName> unit func (c *Client) UnitStatusHistory(kind params.HistoryKind, unitName string, size int) (*UnitStatusHistory, error) { var results UnitStatusHistory args := params.StatusHistory{ Kind: kind, Size: size, Name: unitName, } err := c.facade.FacadeCall("UnitStatusHistory", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { return &UnitStatusHistory{}, errors.NotImplementedf("UnitStatusHistory") } return &UnitStatusHistory{}, errors.Trace(err) } return &results, nil }
// Environment returns the environment entity. func (st *State) Environment() (*Environment, error) { var result params.EnvironmentResult err := st.facade.FacadeCall("CurrentEnvironment", nil, &result) if params.IsCodeNotImplemented(err) { // Fall back to using the 1.16 API. return st.environment1dot16() } if err != nil { return nil, err } if err := result.Error; err != nil { return nil, err } return &Environment{ name: result.Name, uuid: result.UUID, }, nil }
// ServiceDeploy obtains the charm, either locally or from // the charm store, and deploys it. It allows the specification of // requested networks that must be present on the machines where the // service is deployed. Another way to specify networks to include/exclude // is using constraints. Placement directives, if provided, specify the // machine on which the charm is deployed. func (c *Client) ServiceDeploy( charmURL string, serviceName string, series string, numUnits int, configYAML string, cons constraints.Value, toMachineSpec string, placement []*instance.Placement, networks []string, storage map[string]storage.Constraints, ) error { args := params.ServicesDeploy{ Services: []params.ServiceDeploy{{ ServiceName: serviceName, Series: series, CharmUrl: charmURL, NumUnits: numUnits, ConfigYAML: configYAML, Constraints: cons, ToMachineSpec: toMachineSpec, Placement: placement, Networks: networks, Storage: storage, }}, } var results params.ErrorResults var err error if len(placement) > 0 { err = c.FacadeCall("ServicesDeployWithPlacement", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { return errors.Errorf("unsupported --to parameter %q", toMachineSpec) } return err } } else { err = c.FacadeCall("ServicesDeploy", args, &results) } if err != nil { return err } return results.OneError() }
// pollInstanceInfo checks the current provider addresses and status // for the given machine's instance, and sets them on the machine if they've changed. func pollInstanceInfo(context machineContext, m machine) (instInfo instanceInfo, err error) { instInfo = instanceInfo{} instId, err := m.InstanceId() // We can't ask the machine for its addresses if it isn't provisioned yet. if params.IsCodeNotProvisioned(err) { return instInfo, err } if err != nil { return instInfo, fmt.Errorf("cannot get machine's instance id: %v", err) } instInfo, err = context.instanceInfo(instId) if err != nil { if params.IsCodeNotImplemented(err) { return instInfo, err } logger.Warningf("cannot get instance info for instance %q: %v", instId, err) return instInfo, nil } currentInstStatus, err := m.InstanceStatus() if err != nil { // This should never occur since the machine is provisioned. // But just in case, we reset polled status so we try again next time. logger.Warningf("cannot get current instance status for machine %v: %v", m.Id(), err) instInfo.status = "" } else { if instInfo.status != currentInstStatus { logger.Infof("machine %q instance status changed from %q to %q", m.Id(), currentInstStatus, instInfo.status) if err = m.SetInstanceStatus(instInfo.status); err != nil { logger.Errorf("cannot set instance status on %q: %v", m, err) } } } providerAddresses, err := m.ProviderAddresses() if err != nil { return instInfo, err } if !addressesEqual(providerAddresses, instInfo.addresses) { logger.Infof("machine %q has new addresses: %v", m.Id(), instInfo.addresses) if err = m.SetProviderAddresses(instInfo.addresses...); err != nil { logger.Errorf("cannot set addresses on %q: %v", m, err) } } return instInfo, err }
// MachineNetworkConfig returns information about network interfaces to // setup only for a single machine. func (st *state) MachineNetworkConfig(tag names.MachineTag) ([]network.InterfaceInfo, error) { args := params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, } var results params.MachineNetworkConfigResults err := st.facade.FacadeCall("MachineNetworkConfig", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { // Fallback to former name. err = st.facade.FacadeCall("MachineNetworkInfo", args, &results) } if err != nil { // TODO: Not directly tested. return nil, err } } if len(results.Results) != 1 { // TODO: Not directly tested err = errors.Errorf("expected one result, got %d", len(results.Results)) return nil, err } result := results.Results[0] if result.Error != nil { return nil, result.Error } interfaceInfo := make([]network.InterfaceInfo, len(result.Config)) for i, ifaceInfo := range result.Config { interfaceInfo[i].DeviceIndex = ifaceInfo.DeviceIndex interfaceInfo[i].MACAddress = ifaceInfo.MACAddress interfaceInfo[i].CIDR = ifaceInfo.CIDR interfaceInfo[i].NetworkName = ifaceInfo.NetworkName interfaceInfo[i].ProviderId = network.Id(ifaceInfo.ProviderId) interfaceInfo[i].VLANTag = ifaceInfo.VLANTag interfaceInfo[i].InterfaceName = ifaceInfo.InterfaceName interfaceInfo[i].Disabled = ifaceInfo.Disabled // TODO(dimitern) Once we store all the information from // network.InterfaceInfo in state, change this as needed to // return it. } return interfaceInfo, nil }
// Run connects to the environment specified on the command line // and calls AddServiceUnits for the given service. func (c *addUnitCommand) Run(_ *cmd.Context) error { apiclient, err := c.getAPI() if err != nil { return err } defer apiclient.Close() conf, err := GetClientConfig(apiclient) if err != nil { return err } if err := c.CheckProvider(conf); err != nil { return err } for i, p := range c.Placement { if p.Scope == "env-uuid" { p.Scope = apiclient.EnvironmentUUID() } c.Placement[i] = p } if len(c.Placement) > 0 { _, err = apiclient.AddServiceUnitsWithPlacement(c.ServiceName, c.NumUnits, c.Placement) if err == nil { return nil } if !params.IsCodeNotImplemented(err) { return block.ProcessBlockedError(err, block.BlockChange) } } if c.PlacementSpec != "" && !IsMachineOrNewContainer(c.PlacementSpec) { return errors.Errorf("unsupported --to parameter %q", c.PlacementSpec) } if c.PlacementSpec != "" && c.NumUnits > 1 { return errors.New("this version of Juju does not support --num-units > 1 with --to") } _, err = apiclient.AddServiceUnits(c.ServiceName, c.NumUnits, c.PlacementSpec) return block.ProcessBlockedError(err, block.BlockChange) }
// RunPre obtains authorization to deploy this charm. The authorization, if received is not // sent to the controller, rather it is kept as an attribute on RegisterMeteredCharm. func (r *RegisterMeteredCharm) RunPre(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error { charmsClient := charms.NewClient(state) defer charmsClient.Close() metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Tracef("current state server version does not support charm metering") return nil } else if err != nil { return err } if !metered { return nil } bakeryClient := httpbakery.Client{Client: client, VisitWebPage: httpbakery.OpenWebBrowser} if r.Plan == "" { r.Plan, err = r.getDefaultPlan(client, deployInfo.CharmURL.String()) if err != nil { if isNoDefaultPlanError(err) { options, err1 := r.getCharmPlans(client, deployInfo.CharmURL.String()) if err1 != nil { return err1 } charmUrl := deployInfo.CharmURL.String() return errors.Errorf(`%v has no default plan. Try "juju deploy --plan <plan-name> with one of %v"`, charmUrl, strings.Join(options, ", ")) } return err } } r.credentials, err = r.registerMetrics(deployInfo.EnvUUID, deployInfo.CharmURL.String(), deployInfo.ServiceName, &bakeryClient) if err != nil { logger.Infof("failed to obtain plan authorization: %v", err) return err } return nil }
// RunPre is part of the DeployStep interface. func (a *AllocateBudget) RunPre(state api.Connection, client *http.Client, ctx *cmd.Context, deployInfo DeploymentInfo) error { if deployInfo.CharmURL.Schema == "local" { return nil } charmsClient := charms.NewClient(state) metered, err := charmsClient.IsMetered(deployInfo.CharmURL.String()) if params.IsCodeNotImplemented(err) { // The state server is too old to support metering. Warn // the user, but don't return an error. logger.Tracef("current state server version does not support charm metering") return nil } else if err != nil { return errors.Annotate(err, "could not determine charm type") } if !metered { return nil } allocBudget, allocLimit, err := parseBudgetWithLimit(a.AllocationSpec) if err != nil { return errors.Trace(err) } a.APIClient, err = getApiClient(client) if err != nil { return errors.Annotate(err, "could not create API client") } resp, err := a.APIClient.CreateAllocation(allocBudget, allocLimit, deployInfo.ModelUUID, []string{deployInfo.ServiceName}) if err != nil { if wireformat.IsNotAvail(err) { fmt.Fprintf(ctx.Stdout, "WARNING: Allocation not created - %s.\n", err.Error()) return nil } return errors.Annotate(err, "could not create budget allocation") } a.allocated = true fmt.Fprintf(ctx.Stdout, "%s\n", resp) return nil }
// ServiceStatus returns the status of the service if the passed unitName, // corresponding to the calling unit, is of the leader. func (s *Service) Status(unitName string) (params.ServiceStatusResult, error) { tag := names.NewUnitTag(unitName) var results params.ServiceStatusResults args := params.Entities{ Entities: []params.Entity{ { Tag: tag.String(), }, }, } err := s.st.facade.FacadeCall("ServiceStatus", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { return params.ServiceStatusResult{}, errors.NotImplementedf("ServiceStatus") } return params.ServiceStatusResult{}, errors.Trace(err) } result := results.Results[0] if result.Error != nil { return params.ServiceStatusResult{}, result.Error } return result, nil }
// UnitStatus gets the status details of the unit. func (u *Unit) UnitStatus() (params.StatusResult, error) { var results params.StatusResults args := params.Entities{ Entities: []params.Entity{ {Tag: u.tag.String()}, }, } err := u.st.facade.FacadeCall("UnitStatus", args, &results) if err != nil { if params.IsCodeNotImplemented(err) { return params.StatusResult{}, errors.NotImplementedf("UnitStatus") } return params.StatusResult{}, errors.Trace(err) } if len(results.Results) != 1 { panic(errors.Errorf("expected 1 result, got %d", len(results.Results))) } result := results.Results[0] if result.Error != nil { return params.StatusResult{}, result.Error } return result, nil }
// RunPost sends credentials obtained during the call to RunPre to the controller. func (r *RegisterMeteredCharm) RunPost(state api.Connection, client *http.Client, deployInfo DeploymentInfo) error { if r.credentials == nil { return nil } api, cerr := getMetricCredentialsAPI(state) if cerr != nil { logger.Infof("failed to get the metrics credentials setter: %v", cerr) return cerr } defer api.Close() err := api.SetMetricCredentials(deployInfo.ServiceName, r.credentials) 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 } else if err != nil { logger.Infof("failed to set metric credentials: %v", err) return err } return nil }
// ProvisioningInfo is new in 1.20; wait for the API server to be // upgraded so we don't spew errors on upgrade. func (task *provisionerTask) blockUntilProvisioned( provision func() (*params.ProvisioningInfo, error), ) (*params.ProvisioningInfo, error) { var pInfo *params.ProvisioningInfo var err error for { if pInfo, err = provision(); err == nil { break } if params.IsCodeNotImplemented(err) { logger.Infof("waiting for state server to be upgraded") select { case <-task.tomb.Dying(): return nil, tomb.ErrDying case <-time.After(15 * time.Second): continue } } return nil, err } return pInfo, 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 } 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) }
// Run implements Command.Run func (c *killCommand) Run(ctx *cmd.Context) error { if c.apiDialerFunc == nil { c.apiDialerFunc = c.NewAPIRoot } store, err := configstore.Default() if err != nil { return errors.Annotate(err, "cannot open system info storage") } cfgInfo, err := store.ReadInfo(c.systemName) if err != nil { return errors.Annotate(err, "cannot read system info") } // Verify that we're destroying a system apiEndpoint := cfgInfo.APIEndpoint() if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID != apiEndpoint.ServerUUID { return errors.Errorf("%q is not a system; use juju environment destroy to destroy it", c.systemName) } if !c.assumeYes { if err = confirmDestruction(ctx, c.systemName); err != nil { return err } } // Attempt to connect to the API. api, err := c.getSystemAPI(cfgInfo) switch { case err == nil: defer api.Close() case errors.Cause(err) == common.ErrPerm: return errors.Annotate(err, "cannot destroy system") default: if err != ErrConnTimedOut { logger.Debugf("unable to open api: %s", err) } ctx.Infof("Unable to open API: %s\n", err) api = nil } // Obtain bootstrap / system environ information systemEnviron, err := c.getSystemEnviron(cfgInfo, api) if err != nil { return errors.Annotate(err, "cannot obtain bootstrap information") } // If we were unable to connect to the API, just destroy the system through // the environs interface. if api == nil { return environs.Destroy(systemEnviron, store) } // Attempt to destroy the system with destroyEnvs and ignoreBlocks = true err = api.DestroySystem(true, true) if params.IsCodeNotImplemented(err) { // Fall back to using the client endpoint to destroy the system, // sending the info we were already able to collect. return c.killSystemViaClient(ctx, cfgInfo, systemEnviron, store) } if err != nil { ctx.Infof("Unable to destroy system through the API: %s. Destroying through provider.", err) } return environs.Destroy(systemEnviron, store) }
func (c *addCommand) Run(ctx *cmd.Context) error { client, err := c.getClientAPI() if err != nil { return errors.Trace(err) } defer client.Close() var machineManager MachineManagerAPI if len(c.Disks) > 0 { machineManager, err = c.getMachineManagerAPI() if err != nil { return errors.Trace(err) } defer machineManager.Close() if machineManager.BestAPIVersion() < 1 { return errors.New("cannot add machines with disks: not supported by the API server") } } logger.Infof("load config") var config *config.Config if defaultStore, err := configstore.Default(); err != nil { return err } else if config, err = c.Config(defaultStore, client); err != nil { return err } if c.Placement != nil && c.Placement.Scope == "ssh" { logger.Infof("manual provisioning") args := manual.ProvisionMachineArgs{ Host: c.Placement.Directive, Client: client, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, UpdateBehavior: ¶ms.UpdateBehavior{ config.EnableOSRefreshUpdate(), config.EnableOSUpgrade(), }, } machineId, err := manualProvisioner(args) if err == nil { ctx.Infof("created machine %v", machineId) } return err } logger.Infof("environment provisioning") if c.Placement != nil && c.Placement.Scope == "env-uuid" { c.Placement.Scope = client.EnvironmentUUID() } if c.Placement != nil && c.Placement.Scope == instance.MachineScope { // It does not make sense to add-machine <id>. return fmt.Errorf("machine-id cannot be specified when adding machines") } jobs := []multiwatcher.MachineJob{multiwatcher.JobHostUnits} envVersion, err := envcmd.GetEnvironmentVersion(client) if err != nil { return err } // Servers before 1.21-alpha2 don't have the networker so don't // try to use JobManageNetworking with them. // // In case of MAAS and Joyent JobManageNetworking is not added // to ensure the non-intrusive start of a networker like above // for the manual provisioning. See this related joyent bug // http://pad.lv/1401423 if envVersion.Compare(version.MustParse("1.21-alpha2")) >= 0 && config.Type() != provider.MAAS && config.Type() != provider.Joyent { jobs = append(jobs, multiwatcher.JobManageNetworking) } machineParams := params.AddMachineParams{ Placement: c.Placement, Series: c.Series, Constraints: c.Constraints, Jobs: jobs, Disks: c.Disks, } machines := make([]params.AddMachineParams, c.NumMachines) for i := 0; i < c.NumMachines; i++ { machines[i] = machineParams } var results []params.AddMachinesResult // If storage is specified, we attempt to use a new API on the service facade. if len(c.Disks) > 0 { results, err = machineManager.AddMachines(machines) } else { results, err = client.AddMachines(machines) if params.IsCodeNotImplemented(err) { if c.Placement != nil { containerType, parseErr := instance.ParseContainerType(c.Placement.Scope) if parseErr != nil { // The user specified a non-container placement directive: // return original API not implemented error. return err } machineParams.ContainerType = containerType machineParams.ParentId = c.Placement.Directive machineParams.Placement = nil } logger.Infof( "AddMachinesWithPlacement not supported by the API server, " + "falling back to 1.18 compatibility mode", ) results, err = client.AddMachines1dot18([]params.AddMachineParams{machineParams}) } } if params.IsCodeOperationBlocked(err) { return block.ProcessBlockedError(err, block.BlockChange) } if err != nil { return errors.Trace(err) } errs := []error{} for _, machineInfo := range results { if machineInfo.Error != nil { errs = append(errs, machineInfo.Error) continue } machineId := machineInfo.Machine if names.IsContainerMachine(machineId) { ctx.Infof("created container %v", machineId) } else { ctx.Infof("created machine %v", machineId) } } if len(errs) == 1 { fmt.Fprintf(ctx.Stderr, "failed to create 1 machine\n") return errs[0] } if len(errs) > 1 { fmt.Fprintf(ctx.Stderr, "failed to create %d machines\n", len(errs)) returnErr := []string{} for _, e := range errs { returnErr = append(returnErr, e.Error()) } return errors.New(strings.Join(returnErr, ", ")) } return nil }