launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/api/provisioner/machine.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "fmt" 8 "launchpad.net/errgo/errors" 9 10 "launchpad.net/juju-core/constraints" 11 "launchpad.net/juju-core/instance" 12 "launchpad.net/juju-core/names" 13 "launchpad.net/juju-core/state/api/base" 14 "launchpad.net/juju-core/state/api/params" 15 "launchpad.net/juju-core/state/api/watcher" 16 ) 17 18 // Machine represents a juju machine as seen by the provisioner worker. 19 type Machine struct { 20 tag string 21 life params.Life 22 st *State 23 } 24 25 // Tag returns the machine's tag. 26 func (m *Machine) Tag() string { 27 return m.tag 28 } 29 30 // Id returns the machine id. 31 func (m *Machine) Id() string { 32 _, machineId, err := names.ParseTag(m.tag, names.MachineTagKind) 33 if err != nil { 34 panic(fmt.Sprintf("%q is not a valid machine tag", m.tag)) 35 } 36 return machineId 37 } 38 39 // String returns the machine as a string. 40 func (m *Machine) String() string { 41 return m.Id() 42 } 43 44 // Life returns the machine's lifecycle value. 45 func (m *Machine) Life() params.Life { 46 return m.life 47 } 48 49 // Refresh updates the cached local copy of the machine's data. 50 func (m *Machine) Refresh() error { 51 life, err := m.st.machineLife(m.tag) 52 if err != nil { 53 return base.WrapError(err) 54 } 55 m.life = life 56 return nil 57 } 58 59 // SetStatus sets the status of the machine. 60 func (m *Machine) SetStatus(status params.Status, info string) error { 61 var result params.ErrorResults 62 args := params.SetStatus{ 63 Entities: []params.SetEntityStatus{ 64 {Tag: m.tag, Status: status, Info: info}, 65 }, 66 } 67 err := m.st.caller.Call("Provisioner", "", "SetStatus", args, &result) 68 if err != nil { 69 return base.WrapError(err) 70 } 71 return result.OneError() 72 } 73 74 // Status returns the status of the machine. 75 func (m *Machine) Status() (params.Status, string, error) { 76 var results params.StatusResults 77 args := params.Entities{ 78 Entities: []params.Entity{{Tag: m.tag}}, 79 } 80 err := m.st.caller.Call("Provisioner", "", "Status", args, &results) 81 if err != nil { 82 return "", "", base.WrapError(err) 83 } 84 if len(results.Results) != 1 { 85 return "", "", errors.Newf("expected one result, got %d", len(results.Results)) 86 } 87 result := results.Results[0] 88 if result.Error != nil { 89 return "", "", result.Error 90 } 91 return result.Status, result.Info, nil 92 } 93 94 // Constraints returns the exact constraints that should apply when provisioning 95 // an instance for the machine. 96 func (m *Machine) Constraints() (constraints.Value, error) { 97 nothing := constraints.Value{} 98 var results params.ConstraintsResults 99 args := params.Entities{ 100 Entities: []params.Entity{{Tag: m.tag}}, 101 } 102 err := m.st.caller.Call("Provisioner", "", "Constraints", args, &results) 103 if err != nil { 104 return nothing, base.WrapError(err) 105 } 106 if len(results.Results) != 1 { 107 return nothing, errors.Newf("expected one result, got %d", len(results.Results)) 108 } 109 result := results.Results[0] 110 if result.Error != nil { 111 return nothing, result.Error 112 } 113 return result.Constraints, nil 114 } 115 116 // EnsureDead sets the machine lifecycle to Dead if it is Alive or 117 // Dying. It does nothing otherwise. 118 func (m *Machine) EnsureDead() error { 119 var result params.ErrorResults 120 args := params.Entities{ 121 Entities: []params.Entity{{Tag: m.tag}}, 122 } 123 err := m.st.caller.Call("Provisioner", "", "EnsureDead", args, &result) 124 if err != nil { 125 return base.WrapError(err) 126 } 127 return result.OneError() 128 } 129 130 // Remove removes the machine from state. It will fail if the machine 131 // is not Dead. 132 func (m *Machine) Remove() error { 133 var result params.ErrorResults 134 args := params.Entities{ 135 Entities: []params.Entity{{Tag: m.tag}}, 136 } 137 err := m.st.caller.Call("Provisioner", "", "Remove", args, &result) 138 if err != nil { 139 return base.WrapError(err) 140 } 141 return result.OneError() 142 } 143 144 // Series returns the operating system series running on the machine. 145 // 146 // NOTE: Unlike state.Machine.Series(), this method returns an error 147 // as well, because it needs to do an API call. 148 func (m *Machine) Series() (string, error) { 149 var results params.StringResults 150 args := params.Entities{ 151 Entities: []params.Entity{{Tag: m.tag}}, 152 } 153 err := m.st.caller.Call("Provisioner", "", "Series", args, &results) 154 if err != nil { 155 return "", base.WrapError(err) 156 } 157 if len(results.Results) != 1 { 158 return "", errors.Newf("expected one result, got %d", len(results.Results)) 159 } 160 result := results.Results[0] 161 if result.Error != nil { 162 return "", result.Error 163 } 164 return result.Result, nil 165 } 166 167 // SetProvisioned sets the provider specific machine id, nonce and also metadata for 168 // this machine. Once set, the instance id cannot be changed. 169 func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) error { 170 var result params.ErrorResults 171 args := params.SetProvisioned{ 172 Machines: []params.MachineSetProvisioned{{ 173 Tag: m.tag, 174 InstanceId: id, 175 Nonce: nonce, 176 Characteristics: characteristics, 177 }}, 178 } 179 err := m.st.caller.Call("Provisioner", "", "SetProvisioned", args, &result) 180 if err != nil { 181 return base.WrapError(err) 182 } 183 return result.OneError() 184 } 185 186 // InstanceId returns the provider specific instance id for the 187 // machine or an CodeNotProvisioned error, if not set. 188 func (m *Machine) InstanceId() (instance.Id, error) { 189 var results params.StringResults 190 args := params.Entities{ 191 Entities: []params.Entity{{Tag: m.tag}}, 192 } 193 err := m.st.caller.Call("Provisioner", "", "InstanceId", args, &results) 194 if err != nil { 195 return "", base.WrapError(err) 196 } 197 if len(results.Results) != 1 { 198 return "", errors.Newf("expected one result, got %d", len(results.Results)) 199 } 200 result := results.Results[0] 201 if result.Error != nil { 202 return "", result.Error 203 } 204 return instance.Id(result.Result), nil 205 } 206 207 // SetPassword sets the machine's password. 208 func (m *Machine) SetPassword(password string) error { 209 var result params.ErrorResults 210 args := params.PasswordChanges{ 211 Changes: []params.PasswordChange{ 212 {Tag: m.tag, Password: password}, 213 }, 214 } 215 err := m.st.caller.Call("Provisioner", "", "SetPasswords", args, &result) 216 if err != nil { 217 return base.WrapError(err) 218 } 219 return result.OneError() 220 } 221 222 // WatchContainers returns a StringsWatcher that notifies of changes 223 // to the lifecycles of containers of the specified type on the machine. 224 func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) { 225 if string(ctype) == "" { 226 return nil, errors.New("container type must be specified") 227 } 228 supported := false 229 for _, c := range instance.ContainerTypes { 230 if ctype == c { 231 supported = true 232 break 233 } 234 } 235 if !supported { 236 return nil, errors.Newf("unsupported container type %q", ctype) 237 } 238 var results params.StringsWatchResults 239 args := params.WatchContainers{ 240 Params: []params.WatchContainer{ 241 {MachineTag: m.tag, ContainerType: string(ctype)}, 242 }, 243 } 244 err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results) 245 if err != nil { 246 return nil, base.WrapError(err) 247 } 248 if len(results.Results) != 1 { 249 return nil, errors.Newf("expected one result, got %d", len(results.Results)) 250 } 251 result := results.Results[0] 252 if result.Error != nil { 253 return nil, result.Error 254 } 255 w := watcher.NewStringsWatcher(m.st.caller, result) 256 return w, nil 257 } 258 259 // WatchAllContainers returns a StringsWatcher that notifies of changes 260 // to the lifecycles of all containers on the machine. 261 func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) { 262 var results params.StringsWatchResults 263 args := params.WatchContainers{ 264 Params: []params.WatchContainer{ 265 {MachineTag: m.tag}, 266 }, 267 } 268 err := m.st.caller.Call("Provisioner", "", "WatchContainers", args, &results) 269 if err != nil { 270 return nil, base.WrapError(err) 271 } 272 if len(results.Results) != 1 { 273 return nil, errors.Newf("expected one result, got %d", len(results.Results)) 274 } 275 result := results.Results[0] 276 if result.Error != nil { 277 return nil, result.Error 278 } 279 w := watcher.NewStringsWatcher(m.st.caller, result) 280 return w, nil 281 } 282 283 // SetSupportedContainers updates the list of containers supported by this machine. 284 func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error { 285 var results params.ErrorResults 286 args := params.MachineContainersParams{ 287 Params: []params.MachineContainers{ 288 {MachineTag: m.tag, ContainerTypes: containerTypes}, 289 }, 290 } 291 err := m.st.caller.Call("Provisioner", "", "SetSupportedContainers", args, &results) 292 if err != nil { 293 return base.WrapError(err) 294 } 295 if len(results.Results) != 1 { 296 return errors.Newf("expected one result, got %d", len(results.Results)) 297 } 298 apiError := results.Results[0].Error 299 if apiError != nil { 300 return apiError 301 } 302 return nil 303 } 304 305 // SupportsNoContainers records the fact that this machine doesn't support any containers. 306 func (m *Machine) SupportsNoContainers() error { 307 return m.SetSupportedContainers([]instance.ContainerType{}...) 308 }