github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/errors" 10 "github.com/juju/version" 11 "gopkg.in/juju/charm.v6" 12 "gopkg.in/juju/names.v2" 13 14 apiwatcher "github.com/juju/juju/api/watcher" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/core/status" 18 "github.com/juju/juju/core/watcher" 19 ) 20 21 //go:generate mockgen -package mocks -destination mocks/machine_mock.go github.com/juju/juju/api/provisioner MachineProvisioner 22 23 // MachineProvisioner defines what provisioner needs to provision a machine. 24 type MachineProvisioner interface { 25 // Tag returns the machine's tag. 26 Tag() names.Tag 27 28 // ModelAgentVersion returns the agent version the machine's model is currently 29 // running or an error. 30 ModelAgentVersion() (*version.Number, error) 31 32 // MachineTag returns the identifier for the machine as the most specific type. 33 MachineTag() names.MachineTag 34 35 // Id returns the machine id. 36 Id() string 37 38 // String returns the machine as a string. 39 String() string 40 41 // Life returns the machine's lifecycle value. 42 Life() params.Life 43 44 // Refresh updates the cached local copy of the machine's data. 45 Refresh() error 46 47 // ProvisioningInfo returns the information required to provision a machine. 48 ProvisioningInfo() (*params.ProvisioningInfo, error) 49 50 // SetInstanceStatus sets the status for the provider instance. 51 SetInstanceStatus(status status.Status, message string, data map[string]interface{}) error 52 53 // InstanceStatus returns the status of the provider instance. 54 InstanceStatus() (status.Status, string, error) 55 56 // SetStatus sets the status of the machine. 57 SetStatus(status status.Status, info string, data map[string]interface{}) error 58 59 // Status returns the status of the machine. 60 Status() (status.Status, string, error) 61 62 // EnsureDead sets the machine lifecycle to Dead if it is Alive or 63 // Dying. It does nothing otherwise. 64 EnsureDead() error 65 66 // Remove removes the machine from state. It will fail if the machine 67 // is not Dead. 68 Remove() error 69 70 // MarkForRemoval indicates that the machine is ready to have any 71 // provider-level resources cleaned up and be removed. 72 MarkForRemoval() error 73 74 // AvailabilityZone returns an underlying provider's availability zone 75 // for a machine. 76 AvailabilityZone() (string, error) 77 78 // DistributionGroup returns a slice of instance.Ids 79 // that belong to the same distribution group as this 80 // Machine. The provisioner may use this information 81 // to distribute instances for high availability. 82 DistributionGroup() ([]instance.Id, error) 83 84 // SetInstanceInfo sets the provider specific instance id, nonce, metadata, 85 // network config for this machine. Once set, the instance id cannot be changed. 86 SetInstanceInfo( 87 id instance.Id, displayName string, nonce string, characteristics *instance.HardwareCharacteristics, 88 networkConfig []params.NetworkConfig, volumes []params.Volume, 89 volumeAttachments map[string]params.VolumeAttachmentInfo, charmProfiles []string, 90 ) error 91 92 // InstanceId returns the provider specific instance id for the 93 // machine or an CodeNotProvisioned error, if not set. 94 InstanceId() (instance.Id, error) 95 96 // KeepInstance returns the value of the keep-instance 97 // for the machine. 98 KeepInstance() (bool, error) 99 100 // SetPassword sets the machine's password. 101 SetPassword(password string) error 102 103 // WatchContainers returns a StringsWatcher that notifies of changes 104 // to the lifecycles of containers of the specified type on the machine. 105 WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) 106 107 // WatchAllContainers returns a StringsWatcher that notifies of changes 108 // to the lifecycles of all containers on the machine. 109 WatchAllContainers() (watcher.StringsWatcher, error) 110 111 // SetSupportedContainers updates the list of containers supported by this machine. 112 SetSupportedContainers(containerTypes ...instance.ContainerType) error 113 114 // SupportsNoContainers records the fact that this machine doesn't support any containers. 115 SupportsNoContainers() error 116 117 // WatchContainers returns a StringsWatcher that notifies of 118 // changes to the upgrade charm profile charm url for all 119 // containers of the specified type on the machine. 120 WatchContainersCharmProfiles(ctype instance.ContainerType) (watcher.StringsWatcher, error) 121 122 // CharmProfileChangeInfo retrieves the info necessary to change a charm 123 // profile used by a machine. 124 CharmProfileChangeInfo() (CharmProfileChangeInfo, error) 125 126 // SetCharmProfiles records the given slice of charm profile names. 127 SetCharmProfiles([]string) error 128 129 // SetUpgradeCharmProfileComplete recorded that the result of updating 130 // the machine's charm profile(s) 131 SetUpgradeCharmProfileComplete(string) error 132 133 // RemoveUpgradeCharmProfileData completely removes the instance charm profile 134 // data for a machine, even if the machine is dead. 135 RemoveUpgradeCharmProfileData() error 136 } 137 138 // Machine represents a juju machine as seen by the provisioner worker. 139 type Machine struct { 140 tag names.MachineTag 141 life params.Life 142 st *State 143 } 144 145 // Tag implements MachineProvisioner.Tag. 146 func (m *Machine) Tag() names.Tag { 147 return m.tag 148 } 149 150 // ModelAgentVersion implements MachineProvisioner.ModelAgentVersion. 151 func (m *Machine) ModelAgentVersion() (*version.Number, error) { 152 mc, err := m.st.ModelConfig() 153 if err != nil { 154 return nil, errors.Trace(err) 155 } 156 157 if v, ok := mc.AgentVersion(); ok { 158 return &v, nil 159 } 160 161 return nil, errors.New("failed to get model's agent version.") 162 } 163 164 // MachineTag implements MachineProvisioner.MachineTag. 165 func (m *Machine) MachineTag() names.MachineTag { 166 return m.tag 167 } 168 169 // Id implements MachineProvisioner.Id. 170 func (m *Machine) Id() string { 171 return m.tag.Id() 172 } 173 174 // String implements MachineProvisioner.String. 175 func (m *Machine) String() string { 176 return m.Id() 177 } 178 179 // Life implements MachineProvisioner.. 180 func (m *Machine) Life() params.Life { 181 return m.life 182 } 183 184 // Refresh implements MachineProvisioner.Refresh. 185 func (m *Machine) Refresh() error { 186 life, err := m.st.machineLife(m.tag) 187 if err != nil { 188 return err 189 } 190 m.life = life 191 return nil 192 } 193 194 // ProvisioningInfo implements MachineProvisioner.ProvisioningInfo. 195 func (m *Machine) ProvisioningInfo() (*params.ProvisioningInfo, error) { 196 var results params.ProvisioningInfoResults 197 args := params.Entities{Entities: []params.Entity{{m.tag.String()}}} 198 err := m.st.facade.FacadeCall("ProvisioningInfo", args, &results) 199 if err != nil { 200 return nil, err 201 } 202 if len(results.Results) != 1 { 203 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 204 } 205 result := results.Results[0] 206 if result.Error != nil { 207 return nil, result.Error 208 } 209 return result.Result, nil 210 } 211 212 // SetInstanceStatus implements MachineProvisioner.SetInstanceStatus. 213 func (m *Machine) SetInstanceStatus(status status.Status, message string, data map[string]interface{}) error { 214 var result params.ErrorResults 215 args := params.SetStatus{Entities: []params.EntityStatusArgs{ 216 {Tag: m.tag.String(), Status: status.String(), Info: message, Data: data}, 217 }} 218 err := m.st.facade.FacadeCall("SetInstanceStatus", args, &result) 219 if err != nil { 220 return err 221 } 222 return result.OneError() 223 } 224 225 // InstanceStatus implements MachineProvisioner.InstanceStatus. 226 func (m *Machine) InstanceStatus() (status.Status, string, error) { 227 var results params.StatusResults 228 args := params.Entities{Entities: []params.Entity{ 229 {Tag: m.tag.String()}, 230 }} 231 err := m.st.facade.FacadeCall("InstanceStatus", args, &results) 232 if err != nil { 233 return "", "", err 234 } 235 if len(results.Results) != 1 { 236 return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 237 } 238 result := results.Results[0] 239 if result.Error != nil { 240 return "", "", result.Error 241 } 242 // TODO(perrito666) add status validation. 243 return status.Status(result.Status), result.Info, nil 244 } 245 246 // SetStatus implements MachineProvisioner.SetStatus. 247 func (m *Machine) SetStatus(status status.Status, info string, data map[string]interface{}) error { 248 var result params.ErrorResults 249 args := params.SetStatus{ 250 Entities: []params.EntityStatusArgs{ 251 {Tag: m.tag.String(), Status: status.String(), Info: info, Data: data}, 252 }, 253 } 254 err := m.st.facade.FacadeCall("SetStatus", args, &result) 255 if err != nil { 256 return err 257 } 258 return result.OneError() 259 } 260 261 // Status implements MachineProvisioner.Status. 262 func (m *Machine) Status() (status.Status, string, error) { 263 var results params.StatusResults 264 args := params.Entities{ 265 Entities: []params.Entity{{Tag: m.tag.String()}}, 266 } 267 err := m.st.facade.FacadeCall("Status", args, &results) 268 if err != nil { 269 return "", "", err 270 } 271 if len(results.Results) != 1 { 272 return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 273 } 274 result := results.Results[0] 275 if result.Error != nil { 276 return "", "", result.Error 277 } 278 // TODO(perrito666) add status validation. 279 return status.Status(result.Status), result.Info, nil 280 } 281 282 // EnsureDead implements MachineProvisioner.EnsureDead. 283 func (m *Machine) EnsureDead() error { 284 var result params.ErrorResults 285 args := params.Entities{ 286 Entities: []params.Entity{{Tag: m.tag.String()}}, 287 } 288 err := m.st.facade.FacadeCall("EnsureDead", args, &result) 289 if err != nil { 290 return err 291 } 292 return result.OneError() 293 } 294 295 // Remove implements MachineProvisioner.Remove. 296 func (m *Machine) Remove() error { 297 var result params.ErrorResults 298 args := params.Entities{ 299 Entities: []params.Entity{{Tag: m.tag.String()}}, 300 } 301 err := m.st.facade.FacadeCall("Remove", args, &result) 302 if err != nil { 303 return err 304 } 305 return result.OneError() 306 } 307 308 // MarkForRemoval implements MachineProvisioner.MarkForRemoval. 309 func (m *Machine) MarkForRemoval() error { 310 var result params.ErrorResults 311 args := params.Entities{ 312 Entities: []params.Entity{{Tag: m.tag.String()}}, 313 } 314 err := m.st.facade.FacadeCall("MarkMachinesForRemoval", args, &result) 315 if err != nil { 316 return err 317 } 318 return result.OneError() 319 } 320 321 // AvailabilityZone implements MachineProvisioner.AvailabilityZone. 322 func (m *Machine) AvailabilityZone() (string, error) { 323 var results params.StringResults 324 args := params.Entities{ 325 Entities: []params.Entity{{Tag: m.tag.String()}}, 326 } 327 err := m.st.facade.FacadeCall("AvailabilityZone", args, &results) 328 if err != nil { 329 return "", err 330 } 331 if len(results.Results) != 1 { 332 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 333 } 334 result := results.Results[0] 335 if result.Error != nil { 336 return "", result.Error 337 } 338 return result.Result, nil 339 } 340 341 // DistributionGroup implements MachineProvisioner.DistributionGroup. 342 func (m *Machine) DistributionGroup() ([]instance.Id, error) { 343 var results params.DistributionGroupResults 344 args := params.Entities{ 345 Entities: []params.Entity{{Tag: m.tag.String()}}, 346 } 347 err := m.st.facade.FacadeCall("DistributionGroup", args, &results) 348 if err != nil { 349 return nil, err 350 } 351 if len(results.Results) != 1 { 352 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 353 } 354 result := results.Results[0] 355 if result.Error != nil { 356 return nil, result.Error 357 } 358 return result.Result, nil 359 } 360 361 // SetInstanceInfo implements MachineProvisioner.SetInstanceInfo. 362 func (m *Machine) SetInstanceInfo( 363 id instance.Id, displayName string, nonce string, characteristics *instance.HardwareCharacteristics, 364 networkConfig []params.NetworkConfig, volumes []params.Volume, 365 volumeAttachments map[string]params.VolumeAttachmentInfo, charmProfiles []string, 366 ) error { 367 var result params.ErrorResults 368 args := params.InstancesInfo{ 369 Machines: []params.InstanceInfo{{ 370 Tag: m.tag.String(), 371 InstanceId: id, 372 DisplayName: displayName, 373 Nonce: nonce, 374 Characteristics: characteristics, 375 Volumes: volumes, 376 VolumeAttachments: volumeAttachments, 377 NetworkConfig: networkConfig, 378 CharmProfiles: charmProfiles, 379 }}, 380 } 381 err := m.st.facade.FacadeCall("SetInstanceInfo", args, &result) 382 if err != nil { 383 return err 384 } 385 return result.OneError() 386 } 387 388 // InstanceId implements MachineProvisioner.InstanceId. 389 func (m *Machine) InstanceId() (instance.Id, error) { 390 var results params.StringResults 391 args := params.Entities{ 392 Entities: []params.Entity{{Tag: m.tag.String()}}, 393 } 394 err := m.st.facade.FacadeCall("InstanceId", args, &results) 395 if err != nil { 396 return "", err 397 } 398 if len(results.Results) != 1 { 399 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) 400 } 401 result := results.Results[0] 402 if result.Error != nil { 403 return "", result.Error 404 } 405 return instance.Id(result.Result), nil 406 } 407 408 // KeepInstance implements MachineProvisioner.KeepInstance. 409 func (m *Machine) KeepInstance() (bool, error) { 410 var results params.BoolResults 411 args := params.Entities{ 412 Entities: []params.Entity{{Tag: m.tag.String()}}, 413 } 414 err := m.st.facade.FacadeCall("KeepInstance", args, &results) 415 if err != nil { 416 return false, err 417 } 418 if len(results.Results) != 1 { 419 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 420 } 421 result := results.Results[0] 422 if result.Error != nil { 423 if params.IsCodeNotSupported(err) { 424 return false, errors.NewNotSupported(nil, "KeepInstance") 425 } 426 return false, result.Error 427 } 428 return result.Result, nil 429 } 430 431 // SetPassword implements MachineProvisioner.SetPassword. 432 func (m *Machine) SetPassword(password string) error { 433 var result params.ErrorResults 434 args := params.EntityPasswords{ 435 Changes: []params.EntityPassword{ 436 {Tag: m.tag.String(), Password: password}, 437 }, 438 } 439 err := m.st.facade.FacadeCall("SetPasswords", args, &result) 440 if err != nil { 441 return err 442 } 443 return result.OneError() 444 } 445 446 // WatchContainers implements MachineProvisioner.WatchContainers. 447 func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) { 448 if string(ctype) == "" { 449 return nil, fmt.Errorf("container type must be specified") 450 } 451 supported := false 452 for _, c := range instance.ContainerTypes { 453 if ctype == c { 454 supported = true 455 break 456 } 457 } 458 if !supported { 459 return nil, fmt.Errorf("unsupported container type %q", ctype) 460 } 461 var results params.StringsWatchResults 462 args := params.WatchContainers{ 463 Params: []params.WatchContainer{ 464 {MachineTag: m.tag.String(), ContainerType: string(ctype)}, 465 }, 466 } 467 err := m.st.facade.FacadeCall("WatchContainers", args, &results) 468 if err != nil { 469 return nil, err 470 } 471 if len(results.Results) != 1 { 472 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 473 } 474 result := results.Results[0] 475 if result.Error != nil { 476 return nil, result.Error 477 } 478 w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result) 479 return w, nil 480 } 481 482 // WatchAllContainers implements MachineProvisioner.WatchAllContainers. 483 func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) { 484 var results params.StringsWatchResults 485 args := params.WatchContainers{ 486 Params: []params.WatchContainer{ 487 {MachineTag: m.tag.String()}, 488 }, 489 } 490 err := m.st.facade.FacadeCall("WatchContainers", args, &results) 491 if err != nil { 492 return nil, err 493 } 494 if len(results.Results) != 1 { 495 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 496 } 497 result := results.Results[0] 498 if result.Error != nil { 499 return nil, result.Error 500 } 501 w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result) 502 return w, nil 503 } 504 505 // WatchContainers implements MachineProvisioner.WatchContainersCharmProfiles. 506 func (m *Machine) WatchContainersCharmProfiles(ctype instance.ContainerType) (watcher.StringsWatcher, error) { 507 if string(ctype) == "" { 508 return nil, fmt.Errorf("container type must be specified") 509 } 510 supported := false 511 for _, c := range instance.ContainerTypes { 512 if ctype == c { 513 supported = true 514 break 515 } 516 } 517 if !supported { 518 return nil, fmt.Errorf("unsupported container type %q", ctype) 519 } 520 var results params.StringsWatchResults 521 args := params.WatchContainers{ 522 Params: []params.WatchContainer{ 523 {MachineTag: m.tag.String(), ContainerType: string(ctype)}, 524 }, 525 } 526 err := m.st.facade.FacadeCall("WatchContainersCharmProfiles", args, &results) 527 if err != nil { 528 return nil, err 529 } 530 if len(results.Results) != 1 { 531 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 532 } 533 result := results.Results[0] 534 if result.Error != nil { 535 return nil, result.Error 536 } 537 w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result) 538 return w, nil 539 } 540 541 // SetSupportedContainers implements MachineProvisioner.SetSupportedContainers. 542 func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error { 543 var results params.ErrorResults 544 args := params.MachineContainersParams{ 545 Params: []params.MachineContainers{ 546 {MachineTag: m.tag.String(), ContainerTypes: containerTypes}, 547 }, 548 } 549 err := m.st.facade.FacadeCall("SetSupportedContainers", args, &results) 550 if err != nil { 551 return err 552 } 553 if len(results.Results) != 1 { 554 return fmt.Errorf("expected 1 result, got %d", len(results.Results)) 555 } 556 apiError := results.Results[0].Error 557 if apiError != nil { 558 return apiError 559 } 560 return nil 561 } 562 563 // SupportsNoContainers implements MachineProvisioner.SupportsNoContainers. 564 func (m *Machine) SupportsNoContainers() error { 565 return m.SetSupportedContainers([]instance.ContainerType{}...) 566 } 567 568 type CharmProfileChangeInfo struct { 569 OldProfileName string 570 NewProfileName string 571 LXDProfile *charm.LXDProfile 572 Subordinate bool 573 } 574 575 // CharmProfileChangeInfo implements MachineProvisioner.CharmProfileChangeInfo. 576 func (m *Machine) CharmProfileChangeInfo() (CharmProfileChangeInfo, error) { 577 var results params.ProfileChangeResults 578 args := params.Entities{Entities: []params.Entity{ 579 {Tag: m.tag.String()}, 580 }} 581 err := m.st.facade.FacadeCall("CharmProfileChangeInfo", args, &results) 582 if err != nil { 583 return CharmProfileChangeInfo{}, err 584 } 585 if len(results.Results) != 1 { 586 return CharmProfileChangeInfo{}, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 587 } 588 result := results.Results[0] 589 if result.Error != nil { 590 return CharmProfileChangeInfo{}, result.Error 591 } 592 var profile *charm.LXDProfile 593 if result.Profile != nil { 594 p := charm.LXDProfile(*result.Profile) 595 profile = &p 596 } 597 return CharmProfileChangeInfo{ 598 OldProfileName: result.OldProfileName, 599 NewProfileName: result.NewProfileName, 600 LXDProfile: profile, 601 Subordinate: result.Subordinate, 602 }, nil 603 } 604 605 // SetCharmProfiles implements MachineProvisioner.SetCharmProfiles. 606 func (m *Machine) SetCharmProfiles(profiles []string) error { 607 var results params.ErrorResults 608 args := params.SetProfileArgs{ 609 Args: []params.SetProfileArg{ 610 { 611 Entity: params.Entity{Tag: m.tag.String()}, 612 Profiles: profiles, 613 }, 614 }, 615 } 616 err := m.st.facade.FacadeCall("SetCharmProfiles", args, &results) 617 if err != nil { 618 return err 619 } 620 if len(results.Results) != 1 { 621 return fmt.Errorf("expected 1 result, got %d", len(results.Results)) 622 } 623 result := results.Results[0] 624 if result.Error != nil { 625 return result.Error 626 } 627 return nil 628 } 629 630 // SetUpgradeCharmProfileComplete implements MachineProvisioner.SetUpgradeCharmProfileComplete. 631 func (m *Machine) SetUpgradeCharmProfileComplete(message string) error { 632 var results params.ErrorResults 633 args := params.SetProfileUpgradeCompleteArgs{ 634 Args: []params.SetProfileUpgradeCompleteArg{ 635 { 636 Entity: params.Entity{Tag: m.tag.String()}, 637 Message: message, 638 }, 639 }, 640 } 641 err := m.st.facade.FacadeCall("SetUpgradeCharmProfileComplete", args, &results) 642 if err != nil { 643 return err 644 } 645 if len(results.Results) != 1 { 646 return fmt.Errorf("expected 1 result, got %d", len(results.Results)) 647 } 648 result := results.Results[0] 649 if result.Error != nil { 650 return result.Error 651 } 652 return nil 653 } 654 655 // RemoveUpgradeCharmProfileData implements MachineProvisioner.RemoveUpgradeCharmProfileData. 656 func (m *Machine) RemoveUpgradeCharmProfileData() error { 657 var results params.ErrorResults 658 args := params.Entities{ 659 Entities: []params.Entity{ 660 { 661 Tag: m.tag.String(), 662 }, 663 }, 664 } 665 err := m.st.facade.FacadeCall("RemoveUpgradeCharmProfileData", args, &results) 666 if err != nil { 667 return err 668 } 669 if len(results.Results) != 1 { 670 return fmt.Errorf("expected 1 result, got %d", len(results.Results)) 671 } 672 result := results.Results[0] 673 if result.Error != nil { 674 return result.Error 675 } 676 return nil 677 }