// ensureUserFriendlyErrorLog ensures that error will be logged and displayed // in a user-friendly manner with readable and digestable error message. func (c *destroyCommand) ensureUserFriendlyErrorLog(destroyErr error, ctx *cmd.Context, api destroyControllerAPI) error { if destroyErr == nil { return nil } if params.IsCodeOperationBlocked(destroyErr) { logger.Errorf(destroyControllerBlockedMsg) if api != nil { models, err := api.ListBlockedModels() out := &bytes.Buffer{} if err == nil { var info interface{} info, err = block.FormatModelBlockInfo(models) if err != nil { return errors.Trace(err) } err = block.FormatTabularBlockedModels(out, info) } if err != nil { logger.Errorf("Unable to list models: %s", err) return cmd.ErrSilent } ctx.Infof(out.String()) } return cmd.ErrSilent } if params.IsCodeHasHostedModels(destroyErr) { return destroyErr } logger.Errorf(stdFailureMsg, c.ControllerName()) return destroyErr }
func (c *setDefaultRegionCommand) Run(ctxt *cmd.Context) error { cloudDetails, err := cloudOrProvider(c.cloud, jujucloud.CloudByName) if err != nil { return err } if len(cloudDetails.Regions) == 0 { return errors.Errorf("cloud %s has no regions", c.cloud) } if !hasRegion(c.region, cloudDetails.Regions) { var regionNames []string for _, r := range cloudDetails.Regions { regionNames = append(regionNames, r.Name) } return errors.NewNotValid( nil, fmt.Sprintf("region %q for cloud %s not valid, valid regions are %s", c.region, c.cloud, strings.Join(regionNames, ", "))) } var cred *jujucloud.CloudCredential cred, err = c.store.CredentialForCloud(c.cloud) if errors.IsNotFound(err) { cred = &jujucloud.CloudCredential{} } else if err != nil { return err } cred.DefaultRegion = c.region if err := c.store.UpdateCredential(c.cloud, *cred); err != nil { return err } ctxt.Infof("Default region in %s set to %q.", c.cloud, c.region) return nil }
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, environ.Name(), 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) }
// Run implements Command.Run. func (c *changePasswordCommand) Run(ctx *cmd.Context) error { if c.api == nil { api, err := c.NewUserManagerAPIClient() if err != nil { return errors.Trace(err) } c.api = api defer c.api.Close() } password, err := c.generateOrReadPassword(ctx, c.Generate) if err != nil { return errors.Trace(err) } var writer EnvironInfoCredsWriter var creds configstore.APICredentials if c.User == "" { // We get the creds writer before changing the password just to // minimise the things that could go wrong after changing the password // in the server. if c.writer == nil { writer, err = c.ConnectionInfo() if err != nil { return errors.Trace(err) } } else { writer = c.writer } creds = writer.APICredentials() } else { creds.User = c.User } oldPassword := creds.Password creds.Password = password if err = c.api.SetPassword(creds.User, password); err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if c.User != "" { return writeServerFile(c, ctx, c.User, password, c.OutPath) } writer.SetAPICredentials(creds) if err := writer.Write(); err != nil { logger.Errorf("updating the cached credentials failed, reverting to original password") setErr := c.api.SetPassword(creds.User, oldPassword) if setErr != nil { logger.Errorf("failed to set password back, you will need to edit your environments file by hand to specify the password: %q", password) return errors.Annotate(setErr, "failed to set password back") } return errors.Annotate(err, "failed to write new password to environments file") } ctx.Infof("Your password has been updated.") return nil }
// Run implements Command.Run. func (c *listKeysCommand) Run(context *cmd.Context) error { client, err := c.NewKeyManagerClient() if err != nil { return err } defer client.Close() mode := ssh.Fingerprints if c.showFullKey { mode = ssh.FullKeys } // TODO(alexisb) - currently keys are global which is not ideal. // keymanager needs to be updated to allow keys per user c.user = "******" results, err := client.ListKeys(mode, c.user) if err != nil { return err } result := results[0] if result.Error != nil { return result.Error } if len(result.Result) == 0 { context.Infof("No keys to display.") return nil } fmt.Fprintf(context.Stdout, "Keys used in model: %s\n", c.ConnectionName()) fmt.Fprintln(context.Stdout, strings.Join(result.Result, "\n")) return nil }
// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error { // Prepare a nicer message and proper arguments to use in case // there are not CIDRs given. var subnetIds []string msgSuffix := "no subnets" if !c.CIDRs.IsEmpty() { subnetIds = c.CIDRs.SortedValues() msgSuffix = fmt.Sprintf("subnets %s", strings.Join(subnetIds, ", ")) } // Add the new space. // TODO(dimitern): Accept --public|--private and pass it here. err := api.AddSpace(c.Name, subnetIds, true) if err != nil { if errors.IsNotSupported(err) { ctx.Infof("cannot add space %q: %v", c.Name, err) } if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a space") } return errors.Annotatef(err, "cannot add space %q", c.Name) } ctx.Infof("added space %q with %s", c.Name, msgSuffix) return nil }) }
// addCharmViaAPI calls the appropriate client API calls to add the // given charm URL to state. Also displays the charm URL of the added // charm on stdout. func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charm.Repository) (*charm.URL, error) { if curl.Revision < 0 { latest, err := charm.Latest(repo, curl) if err != nil { return nil, err } curl = curl.WithRevision(latest) } switch curl.Schema { case "local": ch, err := repo.Get(curl) if err != nil { return nil, err } stateCurl, err := client.AddLocalCharm(curl, ch) if err != nil { return nil, err } curl = stateCurl case "cs": err := client.AddCharm(curl) if err != nil { return nil, err } default: return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema) } ctx.Infof("Added charm %q to the environment.", curl) return curl, nil }
// Run implements Command.Run. func (c *listAgreementsCommand) Run(ctx *cmd.Context) error { client, err := c.BakeryClient() if err != nil { return errors.Annotate(err, "failed to create an http client") } apiClient, err := newClient(client) if err != nil { return errors.Annotate(err, "failed to create a terms API client") } agreements, err := apiClient.GetUsersAgreements() if err != nil { return errors.Annotate(err, "failed to list user agreements") } if len(agreements) == 0 { ctx.Infof("No agreements to display.") return nil } err = c.out.Write(ctx, agreements) if err != nil { return errors.Mask(err) } return nil }
func writeServerFile(endpointProvider EndpointProvider, ctx *cmd.Context, username, password, outPath string) error { outPath = ctx.AbsPath(outPath) endpoint, err := endpointProvider.ConnectionEndpoint() if err != nil { return errors.Trace(err) } if !names.IsValidUser(username) { return errors.Errorf("%q is not a valid username", username) } outputInfo := modelcmd.ServerFile{ Username: username, Password: password, Addresses: endpoint.Addresses, CACert: endpoint.CACert, } yaml, err := cmd.FormatYaml(outputInfo) if err != nil { return errors.Trace(err) } if err := ioutil.WriteFile(outPath, yaml, 0644); err != nil { return errors.Trace(err) } serverFileNotify(outPath) ctx.Infof("server file written to %s", outPath) return nil }
func (c *ImageMetadataCommand) Run(context *cmd.Context) error { if err := c.setParams(context); err != nil { return err } out := context.Stdout im := &imagemetadata.ImageMetadata{ Id: c.ImageId, Arch: c.Arch, } cloudSpec := simplestreams.CloudSpec{ Region: c.Region, Endpoint: c.Endpoint, } targetStorage, err := filestorage.NewFileStorageWriter(c.Dir) if err != nil { return err } err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage) if err != nil { return fmt.Errorf("image metadata files could not be created: %v", err) } dir := context.AbsPath(c.Dir) dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1") fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir)) return nil }
// addCharmViaAPI calls the appropriate client API calls to add the // given charm URL to state. For non-public charm URLs, this function also // handles the macaroon authorization process using the given csClient. // The resulting charm URL of the added charm is displayed on stdout. func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charmrepo.Interface, csclient *csClient) (*charm.URL, error) { switch curl.Schema { case "local": ch, err := repo.Get(curl) if err != nil { return nil, err } stateCurl, err := client.AddLocalCharm(curl, ch) if err != nil { return nil, err } curl = stateCurl case "cs": if err := client.AddCharm(curl); err != nil { if !params.IsCodeUnauthorized(err) { return nil, errors.Mask(err) } m, err := csclient.authorize(curl) if err != nil { return nil, errors.Mask(err) } if err := client.AddCharmWithAuthorization(curl, m); err != nil { return nil, errors.Mask(err) } } default: return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema) } ctx.Infof("Added charm %q to the environment.", curl) return curl, nil }
func (c *ListCommand) Run(ctx *cmd.Context) error { apiclient, err := c.newAPIClient(c) if err != nil { return errors.Trace(err) } defer apiclient.Close() payloads, err := apiclient.ListFull(c.patterns...) if err != nil { if payloads == nil { // List call completely failed; there is nothing to report. return errors.Trace(err) } // Display any error, but continue to print info if some was returned. fmt.Fprintf(ctx.Stderr, "%v\n", err) } if len(payloads) == 0 { ctx.Infof("No payloads to display.") return nil } // Note that we do not worry about c.CompatVersion for payloads... formatter := newListFormatter(payloads) formatted := formatter.format() return c.out.Write(ctx, formatted) }
func (c *ShowServiceCommand) formatUnitResources(ctx *cmd.Context, unit, service string, sr resource.ServiceResources) error { if len(sr.UnitResources) == 0 { ctx.Infof(noResources) return nil } if c.details { formatted, err := detailedResources(unit, sr) if err != nil { return errors.Trace(err) } return c.out.Write(ctx, FormattedUnitDetails(formatted)) } resources, err := unitResources(unit, service, sr) if err != nil { return errors.Trace(err) } res := make([]FormattedUnitResource, len(resources)) for i, r := range resources { res[i] = FormattedUnitResource(FormatSvcResource(r)) } return c.out.Write(ctx, res) }
// waitForAgentInitialisation polls the bootstrapped state server with a read-only // command which will fail until the state server is fully initialised. // TODO(wallyworld) - add a bespoke command to maybe the admin facade for this purpose. func (c *BootstrapCommand) waitForAgentInitialisation(ctx *cmd.Context) (err error) { attempts := utils.AttemptStrategy{ Min: bootstrapReadyPollCount, Delay: bootstrapReadyPollDelay, } var client block.BlockListAPI for attempt := attempts.Start(); attempt.Next(); { client, err = blockAPI(&c.EnvCommandBase) if err != nil { return err } _, err = client.List() client.Close() if err == nil { ctx.Infof("Bootstrap complete") return nil } if strings.Contains(err.Error(), apiserver.UpgradeInProgressError.Error()) { ctx.Infof("Waiting for API to become available") continue } return err } return err }
// ensureUserFriendlyErrorLog ensures that error will be logged and displayed // in a user-friendly manner with readable and digestable error message. func (c *destroyCommand) ensureUserFriendlyErrorLog(destroyErr error, ctx *cmd.Context, api destroyControllerAPI) error { if destroyErr == nil { return nil } if params.IsCodeOperationBlocked(destroyErr) { logger.Errorf(destroyControllerBlockedMsg) if api != nil { models, err := api.ListBlockedModels() var bytes []byte if err == nil { bytes, err = formatTabularBlockedModels(models) } if err != nil { logger.Errorf("Unable to list blocked models: %s", err) return cmd.ErrSilent } ctx.Infof(string(bytes)) } return cmd.ErrSilent } if params.IsCodeHasHostedModels(destroyErr) { return destroyErr } logger.Errorf(stdFailureMsg, c.ControllerName()) return destroyErr }
func logSwitch(ctx *cmd.Context, oldName string, newName *string) { if *newName == oldName { ctx.Infof("%s (no change)", oldName) } else { ctx.Infof("%s -> %s", oldName, *newName) } }
func (c *MigrateCommand) exportModel(ctx *cmd.Context, st *state.State) error { ctx.Infof("\nexport %s", c.modelUUID) // first make sure the uuid is good enough tag := names.NewModelTag(c.modelUUID) _, err := st.GetModel(tag) if err != nil { return errors.Trace(err) } modelState, err := st.ForModel(tag) if err != nil { return errors.Trace(err) } defer modelState.Close() model, err := modelState.Export() if err != nil { return errors.Trace(err) } bytes, err := yaml.Marshal(model) if err != nil { return errors.Trace(err) } ctx.Stdout.Write(bytes) return nil }
// Run implements Command.Run. func (c *ListCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SubnetAPI, ctx *cmd.Context) error { // Validate space and/or zone, if given to display a nicer error // message. // Get the list of subnets, filtering them as requested. subnets, err := api.ListSubnets(c.spaceTag, c.ZoneName) if err != nil { return errors.Annotate(err, "cannot list subnets") } // Display a nicer message in case no subnets were found. if len(subnets) == 0 { if c.SpaceName != "" || c.ZoneName != "" { ctx.Infof("no subnets found matching requested criteria") } else { ctx.Infof("no subnets to display") } return nil } // Construct the output list for displaying with the chosen // format. result := formattedList{ Subnets: make(map[string]formattedSubnet), } for _, sub := range subnets { subResult := formattedSubnet{ ProviderId: sub.ProviderId, Zones: sub.Zones, } // Use the CIDR to determine the subnet type. if ip, _, err := net.ParseCIDR(sub.CIDR); err != nil { return errors.Errorf("subnet %q has invalid CIDR", sub.CIDR) } else if ip.To4() != nil { subResult.Type = typeIPv4 } else if ip.To16() != nil { subResult.Type = typeIPv6 } // Space must be valid, but verify anyway. spaceTag, err := names.ParseSpaceTag(sub.SpaceTag) if err != nil { return errors.Annotatef(err, "subnet %q has invalid space", sub.CIDR) } subResult.Space = spaceTag.Id() // Display correct status according to the life cycle value. switch sub.Life { case params.Alive: subResult.Status = statusInUse case params.Dying, params.Dead: subResult.Status = statusTerminating } result.Subnets[sub.CIDR] = subResult } return c.out.Write(ctx, result) }) }
// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) error { if c.api == nil { api, err := c.NewUserManagerAPIClient() if err != nil { return errors.Trace(err) } c.api = api defer c.api.Close() } password, err := utils.RandomPassword() if err != nil { return errors.Annotate(err, "failed to generate random password") } randomPasswordNotify(password) if _, err := c.api.AddUser(c.User, c.DisplayName, password); err != nil { return block.ProcessBlockedError(err, block.BlockChange) } displayName := c.User if c.DisplayName != "" { displayName = fmt.Sprintf("%s (%s)", c.DisplayName, c.User) } ctx.Infof("user %q added", displayName) return writeServerFile(c, ctx, c.User, password, c.OutPath) }
// ensureUserFriendlyErrorLog ensures that error will be logged and displayed // in a user-friendly manner with readable and digestable error message. func (c *destroyCommand) ensureUserFriendlyErrorLog(destroyErr error, ctx *cmd.Context, api destroyControllerAPI) error { if destroyErr == nil { return nil } if params.IsCodeOperationBlocked(destroyErr) { logger.Errorf(`there are blocks preventing controller destruction To remove all blocks in the controller, please run: juju controller remove-blocks `) if api != nil { envs, err := api.ListBlockedEnvironments() var bytes []byte if err == nil { bytes, err = formatTabularBlockedEnvironments(envs) } if err != nil { logger.Errorf("Unable to list blocked environments: %s", err) return cmd.ErrSilent } ctx.Infof(string(bytes)) } return cmd.ErrSilent } logger.Errorf(stdFailureMsg, c.EnvName()) return destroyErr }
// 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 controller info storage") } cfgInfo, err := store.ReadInfo(c.ModelName()) if err != nil { return errors.Annotate(err, "cannot read controller info") } // Verify that we're destroying a controller apiEndpoint := cfgInfo.APIEndpoint() if apiEndpoint.ServerUUID != "" && apiEndpoint.ModelUUID != apiEndpoint.ServerUUID { return errors.Errorf("%q is not a controller; use juju model destroy to destroy it", c.ModelName()) } if !c.assumeYes { if err = confirmDestruction(ctx, c.ModelName()); err != nil { return err } } // Attempt to connect to the API. If we can't, fail the destroy. Users will // need to use the controller kill command if we can't connect. api, err := c.getControllerAPI() if err != nil { return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot connect to API"), ctx, nil) } defer api.Close() // Obtain bootstrap / controller environ information controllerEnviron, err := c.getControllerEnviron(cfgInfo, api) if err != nil { return errors.Annotate(err, "cannot obtain bootstrap information") } // Attempt to destroy the controller. err = api.DestroyController(c.destroyEnvs) if err != nil { return c.ensureUserFriendlyErrorLog(errors.Annotate(err, "cannot destroy controller"), ctx, api) } ctx.Infof("Destroying controller %q", c.ModelName()) if c.destroyEnvs { ctx.Infof("Waiting for hosted model resources to be reclaimed.") updateStatus := newTimedStatusUpdater(ctx, api, apiEndpoint.ModelUUID) for ctrStatus, envsStatus := updateStatus(0); hasUnDeadEnvirons(envsStatus); ctrStatus, envsStatus = updateStatus(2 * time.Second) { ctx.Infof(fmtCtrStatus(ctrStatus)) for _, envStatus := range envsStatus { ctx.Verbosef(fmtEnvStatus(envStatus)) } } ctx.Infof("All hosted models reclaimed, cleaning up controller machines") } return environs.Destroy(controllerEnviron, store) }
func (c *listCredentialsCommand) Run(ctxt *cmd.Context) error { var credentials map[string]jujucloud.CloudCredential credentials, err := c.store.AllCredentials() if err != nil && !errors.IsNotFound(err) { return err } if c.cloudName != "" { for cloudName := range credentials { if cloudName != c.cloudName { delete(credentials, cloudName) } } } // Find local cloud names. personalClouds, err := c.personalClouds() if err != nil { return err } var personalCloudNames []string for name := range personalClouds { personalCloudNames = append(personalCloudNames, name) } displayCredentials := make(map[string]CloudCredential) var missingClouds []string for cloudName, cred := range credentials { if !c.showSecrets { if err := c.removeSecrets(cloudName, &cred); err != nil { if errors.IsNotValid(err) { missingClouds = append(missingClouds, cloudName) continue } return errors.Annotatef(err, "removing secrets from credentials for cloud %v", cloudName) } } displayCredential := CloudCredential{ DefaultCredential: cred.DefaultCredential, DefaultRegion: cred.DefaultRegion, } if len(cred.AuthCredentials) != 0 { displayCredential.Credentials = make(map[string]Credential, len(cred.AuthCredentials)) for credName, credDetails := range cred.AuthCredentials { displayCredential.Credentials[credName] = Credential{ string(credDetails.AuthType()), credDetails.Attributes(), credDetails.Revoked, credDetails.Label, } } } displayCredentials[cloudName] = displayCredential } if c.out.Name() == "tabular" && len(missingClouds) > 0 { fmt.Fprintf(ctxt.GetStdout(), "The following clouds have been removed and are omitted from the results to avoid leaking secrets.\n"+ "Run with --show-secrets to display these clouds' credentials: %v\n\n", strings.Join(missingClouds, ", ")) } return c.out.Write(ctxt, credentialsMap{displayCredentials}) }
// Run connects to the specified environment and starts the charm // upgrade process. func (c *upgradeCharmCommand) Run(ctx *cmd.Context) error { client, err := c.NewAPIClient() if err != nil { return err } defer client.Close() oldURL, err := client.ServiceGetCharmURL(c.ServiceName) if err != nil { return err } conf, err := service.GetClientConfig(client) if err != nil { return errors.Trace(err) } var newRef *charm.Reference if c.SwitchURL != "" { newRef, err = charm.ParseReference(c.SwitchURL) if err != nil { return err } } else { // No new URL specified, but revision might have been. newRef = oldURL.WithRevision(c.Revision).Reference() } httpClient, err := c.HTTPClient() if err != nil { return errors.Trace(err) } csClient := newCharmStoreClient(httpClient) newURL, repo, err := resolveCharmStoreEntityURL(newRef.String(), csClient.params, ctx.AbsPath(c.RepoPath), conf) if err != nil { return errors.Trace(err) } // If no explicit revision was set with either SwitchURL // or Revision flags, discover the latest. if *newURL == *oldURL { if newRef.Revision != -1 { return fmt.Errorf("already running specified charm %q", newURL) } if newURL.Schema == "cs" { // No point in trying to upgrade a charm store charm when // we just determined that's the latest revision // available. return fmt.Errorf("already running latest charm %q", newURL) } } addedURL, err := addCharmViaAPI(client, newURL, repo, csClient) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } ctx.Infof("Added charm %q to the environment.", addedURL) return block.ProcessBlockedError(client.ServiceSetCharm(c.ServiceName, addedURL.String(), c.Force), block.BlockChange) }
// Run implements Command.Run. func (c *ChangePasswordCommand) Run(ctx *cmd.Context) error { var err error c.Password, err = c.generateOrReadPassword(ctx, c.Generate) if err != nil { return errors.Trace(err) } var credsWriter EnvironInfoCredsWriter var creds configstore.APICredentials if c.User == "" { // We get the creds writer before changing the password just to // minimise the things that could go wrong after changing the password // in the server. credsWriter, err = getEnvironInfoWriter(c) if err != nil { return errors.Trace(err) } creds, err = getConnectionCredentials(c) if err != nil { return errors.Trace(err) } } else { creds.User = c.User } client, err := getChangePasswordAPI(c) if err != nil { return err } defer client.Close() oldPassword := creds.Password creds.Password = c.Password err = client.SetPassword(creds.User, c.Password) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } if c.User != "" { return c.writeEnvironmentFile(ctx) } credsWriter.SetAPICredentials(creds) if err := credsWriter.Write(); err != nil { logger.Errorf("updating the environments file failed, reverting to original password") setErr := client.SetPassword(creds.User, oldPassword) if setErr != nil { logger.Errorf("failed to set password back, you will need to edit your environments file by hand to specify the password: %q", c.Password) return errors.Annotate(setErr, "failed to set password back") } return errors.Annotate(err, "failed to write new password to environments file") } ctx.Infof("Your password has been updated.") return nil }
// Run connects to the specified environment and starts the charm // upgrade process. func (c *upgradeCharmCommand) Run(ctx *cmd.Context) error { client, err := c.NewAPIClient() if err != nil { return err } defer client.Close() serviceClient, err := c.newServiceAPIClient() if err != nil { return err } oldURL, err := serviceClient.GetCharmURL(c.ServiceName) if err != nil { return err } newRef := c.SwitchURL if newRef == "" { newRef = c.CharmPath } if c.SwitchURL == "" && c.CharmPath == "" { // No new URL specified, but revision might have been. newRef = oldURL.WithRevision(c.Revision).String() } bakeryClient, err := c.BakeryClient() if err != nil { return errors.Trace(err) } csClient := newCharmStoreClient(bakeryClient).WithChannel(c.Channel) conf, err := getClientConfig(client) if err != nil { return errors.Trace(err) } resolver := newCharmURLResolver(conf, csClient, ctx.AbsPath(c.RepoPath)) chID, csMac, err := c.addCharm(oldURL, newRef, client, resolver) if err != nil { return block.ProcessBlockedError(err, block.BlockChange) } ctx.Infof("Added charm %q to the model.", chID.URL) ids, err := c.upgradeResources(client, chID, csMac) if err != nil { return errors.Trace(err) } cfg := apiservice.SetCharmConfig{ ServiceName: c.ServiceName, CharmID: chID, ForceSeries: c.ForceSeries, ForceUnits: c.ForceUnits, ResourceIDs: ids, } return block.ProcessBlockedError(serviceClient.SetCharm(cfg), block.BlockChange) }
// destroyPreparedEnviron destroys the environment and logs an error if it fails. func destroyPreparedEnviron(ctx *cmd.Context, env environs.Environ, store configstore.Storage, err *error, action string) { if *err == nil { return } ctx.Infof("%s failed, destroying environment", action) if err := environs.Destroy(env, store); err != nil { logger.Errorf("%s failed, and the environment could not be destroyed: %v", action, err) } }
func (c *signMetadataCommand) Run(context *cmd.Context) error { loggo.RegisterWriter("signmetadata", cmd.NewCommandLogWriter("juju.plugins.metadata", context.Stdout, context.Stderr), loggo.INFO) defer loggo.RemoveWriter("signmetadata") keyData, err := ioutil.ReadFile(c.keyFile) if err != nil { return err } dir := context.AbsPath(c.dir) return process(dir, string(keyData), c.passphrase) }
// Run implements Command.Run. func (c *renameCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error { err := api.RenameSpace(c.Name, c.NewName) if err != nil { return errors.Annotatef(err, "cannot rename space %q", c.Name) } ctx.Infof("renamed space %q to %q", c.Name, c.NewName) return nil }) }
func destroyPreparedEnvironProductionFunc( ctx *cmd.Context, env environs.Environ, store configstore.Storage, action string, ) { ctx.Infof("%s failed, destroying environment", action) if err := environs.Destroy(env, store); err != nil { logger.Errorf("the environment could not be destroyed: %v", err) } }
func destroyEnvInfoProductionFunc( ctx *cmd.Context, cfgName string, store configstore.Storage, action string, ) { ctx.Infof("%s failed, cleaning up the environment.", action) if err := environs.DestroyInfo(cfgName, store); err != nil { logger.Errorf("the environment jenv file could not be cleaned up: %v", err) } }