1
// Copyright 2012-2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
"github.com/juju/names"
13
"launchpad.net/gnuflag"
15
"github.com/juju/juju/cmd/envcmd"
16
"github.com/juju/juju/cmd/juju/block"
17
"github.com/juju/juju/environs/config"
18
"github.com/juju/juju/provider"
21
// UnitCommandBase provides support for commands which deploy units. It handles the parsing
22
// and validation of --to and --num-units arguments.
23
type UnitCommandBase struct {
28
func (c *UnitCommandBase) SetFlags(f *gnuflag.FlagSet) {
29
f.IntVar(&c.NumUnits, "num-units", 1, "")
30
f.StringVar(&c.ToMachineSpec, "to", "", "the machine or container to deploy the unit in, bypasses constraints")
33
func (c *UnitCommandBase) Init(args []string) error {
35
return errors.New("--num-units must be a positive integer")
37
if c.ToMachineSpec != "" {
39
return errors.New("cannot use --num-units > 1 with --to")
41
if !IsMachineOrNewContainer(c.ToMachineSpec) {
42
return fmt.Errorf("invalid --to parameter %q", c.ToMachineSpec)
49
// TODO(anastasiamac) 2014-10-20 Bug#1383116
50
// This exists to provide more context to the user about
51
// why they cannot allocate units to machine 0. Remove
52
// this when the local provider's machine 0 is a container.
53
// TODO(cherylj) Unexport CheckProvider once deploy is moved under service
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")
61
// TODO(cherylj) Unexport GetClientConfig and make it a standard function
62
// once deploy is moved under service
63
var GetClientConfig = func(client ServiceAddUnitAPI) (*config.Config, error) {
64
// Separated into a variable for easy overrides
65
attrs, err := client.EnvironmentGet()
70
return config.New(config.NoDefaults, attrs)
73
// AddUnitCommand is responsible adding additional units to a service.
74
type AddUnitCommand struct {
82
Adding units to an existing service is a way to scale out an environment by
83
deploying more instances of a service. Add-unit must be called on services that
84
have already been deployed via juju deploy.
86
By default, services are deployed to newly provisioned machines. Alternatively,
87
service units can be added to a specific existing machine using the --to
91
juju service add-unit mysql -n 5 (Add 5 mysql units on 5 new machines)
92
juju service add-unit mysql --to 23 (Add a mysql unit to machine 23)
93
juju service add-unit mysql --to 24/lxc/3 (Add unit to lxc container 3 on host machine 24)
94
juju service add-unit mysql --to lxc:25 (Add unit to a new lxc container on host machine 25)
97
func (c *AddUnitCommand) Info() *cmd.Info {
100
Args: "<service name>",
101
Purpose: "add one or more units of an already-deployed service",
106
func (c *AddUnitCommand) SetFlags(f *gnuflag.FlagSet) {
107
c.UnitCommandBase.SetFlags(f)
108
f.IntVar(&c.NumUnits, "n", 1, "number of service units to add")
111
func (c *AddUnitCommand) Init(args []string) error {
114
c.ServiceName = args[0]
116
return errors.New("no service specified")
118
if err := cmd.CheckEmpty(args[1:]); err != nil {
121
return c.UnitCommandBase.Init(args)
124
// ServiceAddUnitAPI defines the methods on the client API
125
// that the service add-unit command calls.
126
type ServiceAddUnitAPI interface {
128
AddServiceUnits(service string, numUnits int, machineSpec string) ([]string, error)
129
EnvironmentGet() (map[string]interface{}, error)
132
func (c *AddUnitCommand) getAPI() (ServiceAddUnitAPI, error) {
136
return c.NewAPIClient()
139
// Run connects to the environment specified on the command line
140
// and calls AddServiceUnits for the given service.
141
func (c *AddUnitCommand) Run(_ *cmd.Context) error {
142
apiclient, err := c.getAPI()
146
defer apiclient.Close()
148
conf, err := GetClientConfig(apiclient)
153
if err := c.CheckProvider(conf); err != nil {
157
_, err = apiclient.AddServiceUnits(c.ServiceName, c.NumUnits, c.ToMachineSpec)
158
return block.ProcessBlockedError(err, block.BlockChange)
161
// deployTarget describes the format a machine or container target must match to be valid.
162
const deployTarget = "^(" + names.ContainerTypeSnippet + ":)?" + names.MachineSnippet + "$"
164
var validMachineOrNewContainer = regexp.MustCompile(deployTarget)
166
// IsMachineOrNewContainer returns whether spec is a valid machine id
167
// or new container definition.
168
func IsMachineOrNewContainer(spec string) bool {
169
return validMachineOrNewContainer.MatchString(spec)