github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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  
     9  	"github.com/juju/names"
    10  	"launchpad.net/gnuflag"
    11  
    12  	"github.com/juju/juju/cmd"
    13  	"github.com/juju/juju/cmd/envcmd"
    14  	"github.com/juju/juju/constraints"
    15  	"github.com/juju/juju/environs/manual"
    16  	"github.com/juju/juju/instance"
    17  	"github.com/juju/juju/juju"
    18  	"github.com/juju/juju/state/api/params"
    19  )
    20  
    21  // sshHostPrefix is the prefix for a machine to be "manually provisioned".
    22  const sshHostPrefix = "ssh:"
    23  
    24  var addMachineDoc = `
    25  
    26  If no container is specified, a new machine will be
    27  provisioned.  If a container is specified, a new machine will be provisioned
    28  with that container.
    29  
    30  To add a container to an existing machine, use the <container>:<machinenumber>
    31  format.
    32  
    33  When adding a new machine, you may specify constraints for the machine to be
    34  provisioned.  Constraints cannot be combined with deploying a container to an
    35  existing machine.
    36  
    37  Currently, the only supported container type is lxc.
    38  
    39  Machines are created in a clean state and ready to have units deployed.
    40  
    41  This command also supports manual provisioning of existing machines via SSH. The
    42  target machine must be able to communicate with the API server, and be able to
    43  access the environment storage.
    44  
    45  Examples:
    46     juju add-machine                      (starts a new machine)
    47     juju add-machine lxc                  (starts a new machine with an lxc container)
    48     juju add-machine lxc:4                (starts a new lxc container on machine 4)
    49     juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM)
    50     juju add-machine ssh:user@10.10.0.3   (manually provisions a machine with ssh)
    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  	envcmd.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  	// Placement is passed verbatim to the API, to be parsed and evaluated server-side.
    64  	Placement *instance.Placement
    65  }
    66  
    67  func (c *AddMachineCommand) Info() *cmd.Info {
    68  	return &cmd.Info{
    69  		Name:    "add-machine",
    70  		Args:    "[<container>:machine | <container> | ssh:[user@]host]",
    71  		Purpose: "start a new, empty machine and optionally a container, or add a container to a machine",
    72  		Doc:     addMachineDoc,
    73  	}
    74  }
    75  
    76  func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) {
    77  	f.StringVar(&c.Series, "series", "", "the charm series")
    78  	f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "additional machine constraints")
    79  }
    80  
    81  func (c *AddMachineCommand) Init(args []string) error {
    82  	if c.Constraints.Container != nil {
    83  		return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container)
    84  	}
    85  	placement, err := cmd.ZeroOrOneArgs(args)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	c.Placement, err = instance.ParsePlacement(placement)
    90  	if err == instance.ErrPlacementScopeMissing {
    91  		placement = c.EnvName + ":" + placement
    92  		c.Placement, err = instance.ParsePlacement(placement)
    93  	}
    94  	if err != nil {
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  func (c *AddMachineCommand) Run(ctx *cmd.Context) error {
   101  	if c.Placement != nil && c.Placement.Scope == "ssh" {
   102  		args := manual.ProvisionMachineArgs{
   103  			Host:    c.Placement.Directive,
   104  			EnvName: c.EnvName,
   105  			Stdin:   ctx.Stdin,
   106  			Stdout:  ctx.Stdout,
   107  			Stderr:  ctx.Stderr,
   108  		}
   109  		_, err := manual.ProvisionMachine(args)
   110  		return err
   111  	}
   112  
   113  	client, err := juju.NewAPIClientFromName(c.EnvName)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	defer client.Close()
   118  
   119  	if c.Placement != nil && c.Placement.Scope == instance.MachineScope {
   120  		// It does not make sense to add-machine <id>.
   121  		return fmt.Errorf("machine-id cannot be specified when adding machines")
   122  	}
   123  
   124  	machineParams := params.AddMachineParams{
   125  		Placement:   c.Placement,
   126  		Series:      c.Series,
   127  		Constraints: c.Constraints,
   128  		Jobs:        []params.MachineJob{params.JobHostUnits},
   129  	}
   130  	results, err := client.AddMachines([]params.AddMachineParams{machineParams})
   131  	if params.IsCodeNotImplemented(err) {
   132  		if c.Placement != nil {
   133  			containerType, parseErr := instance.ParseContainerType(c.Placement.Scope)
   134  			if parseErr != nil {
   135  				// The user specified a non-container placement directive:
   136  				// return original API not implemented error.
   137  				return err
   138  			}
   139  			machineParams.ContainerType = containerType
   140  			machineParams.ParentId = c.Placement.Directive
   141  			machineParams.Placement = nil
   142  		}
   143  		logger.Infof(
   144  			"AddMachinesWithPlacement not supported by the API server, " +
   145  				"falling back to 1.18 compatibility mode",
   146  		)
   147  		results, err = client.AddMachines1dot18([]params.AddMachineParams{machineParams})
   148  	}
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	// Currently, only one machine is added, but in future there may be several added in one call.
   154  	machineInfo := results[0]
   155  	if machineInfo.Error != nil {
   156  		return machineInfo.Error
   157  	}
   158  	machineId := machineInfo.Machine
   159  
   160  	if names.IsContainerMachine(machineId) {
   161  		ctx.Infof("created container %v", machineId)
   162  	} else {
   163  		ctx.Infof("created machine %v", machineId)
   164  	}
   165  	return nil
   166  }