github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/juju/deploy.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package juju 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "launchpad.net/juju-core/charm" 12 "launchpad.net/juju-core/constraints" 13 "launchpad.net/juju-core/instance" 14 "launchpad.net/juju-core/names" 15 "launchpad.net/juju-core/state" 16 ) 17 18 // DeployServiceParams contains the arguments required to deploy the referenced 19 // charm. 20 type DeployServiceParams struct { 21 ServiceName string 22 Charm *state.Charm 23 ConfigSettings charm.Settings 24 Constraints constraints.Value 25 NumUnits int 26 // ToMachineSpec is either: 27 // - an existing machine/container id eg "1" or "1/lxc/2" 28 // - a new container on an existing machine eg "lxc:1" 29 // Use string to avoid ambiguity around machine 0. 30 ToMachineSpec string 31 } 32 33 // DeployService takes a charm and various parameters and deploys it. 34 func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { 35 if args.NumUnits > 1 && args.ToMachineSpec != "" { 36 return nil, errors.New("cannot use --num-units with --to") 37 } 38 settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) 39 if err != nil { 40 return nil, err 41 } 42 if args.Charm.Meta().Subordinate { 43 if args.NumUnits != 0 || args.ToMachineSpec != "" { 44 return nil, fmt.Errorf("subordinate service must be deployed without units") 45 } 46 if !constraints.IsEmpty(&args.Constraints) { 47 return nil, fmt.Errorf("subordinate service must be deployed without constraints") 48 } 49 } 50 // TODO(fwereade): transactional State.AddService including settings, constraints 51 // (minimumUnitCount, initialMachineIds?). 52 service, err := st.AddService(args.ServiceName, "user-admin", args.Charm) 53 if err != nil { 54 return nil, err 55 } 56 if len(settings) > 0 { 57 if err := service.UpdateConfigSettings(settings); err != nil { 58 return nil, err 59 } 60 } 61 if args.Charm.Meta().Subordinate { 62 return service, nil 63 } 64 if !constraints.IsEmpty(&args.Constraints) { 65 if err := service.SetConstraints(args.Constraints); err != nil { 66 return nil, err 67 } 68 } 69 if args.NumUnits > 0 { 70 if _, err := AddUnits(st, service, args.NumUnits, args.ToMachineSpec); err != nil { 71 return nil, err 72 } 73 } 74 return service, nil 75 } 76 77 // AddUnits starts n units of the given service and allocates machines 78 // to them as necessary. 79 func AddUnits(st *state.State, svc *state.Service, n int, machineIdSpec string) ([]*state.Unit, error) { 80 units := make([]*state.Unit, n) 81 // Hard code for now till we implement a different approach. 82 policy := state.AssignCleanEmpty 83 // TODO what do we do if we fail half-way through this process? 84 for i := 0; i < n; i++ { 85 unit, err := svc.AddUnit() 86 if err != nil { 87 return nil, fmt.Errorf("cannot add unit %d/%d to service %q: %v", i+1, n, svc.Name(), err) 88 } 89 if machineIdSpec != "" { 90 if n != 1 { 91 return nil, fmt.Errorf("cannot add multiple units of service %q to a single machine", svc.Name()) 92 } 93 // machineIdSpec may be an existing machine or container, eg 3/lxc/2 94 // or a new container on a machine, eg lxc:3 95 mid := machineIdSpec 96 var containerType instance.ContainerType 97 specParts := strings.SplitN(machineIdSpec, ":", 2) 98 if len(specParts) > 1 { 99 firstPart := specParts[0] 100 var err error 101 if containerType, err = instance.ParseContainerType(firstPart); err == nil { 102 mid = specParts[1] 103 } else { 104 mid = machineIdSpec 105 } 106 } 107 if !names.IsMachine(mid) { 108 return nil, fmt.Errorf("invalid force machine id %q", mid) 109 } 110 111 var err error 112 var m *state.Machine 113 // If a container is to be used, create it. 114 if containerType != "" { 115 // Create the new machine marked as dirty so that 116 // nothing else will grab it before we assign the unit to it. 117 template := state.MachineTemplate{ 118 Series: unit.Series(), 119 Jobs: []state.MachineJob{state.JobHostUnits}, 120 Dirty: true, 121 } 122 m, err = st.AddMachineInsideMachine(template, mid, containerType) 123 } else { 124 m, err = st.Machine(mid) 125 } 126 if err != nil { 127 return nil, fmt.Errorf("cannot assign unit %q to machine: %v", unit.Name(), err) 128 } 129 err = unit.AssignToMachine(m) 130 131 if err != nil { 132 return nil, err 133 } 134 } else if err := st.AssignUnit(unit, policy); err != nil { 135 return nil, err 136 } 137 units[i] = unit 138 } 139 return units, nil 140 }