launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/juju/addmachine.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "strings" 9 10 "launchpad.net/gnuflag" 11 12 "launchpad.net/juju-core/cmd" 13 "launchpad.net/juju-core/constraints" 14 "launchpad.net/juju-core/environs/manual" 15 "launchpad.net/juju-core/instance" 16 "launchpad.net/juju-core/juju" 17 "launchpad.net/juju-core/names" 18 "launchpad.net/juju-core/state" 19 "launchpad.net/juju-core/state/api/params" 20 ) 21 22 // sshHostPrefix is the prefix for a machine to be "manually provisioned". 23 const sshHostPrefix = "ssh:" 24 25 var addMachineDoc = ` 26 27 If no container is specified, a new machine will be 28 provisioned. If a container is specified, a new machine will be provisioned 29 with that container. 30 31 To add a container to an existing machine, use the <container>:<machinenumber> 32 format. 33 34 When adding a new machine, you may specify constraints for the machine to be 35 provisioned. Constraints cannot be combined with deploying a container to an 36 existing machine. 37 38 Currently, the only supported container type is lxc. 39 40 Machines are created in a clean state and ready to have units deployed. 41 42 This command also supports manual provisioning of existing machines via SSH. The 43 target machine must be able to communicate with the API server, and be able to 44 access the environment storage. 45 46 Examples: 47 juju add-machine (starts a new machine) 48 juju add-machine lxc (starts a new machine with an lxc container) 49 juju add-machine lxc:4 (starts a new lxc container on machine 4) 50 juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM) 51 52 See Also: 53 juju help constraints 54 ` 55 56 // AddMachineCommand starts a new machine and registers it in the environment. 57 type AddMachineCommand struct { 58 cmd.EnvCommandBase 59 // If specified, use this series, else use the environment default-series 60 Series string 61 // If specified, these constraints are merged with those already in the environment. 62 Constraints constraints.Value 63 MachineId string 64 ContainerType instance.ContainerType 65 SSHHost string 66 } 67 68 func (c *AddMachineCommand) Info() *cmd.Info { 69 return &cmd.Info{ 70 Name: "add-machine", 71 Args: "[<container>:machine | <container> | ssh:[user@]host]", 72 Purpose: "start a new, empty machine and optionally a container, or add a container to a machine", 73 Doc: addMachineDoc, 74 } 75 } 76 77 func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) { 78 c.EnvCommandBase.SetFlags(f) 79 f.StringVar(&c.Series, "series", "", "the charm series") 80 f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "additional machine constraints") 81 } 82 83 func (c *AddMachineCommand) Init(args []string) error { 84 if c.Constraints.Container != nil { 85 return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container) 86 } 87 containerSpec, err := cmd.ZeroOrOneArgs(args) 88 if err != nil { 89 return err 90 } 91 if containerSpec == "" { 92 return nil 93 } 94 if strings.HasPrefix(containerSpec, sshHostPrefix) { 95 c.SSHHost = containerSpec[len(sshHostPrefix):] 96 } else { 97 // container arg can either be 'type:machine' or 'type' 98 if c.ContainerType, err = instance.ParseContainerType(containerSpec); err != nil { 99 if names.IsMachine(containerSpec) || !cmd.IsMachineOrNewContainer(containerSpec) { 100 return fmt.Errorf("malformed container argument %q", containerSpec) 101 } 102 sep := strings.Index(containerSpec, ":") 103 c.MachineId = containerSpec[sep+1:] 104 c.ContainerType, err = instance.ParseContainerType(containerSpec[:sep]) 105 } 106 } 107 return err 108 } 109 110 // addMachine1dot16 runs Client.AddMachines using a direct DB connection to maintain 111 // compatibility with an API server running 1.16 or older (when AddMachines 112 // was not available). This fallback can be removed when we no longer maintain 113 // 1.16 compatibility. 114 // This was copied directly from the code in AddMachineCommand.Run in 1.16 115 func (c *AddMachineCommand) addMachine1dot16() (string, error) { 116 conn, err := juju.NewConnFromName(c.EnvName) 117 if err != nil { 118 return "", err 119 } 120 defer conn.Close() 121 122 series := c.Series 123 if series == "" { 124 conf, err := conn.State.EnvironConfig() 125 if err != nil { 126 return "", err 127 } 128 series = conf.DefaultSeries() 129 } 130 template := state.MachineTemplate{ 131 Series: series, 132 Constraints: c.Constraints, 133 Jobs: []state.MachineJob{state.JobHostUnits}, 134 } 135 var m *state.Machine 136 switch { 137 case c.ContainerType == "": 138 m, err = conn.State.AddOneMachine(template) 139 case c.MachineId != "": 140 m, err = conn.State.AddMachineInsideMachine(template, c.MachineId, c.ContainerType) 141 default: 142 m, err = conn.State.AddMachineInsideNewMachine(template, template, c.ContainerType) 143 } 144 if err != nil { 145 return "", err 146 } 147 return m.String(), err 148 } 149 150 func (c *AddMachineCommand) Run(ctx *cmd.Context) error { 151 if c.SSHHost != "" { 152 args := manual.ProvisionMachineArgs{ 153 Host: c.SSHHost, 154 EnvName: c.EnvName, 155 Stdin: ctx.Stdin, 156 Stdout: ctx.Stdout, 157 Stderr: ctx.Stderr, 158 } 159 _, err := manual.ProvisionMachine(args) 160 return err 161 } 162 163 client, err := juju.NewAPIClientFromName(c.EnvName) 164 if err != nil { 165 return err 166 } 167 defer client.Close() 168 169 machineParams := params.AddMachineParams{ 170 ParentId: c.MachineId, 171 ContainerType: c.ContainerType, 172 Series: c.Series, 173 Constraints: c.Constraints, 174 Jobs: []params.MachineJob{params.JobHostUnits}, 175 } 176 results, err := client.AddMachines([]params.AddMachineParams{machineParams}) 177 var machineId string 178 if params.IsCodeNotImplemented(err) { 179 logger.Infof("AddMachines not supported by the API server, " + 180 "falling back to 1.16 compatibility mode (direct DB access)") 181 machineId, err = c.addMachine1dot16() 182 } else if err != nil { 183 return err 184 } else { 185 // Currently, only one machine is added, but in future there may be several added in one call. 186 machineInfo := results[0] 187 var machineErr *params.Error 188 machineId, machineErr = machineInfo.Machine, machineInfo.Error 189 if machineErr != nil { 190 err = machineErr 191 } 192 } 193 if err != nil { 194 return err 195 } 196 if c.ContainerType == "" { 197 logger.Infof("created machine %v", machineId) 198 } else { 199 logger.Infof("created %q container on machine %v", c.ContainerType, machineId) 200 } 201 return nil 202 }