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