github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/addunit.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "errors" 8 "fmt" 9 "regexp" 10 11 "github.com/juju/cmd" 12 "github.com/juju/names" 13 "launchpad.net/gnuflag" 14 15 "github.com/juju/juju/api" 16 "github.com/juju/juju/cmd/envcmd" 17 "github.com/juju/juju/cmd/juju/block" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/provider" 20 ) 21 22 // UnitCommandBase provides support for commands which deploy units. It handles the parsing 23 // and validation of --to and --num-units arguments. 24 type UnitCommandBase struct { 25 ToMachineSpec string 26 NumUnits int 27 } 28 29 func (c *UnitCommandBase) SetFlags(f *gnuflag.FlagSet) { 30 f.IntVar(&c.NumUnits, "num-units", 1, "") 31 f.StringVar(&c.ToMachineSpec, "to", "", "the machine or container to deploy the unit in, bypasses constraints") 32 } 33 34 func (c *UnitCommandBase) Init(args []string) error { 35 if c.NumUnits < 1 { 36 return errors.New("--num-units must be a positive integer") 37 } 38 if c.ToMachineSpec != "" { 39 if c.NumUnits > 1 { 40 return errors.New("cannot use --num-units > 1 with --to") 41 } 42 if !isMachineOrNewContainer(c.ToMachineSpec) { 43 return fmt.Errorf("invalid --to parameter %q", c.ToMachineSpec) 44 } 45 46 } 47 return nil 48 } 49 50 // TODO(anastasiamac) 2014-10-20 Bug#1383116 51 // This exists to provide more context to the user about 52 // why they cannot allocate units to machine 0. Remove 53 // this when the local provider's machine 0 is a container. 54 func (c *UnitCommandBase) checkProvider(conf *config.Config) error { 55 if conf.Type() == provider.Local && c.ToMachineSpec == "0" { 56 return errors.New("machine 0 is the state server for a local environment and cannot host units") 57 } 58 return nil 59 } 60 61 var getClientConfig = func(client *api.Client) (*config.Config, error) { 62 // Separated into a variable for easy overrides 63 attrs, err := client.EnvironmentGet() 64 if err != nil { 65 return nil, err 66 } 67 68 return config.New(config.NoDefaults, attrs) 69 } 70 71 // AddUnitCommand is responsible adding additional units to a service. 72 type AddUnitCommand struct { 73 envcmd.EnvCommandBase 74 UnitCommandBase 75 ServiceName string 76 } 77 78 const addUnitDoc = ` 79 Adding units to an existing service is a way to scale out an environment by 80 deploying more instances of a service. Add-unit must be called on services that 81 have already been deployed via juju deploy. 82 83 By default, services are deployed to newly provisioned machines. Alternatively, 84 service units can be added to a specific existing machine using the --to 85 argument. 86 87 Examples: 88 juju add-unit mysql -n 5 (Add 5 mysql units on 5 new machines) 89 juju add-unit mysql --to 23 (Add a mysql unit to machine 23) 90 juju add-unit mysql --to 24/lxc/3 (Add unit to lxc container 3 on host machine 24) 91 juju add-unit mysql --to lxc:25 (Add unit to a new lxc container on host machine 25) 92 ` 93 94 func (c *AddUnitCommand) Info() *cmd.Info { 95 return &cmd.Info{ 96 Name: "add-unit", 97 Args: "<service name>", 98 Purpose: "add one or more units of an already-deployed service", 99 Doc: addUnitDoc, 100 } 101 } 102 103 func (c *AddUnitCommand) SetFlags(f *gnuflag.FlagSet) { 104 c.UnitCommandBase.SetFlags(f) 105 f.IntVar(&c.NumUnits, "n", 1, "number of service units to add") 106 } 107 108 func (c *AddUnitCommand) Init(args []string) error { 109 switch len(args) { 110 case 1: 111 c.ServiceName = args[0] 112 case 0: 113 return errors.New("no service specified") 114 } 115 if err := cmd.CheckEmpty(args[1:]); err != nil { 116 return err 117 } 118 return c.UnitCommandBase.Init(args) 119 } 120 121 // Run connects to the environment specified on the command line 122 // and calls AddServiceUnits for the given service. 123 func (c *AddUnitCommand) Run(_ *cmd.Context) error { 124 apiclient, err := c.NewAPIClient() 125 if err != nil { 126 return err 127 } 128 defer apiclient.Close() 129 130 conf, err := getClientConfig(apiclient) 131 if err != nil { 132 return err 133 } 134 135 if err := c.checkProvider(conf); err != nil { 136 return err 137 } 138 139 _, err = apiclient.AddServiceUnits(c.ServiceName, c.NumUnits, c.ToMachineSpec) 140 return block.ProcessBlockedError(err, block.BlockChange) 141 } 142 143 const ( 144 deployTarget = "^(" + names.ContainerTypeSnippet + ":)?" + names.MachineSnippet + "$" 145 ) 146 147 var ( 148 validMachineOrNewContainer = regexp.MustCompile(deployTarget) 149 ) 150 151 // isMachineOrNewContainer returns whether spec is a valid machine id 152 // or new container definition. 153 func isMachineOrNewContainer(spec string) bool { 154 return validMachineOrNewContainer.MatchString(spec) 155 }