// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SubnetAPI, ctx *cmd.Context) error { if c.CIDR.Id() != "" && c.RawCIDR != c.CIDR.Id() { ctx.Infof( "WARNING: using CIDR %q instead of the incorrectly specified %q.", c.CIDR.Id(), c.RawCIDR, ) } // Add the existing subnet. err := api.AddSubnet(c.CIDR, network.Id(c.ProviderId), c.Space, c.Zones) // TODO(dimitern): Change this once the API returns a concrete error. if err != nil && strings.Contains(err.Error(), "multiple subnets with") { // Special case: multiple subnets with the same CIDR exist ctx.Infof("ERROR: %v.", err) return nil } else if err != nil { if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a subnet") } return errors.Annotatef(err, "cannot add subnet") } if c.ProviderId != "" { ctx.Infof("added subnet with ProviderId %q in space %q", c.ProviderId, c.Space.Id()) } else { ctx.Infof("added subnet with CIDR %q in space %q", c.CIDR.Id(), c.Space.Id()) } 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 }) }
func (c *addRelationCommand) Run(ctx *cmd.Context) error { client, err := c.newAPIFunc() if err != nil { return err } defer client.Close() _, err = client.AddRelation(c.Endpoints...) if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a relation") } return block.ProcessBlockedError(err, block.BlockChange) }
// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) (err error) { api, err := c.newAPIFunc() if err != nil { return err } defer api.Close() storages := c.createStorageAddParams() results, err := api.AddToUnit(storages) if err != nil { if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add storage") } return err } var added []string var failures []string // If there was a unit-related error, then all storages will get the same error. // We want to collapse these - no need to repeat the same things ad nauseam. collapsedFailures := set.NewStrings() for i, one := range results { us := storages[i] if one.Error != nil { failures = append(failures, fmt.Sprintf(fail, us.StorageName, one.Error)) collapsedFailures.Add(one.Error.Error()) continue } added = append(added, fmt.Sprintf(success, us.StorageName)) } if len(added) > 0 { fmt.Fprintln(ctx.Stdout, strings.Join(added, newline)) } if len(failures) == len(storages) { // If we managed to collapse, then display these instead of the whole list. if len(collapsedFailures) < len(failures) { for _, one := range collapsedFailures.SortedValues() { fmt.Fprintln(ctx.Stderr, one) } return cmd.ErrSilent } } if len(failures) > 0 { fmt.Fprintln(ctx.Stderr, strings.Join(failures, newline)) return cmd.ErrSilent } return nil }
func (c *addModelCommand) Run(ctx *cmd.Context) error { api, err := c.newAPIRoot() if err != nil { return errors.Annotate(err, "opening API connection") } defer api.Close() store := c.ClientStore() controllerName := c.ControllerName() accountDetails, err := store.AccountDetails(controllerName) if err != nil { return errors.Trace(err) } modelOwner := accountDetails.User if c.Owner != "" { if !names.IsValidUser(c.Owner) { return errors.Errorf("%q is not a valid user name", c.Owner) } modelOwner = names.NewUserTag(c.Owner).Id() } forUserSuffix := fmt.Sprintf(" for user '%s'", names.NewUserTag(modelOwner).Name()) attrs, err := c.getConfigValues(ctx) if err != nil { return errors.Trace(err) } cloudClient := c.newCloudAPI(api) var cloudTag names.CloudTag var cloud jujucloud.Cloud var cloudRegion string if c.CloudRegion != "" { cloudTag, cloud, cloudRegion, err = c.getCloudRegion(cloudClient) if err != nil { return errors.Trace(err) } } // If the user has specified a credential, then we will upload it if // it doesn't already exist in the controller, and it exists locally. var credentialTag names.CloudCredentialTag if c.CredentialName != "" { var err error if c.CloudRegion == "" { if cloudTag, cloud, err = defaultCloud(cloudClient); err != nil { return errors.Trace(err) } } credentialTag, err = c.maybeUploadCredential(ctx, cloudClient, cloudTag, cloudRegion, cloud, modelOwner) if err != nil { return errors.Trace(err) } } addModelClient := c.newAddModelAPI(api) model, err := addModelClient.CreateModel(c.Name, modelOwner, cloudTag.Id(), cloudRegion, credentialTag, attrs) if err != nil { if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a model") } return errors.Trace(err) } messageFormat := "Added '%s' model" messageArgs := []interface{}{c.Name} if modelOwner == accountDetails.User { controllerName := c.ControllerName() if err := store.UpdateModel(controllerName, c.Name, jujuclient.ModelDetails{ model.UUID, }); err != nil { return errors.Trace(err) } if err := store.SetCurrentModel(controllerName, c.Name); err != nil { return errors.Trace(err) } } if c.CloudRegion != "" || model.CloudRegion != "" { // The user explicitly requested a cloud/region, // or the cloud supports multiple regions. Whichever // the case, tell the user which cloud/region the // model was deployed to. cloudRegion := model.Cloud if model.CloudRegion != "" { cloudRegion += "/" + model.CloudRegion } messageFormat += " on %s" messageArgs = append(messageArgs, cloudRegion) } if model.CloudCredential != "" { tag := names.NewCloudCredentialTag(model.CloudCredential) credentialName := tag.Name() if tag.Owner().Id() != modelOwner { credentialName = fmt.Sprintf("%s/%s", tag.Owner().Id(), credentialName) } messageFormat += " with credential '%s'" messageArgs = append(messageArgs, credentialName) } messageFormat += forUserSuffix // "Added '<model>' model [on <cloud>/<region>] [with credential '<credential>'] for user '<user namePart>'" ctx.Infof(messageFormat, messageArgs...) if _, ok := attrs[config.AuthorizedKeysKey]; !ok { // It is not an error to have no authorized-keys when adding a // model, though this should never happen since we generate // juju-specific SSH keys. ctx.Infof(` No SSH authorized-keys were found. You must use "juju add-ssh-key" before "juju ssh", "juju scp", or "juju debug-hooks" will work.`) } return nil }
func (c *addCommand) Run(ctx *cmd.Context) error { var err error c.Constraints, err = common.ParseConstraints(ctx, c.ConstraintsStr) if err != nil { return err } 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") modelConfigClient, err := c.getModelConfigAPI() if err != nil { return errors.Trace(err) } defer modelConfigClient.Close() configAttrs, err := modelConfigClient.ModelGet() if err != nil { if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a machine to this model") } return errors.Trace(err) } config, err := config.New(config.NoDefaults, configAttrs) if err != nil { return errors.Trace(err) } if c.Placement != nil && c.Placement.Scope == "ssh" { logger.Infof("manual provisioning") authKeys, err := common.ReadAuthorizedKeys(ctx, "") if err != nil { return errors.Annotate(err, "reading authorized-keys") } args := manual.ProvisionMachineArgs{ Host: c.Placement.Directive, Client: client, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, AuthorizedKeys: authKeys, UpdateBehavior: ¶ms.UpdateBehavior{ config.EnableOSRefreshUpdate(), config.EnableOSUpgrade(), }, } machineId, err := manualProvisioner(args) if err == nil { ctx.Infof("created machine %v", machineId) } return err } logger.Infof("model provisioning") if c.Placement != nil && c.Placement.Scope == "model-uuid" { uuid, ok := client.ModelUUID() if !ok { return errors.New("API connection is controller-only (should never happen)") } c.Placement.Scope = uuid } if c.Placement != nil && c.Placement.Scope == instance.MachineScope { // It does not make sense to add-machine <id>. return errors.Errorf("machine-id cannot be specified when adding machines") } jobs := []multiwatcher.MachineJob{multiwatcher.JobHostUnits} 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.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.Fprint(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 }
// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) error { api := c.api if api == nil { var err error api, err = c.NewUserManagerAPIClient() if err != nil { return errors.Trace(err) } defer api.Close() } // Add a user without a password. This will generate a temporary // secret key, which we'll print out for the user to supply to // "juju register". _, secretKey, err := api.AddUser(c.User, c.DisplayName, "") if err != nil { if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a user") } return block.ProcessBlockedError(err, block.BlockChange) } displayName := c.User if c.DisplayName != "" { displayName = fmt.Sprintf("%s (%s)", c.DisplayName, c.User) } // Generate the base64-encoded string for the user to pass to // "juju register". We marshal the information using ASN.1 // to keep the size down, since we need to encode binary data. controllerDetails, err := c.ClientStore().ControllerByName(c.ControllerName()) if err != nil { return errors.Trace(err) } registrationInfo := jujuclient.RegistrationInfo{ User: c.User, Addrs: controllerDetails.APIEndpoints, SecretKey: secretKey, ControllerName: c.ControllerName(), } registrationData, err := asn1.Marshal(registrationInfo) if err != nil { return errors.Trace(err) } // Use URLEncoding so we don't get + or / in the string, // and pad with zero bytes so we don't get =; this all // makes it easier to copy & paste in a terminal. // // The embedded ASN.1 data is length-encoded, so the // padding will not complicate decoding. remainder := len(registrationData) % 3 for remainder > 0 { registrationData = append(registrationData, 0) remainder-- } base64RegistrationData := base64.URLEncoding.EncodeToString( registrationData, ) fmt.Fprintf(ctx.Stdout, "User %q added\n", displayName) fmt.Fprintf(ctx.Stdout, "Please send this command to %v:\n", c.User) fmt.Fprintf(ctx.Stdout, " juju register %s\n", base64RegistrationData, ) fmt.Fprintf(ctx.Stdout, ` %q has not been granted access to any models. You can use "juju grant" to grant access. `, displayName) return nil }