func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { if p.ParentId != "" && p.ContainerType == "" { return nil, fmt.Errorf("parent machine specified without container type") } if p.ContainerType != "" && p.Placement != nil { return nil, fmt.Errorf("container type and placement are mutually exclusive") } if p.Placement != nil { // Extract container type and parent from container placement directives. containerType, err := instance.ParseContainerType(p.Placement.Scope) if err == nil { p.ContainerType = containerType p.ParentId = p.Placement.Directive p.Placement = nil } } if p.ContainerType != "" || p.Placement != nil { // Guard against dubious client by making sure that // the following attributes can only be set when we're // not using placement. p.InstanceId = "" p.Nonce = "" p.HardwareCharacteristics = instance.HardwareCharacteristics{} p.Addrs = nil } if p.Series == "" { conf, err := c.api.state.EnvironConfig() if err != nil { return nil, err } p.Series = config.PreferredSeries(conf) } var placementDirective string if p.Placement != nil { env, err := c.api.state.Environment() if err != nil { return nil, err } if p.Placement.Scope != env.Name() { return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope) } placementDirective = p.Placement.Directive } jobs, err := stateJobs(p.Jobs) if err != nil { return nil, err } template := state.MachineTemplate{ Series: p.Series, Constraints: p.Constraints, InstanceId: p.InstanceId, Jobs: jobs, Nonce: p.Nonce, HardwareCharacteristics: p.HardwareCharacteristics, Addresses: p.Addrs, Placement: placementDirective, } if p.ContainerType == "" { return c.api.state.AddOneMachine(template) } if p.ParentId != "" { return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) } return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType) }
func (c *AddMachineCommand) Run(ctx *cmd.Context) error { if c.Placement != nil && c.Placement.Scope == "ssh" { args := manual.ProvisionMachineArgs{ Host: c.Placement.Directive, EnvName: c.EnvName, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, } _, err := manual.ProvisionMachine(args) return err } client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() 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") } machineParams := params.AddMachineParams{ Placement: c.Placement, Series: c.Series, Constraints: c.Constraints, Jobs: []params.MachineJob{params.JobHostUnits}, } results, err := client.AddMachines([]params.AddMachineParams{machineParams}) 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 err != nil { return err } // Currently, only one machine is added, but in future there may be several added in one call. machineInfo := results[0] if machineInfo.Error != nil { return machineInfo.Error } machineId := machineInfo.Machine if names.IsContainerMachine(machineId) { ctx.Infof("created container %v", machineId) } else { ctx.Infof("created machine %v", machineId) } return nil }