github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 "gopkg.in/juju/charm.v5" 13 14 "github.com/juju/juju/constraints" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/instance" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/storage" 19 ) 20 21 // DeployServiceParams contains the arguments required to deploy the referenced 22 // charm. 23 type DeployServiceParams struct { 24 ServiceName string 25 ServiceOwner string 26 Charm *state.Charm 27 ConfigSettings charm.Settings 28 Constraints constraints.Value 29 NumUnits int 30 // ToMachineSpec is either: 31 // - an existing machine/container id eg "1" or "1/lxc/2" 32 // - a new container on an existing machine eg "lxc:1" 33 // Use string to avoid ambiguity around machine 0. 34 ToMachineSpec string 35 // Networks holds a list of networks to required to start on boot. 36 Networks []string 37 Storage map[string]storage.Constraints 38 } 39 40 // DeployService takes a charm and various parameters and deploys it. 41 func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { 42 if args.NumUnits > 1 && args.ToMachineSpec != "" { 43 return nil, fmt.Errorf("cannot use --num-units with --to") 44 } 45 settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) 46 if err != nil { 47 return nil, err 48 } 49 if args.Charm.Meta().Subordinate { 50 if args.NumUnits != 0 || args.ToMachineSpec != "" { 51 return nil, fmt.Errorf("subordinate service must be deployed without units") 52 } 53 if !constraints.IsEmpty(&args.Constraints) { 54 return nil, fmt.Errorf("subordinate service must be deployed without constraints") 55 } 56 } 57 if args.ServiceOwner == "" { 58 env, err := st.Environment() 59 if err != nil { 60 return nil, errors.Trace(err) 61 } 62 args.ServiceOwner = env.Owner().String() 63 } 64 // TODO(fwereade): transactional State.AddService including settings, constraints 65 // (minimumUnitCount, initialMachineIds?). 66 if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { 67 conf, err := st.EnvironConfig() 68 if err != nil { 69 return nil, err 70 } 71 env, err := environs.New(conf) 72 if err != nil { 73 return nil, err 74 } 75 if _, ok := environs.SupportsNetworking(env); !ok { 76 return nil, fmt.Errorf("cannot deploy with networks: not suppored by the environment") 77 } 78 } 79 service, err := st.AddService( 80 args.ServiceName, 81 args.ServiceOwner, 82 args.Charm, 83 args.Networks, 84 stateStorageConstraints(args.Storage), 85 ) 86 if err != nil { 87 return nil, err 88 } 89 if len(settings) > 0 { 90 if err := service.UpdateConfigSettings(settings); err != nil { 91 return nil, err 92 } 93 } 94 if args.Charm.Meta().Subordinate { 95 return service, nil 96 } 97 if !constraints.IsEmpty(&args.Constraints) { 98 if err := service.SetConstraints(args.Constraints); err != nil { 99 return nil, err 100 } 101 } 102 if args.NumUnits > 0 { 103 if _, err := AddUnits(st, service, args.NumUnits, args.ToMachineSpec); err != nil { 104 return nil, err 105 } 106 } 107 return service, nil 108 } 109 110 // AddUnits starts n units of the given service and allocates machines 111 // to them as necessary. 112 func AddUnits(st *state.State, svc *state.Service, n int, machineIdSpec string) ([]*state.Unit, error) { 113 units := make([]*state.Unit, n) 114 // Hard code for now till we implement a different approach. 115 policy := state.AssignCleanEmpty 116 // All units should have the same networks as the service. 117 networks, err := svc.Networks() 118 if err != nil { 119 return nil, fmt.Errorf("cannot get service %q networks: %v", svc.Name(), err) 120 } 121 // TODO what do we do if we fail half-way through this process? 122 for i := 0; i < n; i++ { 123 unit, err := svc.AddUnit() 124 if err != nil { 125 return nil, fmt.Errorf("cannot add unit %d/%d to service %q: %v", i+1, n, svc.Name(), err) 126 } 127 if machineIdSpec != "" { 128 if n != 1 { 129 return nil, fmt.Errorf("cannot add multiple units of service %q to a single machine", svc.Name()) 130 } 131 // machineIdSpec may be an existing machine or container, eg 3/lxc/2 132 // or a new container on a machine, eg lxc:3 133 mid := machineIdSpec 134 var containerType instance.ContainerType 135 specParts := strings.SplitN(machineIdSpec, ":", 2) 136 if len(specParts) > 1 { 137 firstPart := specParts[0] 138 var err error 139 if containerType, err = instance.ParseContainerType(firstPart); err == nil { 140 mid = specParts[1] 141 } else { 142 mid = machineIdSpec 143 } 144 } 145 if !names.IsValidMachine(mid) { 146 return nil, fmt.Errorf("invalid force machine id %q", mid) 147 } 148 var unitCons *constraints.Value 149 unitCons, err = unit.Constraints() 150 if err != nil { 151 return nil, err 152 } 153 154 var err error 155 var m *state.Machine 156 // If a container is to be used, create it. 157 if containerType != "" { 158 // Create the new machine marked as dirty so that 159 // nothing else will grab it before we assign the unit to it. 160 template := state.MachineTemplate{ 161 Series: unit.Series(), 162 Jobs: []state.MachineJob{state.JobHostUnits}, 163 Dirty: true, 164 Constraints: *unitCons, 165 RequestedNetworks: networks, 166 } 167 m, err = st.AddMachineInsideMachine(template, mid, containerType) 168 } else { 169 m, err = st.Machine(mid) 170 } 171 if err != nil { 172 return nil, fmt.Errorf("cannot assign unit %q to machine: %v", unit.Name(), err) 173 } 174 err = unit.AssignToMachine(m) 175 176 if err != nil { 177 return nil, err 178 } 179 } else if err := st.AssignUnit(unit, policy); err != nil { 180 return nil, err 181 } 182 units[i] = unit 183 } 184 return units, nil 185 } 186 187 func stateStorageConstraints(cons map[string]storage.Constraints) map[string]state.StorageConstraints { 188 result := make(map[string]state.StorageConstraints) 189 for name, cons := range cons { 190 result[name] = state.StorageConstraints{ 191 Pool: cons.Pool, 192 Size: cons.Size, 193 Count: cons.Count, 194 } 195 } 196 return result 197 }