github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/machine.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 "sort" 9 "strings" 10 "time" 11 12 "github.com/juju/charm/v12" 13 "github.com/juju/collections/set" 14 "github.com/juju/errors" 15 "github.com/juju/mgo/v3" 16 "github.com/juju/mgo/v3/bson" 17 "github.com/juju/mgo/v3/txn" 18 "github.com/juju/names/v5" 19 jujutxn "github.com/juju/txn/v3" 20 "github.com/juju/utils/v3" 21 "github.com/juju/version/v2" 22 "github.com/kr/pretty" 23 24 "github.com/juju/juju/api" 25 "github.com/juju/juju/core/actions" 26 "github.com/juju/juju/core/constraints" 27 corecontainer "github.com/juju/juju/core/container" 28 "github.com/juju/juju/core/instance" 29 "github.com/juju/juju/core/model" 30 "github.com/juju/juju/core/network" 31 "github.com/juju/juju/core/status" 32 "github.com/juju/juju/environs/bootstrap" 33 "github.com/juju/juju/mongo" 34 stateerrors "github.com/juju/juju/state/errors" 35 "github.com/juju/juju/tools" 36 ) 37 38 // Machine represents the state of a machine. 39 type Machine struct { 40 st *State 41 doc machineDoc 42 } 43 44 // MachineJob values define responsibilities that machines may be 45 // expected to fulfil. 46 type MachineJob int 47 48 const ( 49 _ MachineJob = iota 50 JobHostUnits 51 JobManageModel 52 ) 53 54 var ( 55 jobNames = map[MachineJob]model.MachineJob{ 56 JobHostUnits: model.JobHostUnits, 57 JobManageModel: model.JobManageModel, 58 } 59 jobMigrationValue = map[MachineJob]string{ 60 JobHostUnits: "host-units", 61 JobManageModel: "api-server", 62 } 63 ) 64 65 // ToParams returns the job as model.MachineJob. 66 func (job MachineJob) ToParams() model.MachineJob { 67 if jujuJob, ok := jobNames[job]; ok { 68 return jujuJob 69 } 70 return model.MachineJob(fmt.Sprintf("<unknown job %d>", int(job))) 71 } 72 73 // paramsJobsFromJobs converts state jobs to juju jobs. 74 func paramsJobsFromJobs(jobs []MachineJob) []model.MachineJob { 75 jujuJobs := make([]model.MachineJob, len(jobs)) 76 for i, machineJob := range jobs { 77 jujuJobs[i] = machineJob.ToParams() 78 } 79 return jujuJobs 80 } 81 82 // MigrationValue converts the state job into a useful human readable 83 // string for model migration. 84 func (job MachineJob) MigrationValue() string { 85 if value, ok := jobMigrationValue[job]; ok { 86 return value 87 } 88 return "unknown" 89 } 90 91 func (job MachineJob) String() string { 92 return string(job.ToParams()) 93 } 94 95 // manualMachinePrefix signals as prefix of Nonce that a machine is 96 // manually provisioned. 97 const manualMachinePrefix = "manual:" 98 99 // machineDoc represents the internal state of a machine in MongoDB. 100 // Note the correspondence with MachineInfo in apiserver/juju. 101 type machineDoc struct { 102 DocID string `bson:"_id"` 103 Id string `bson:"machineid"` 104 ModelUUID string `bson:"model-uuid"` 105 Base Base `bson:"base"` 106 Nonce string 107 ContainerType string 108 Principals []string 109 Life Life 110 Tools *tools.Tools `bson:",omitempty"` 111 Jobs []MachineJob 112 PasswordHash string 113 Clean bool 114 ForceDestroyed bool `bson:"force-destroyed"` 115 116 // Volumes contains the names of volumes attached to the machine. 117 Volumes []string `bson:"volumes,omitempty"` 118 // Filesystems contains the names of filesystems attached to the machine. 119 Filesystems []string `bson:"filesystems,omitempty"` 120 121 // We store 2 different sets of addresses for the machine, obtained 122 // from different sources. 123 // Addresses is the set of addresses obtained by asking the provider. 124 Addresses []address 125 126 // MachineAddresses is the set of addresses obtained from the machine itself. 127 MachineAddresses []address 128 129 // PreferredPublicAddress is the preferred address to be used for 130 // the machine when a public address is requested. 131 PreferredPublicAddress address `bson:",omitempty"` 132 133 // PreferredPrivateAddress is the preferred address to be used for 134 // the machine when a private address is requested. 135 PreferredPrivateAddress address `bson:",omitempty"` 136 137 // The SupportedContainers attributes are used to advertise what containers this 138 // machine is capable of hosting. 139 SupportedContainersKnown bool 140 SupportedContainers []instance.ContainerType `bson:",omitempty"` 141 // Placement is the placement directive that should be used when provisioning 142 // an instance for the machine. 143 Placement string `bson:",omitempty"` 144 145 // AgentStartedAt records the time when the machine agent started. 146 AgentStartedAt time.Time `bson:"agent-started-at,omitempty"` 147 148 // Hostname records the machine's hostname as reported by the machine agent. 149 Hostname string `bson:"hostname,omitempty"` 150 } 151 152 func newMachine(st *State, doc *machineDoc) *Machine { 153 machine := &Machine{ 154 st: st, 155 doc: *doc, 156 } 157 return machine 158 } 159 160 // Id returns the machine id. 161 func (m *Machine) Id() string { 162 return m.doc.Id 163 } 164 165 // Principals returns the principals for the machine. 166 func (m *Machine) Principals() []string { 167 return m.doc.Principals 168 } 169 170 // Base returns the os base running on the machine. 171 func (m *Machine) Base() Base { 172 return m.doc.Base 173 } 174 175 // ContainerType returns the type of container hosting this machine. 176 func (m *Machine) ContainerType() instance.ContainerType { 177 return instance.ContainerType(m.doc.ContainerType) 178 } 179 180 // ModelUUID returns the unique identifier 181 // for the model that this machine is in. 182 func (m *Machine) ModelUUID() string { 183 return m.doc.ModelUUID 184 } 185 186 // ForceDestroyed returns whether the destruction of a dying/dead 187 // machine was forced. It's always false for a machine that's alive. 188 func (m *Machine) ForceDestroyed() bool { 189 return m.doc.ForceDestroyed 190 } 191 192 func (m *Machine) forceDestroyedOps() []txn.Op { 193 return []txn.Op{{ 194 C: machinesC, 195 Id: m.doc.DocID, 196 Assert: txn.DocExists, 197 Update: bson.D{{"$set", bson.D{{"force-destroyed", true}}}}, 198 }} 199 } 200 201 // machineGlobalKey returns the global database key for the identified machine. 202 func machineGlobalKey(id string) string { 203 return "m#" + id 204 } 205 206 // machineGlobalInstanceKey returns the global database key for the identified 207 // machine's instance. 208 func machineGlobalInstanceKey(id string) string { 209 return machineGlobalKey(id) + "#instance" 210 } 211 212 // globalInstanceKey returns the global database key for the machine's instance. 213 func (m *Machine) globalInstanceKey() string { 214 return machineGlobalInstanceKey(m.doc.Id) 215 } 216 217 // machineGlobalModificationKey returns the global database key for the 218 // identified machine's modification changes. 219 func machineGlobalModificationKey(id string) string { 220 return machineGlobalKey(id) + "#modification" 221 } 222 223 // globalModificationKey returns the global database key for the machine's 224 // modification changes. 225 func (m *Machine) globalModificationKey() string { 226 return machineGlobalModificationKey(m.doc.Id) 227 } 228 229 // globalKey returns the global database key for the machine. 230 func (m *Machine) globalKey() string { 231 return machineGlobalKey(m.doc.Id) 232 } 233 234 // instanceData holds attributes relevant to a provisioned machine. 235 type instanceData struct { 236 DocID string `bson:"_id"` 237 MachineId string `bson:"machineid"` 238 InstanceId instance.Id `bson:"instanceid"` 239 DisplayName string `bson:"display-name"` 240 ModelUUID string `bson:"model-uuid"` 241 Arch *string `bson:"arch,omitempty"` 242 Mem *uint64 `bson:"mem,omitempty"` 243 RootDisk *uint64 `bson:"rootdisk,omitempty"` 244 RootDiskSource *string `bson:"rootdisksource,omitempty"` 245 CpuCores *uint64 `bson:"cpucores,omitempty"` 246 CpuPower *uint64 `bson:"cpupower,omitempty"` 247 Tags *[]string `bson:"tags,omitempty"` 248 AvailZone *string `bson:"availzone,omitempty"` 249 VirtType *string `bson:"virt-type,omitempty"` 250 251 // KeepInstance is set to true if, on machine removal from Juju, 252 // the cloud instance should be retained. 253 KeepInstance bool `bson:"keep-instance,omitempty"` 254 255 // CharmProfiles contains the names of LXD profiles used by this machine. 256 // Profiles would have been defined in the charm deployed to this machine. 257 CharmProfiles []string `bson:"charm-profiles,omitempty"` 258 } 259 260 func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics { 261 return &instance.HardwareCharacteristics{ 262 Arch: instData.Arch, 263 Mem: instData.Mem, 264 RootDisk: instData.RootDisk, 265 RootDiskSource: instData.RootDiskSource, 266 CpuCores: instData.CpuCores, 267 CpuPower: instData.CpuPower, 268 Tags: instData.Tags, 269 AvailabilityZone: instData.AvailZone, 270 VirtType: instData.VirtType, 271 } 272 } 273 274 // TODO(wallyworld): move this method to a service. 275 func (m *Machine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) { 276 instData, err := getInstanceData(m.st, m.Id()) 277 if err != nil { 278 return nil, err 279 } 280 return hardwareCharacteristics(instData), nil 281 } 282 283 func getInstanceData(st *State, id string) (instanceData, error) { 284 instanceDataCollection, closer := st.db().GetCollection(instanceDataC) 285 defer closer() 286 287 var instData instanceData 288 err := instanceDataCollection.FindId(id).One(&instData) 289 if err == mgo.ErrNotFound { 290 return instanceData{}, errors.NotFoundf("instance data for machine %v", id) 291 } 292 if err != nil { 293 return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err) 294 } 295 return instData, nil 296 } 297 298 // removeInstanceDataOp returns the operation needed to remove the 299 // instance data document associated with the given globalKey. 300 func removeInstanceDataOp(globalKey string) txn.Op { 301 return txn.Op{ 302 C: instanceDataC, 303 Id: globalKey, 304 Remove: true, 305 } 306 } 307 308 // AllInstanceData retrieves all instance data in the model 309 // and provides a way to query hardware characteristics and 310 // charm profiles by machine. 311 func (m *Model) AllInstanceData() (*ModelInstanceData, error) { 312 coll, closer := m.st.db().GetCollection(instanceDataC) 313 defer closer() 314 315 var docs []instanceData 316 err := coll.Find(nil).All(&docs) 317 if err != nil { 318 return nil, errors.Annotate(err, "cannot get all instance data for model") 319 } 320 all := &ModelInstanceData{ 321 data: make(map[string]instanceData), 322 } 323 for _, doc := range docs { 324 all.data[doc.MachineId] = doc 325 } 326 return all, nil 327 } 328 329 // ModelInstanceData represents all the instance data for a model 330 // keyed on machine ID. 331 type ModelInstanceData struct { 332 data map[string]instanceData 333 } 334 335 // HardwareCharacteristics returns the hardware characteristics of the 336 // machine. If it isn't found in the map, a nil is returned. 337 func (d *ModelInstanceData) HardwareCharacteristics(machineID string) *instance.HardwareCharacteristics { 338 instData, found := d.data[machineID] 339 if !found { 340 return nil 341 } 342 return hardwareCharacteristics(instData) 343 } 344 345 // CharmProfiles returns the names of the profiles that are defined for 346 // the machine. If the machine isn't found in the map, a nil is returned. 347 func (d *ModelInstanceData) CharmProfiles(machineID string) []string { 348 instData, found := d.data[machineID] 349 if !found { 350 return nil 351 } 352 return instData.CharmProfiles 353 } 354 355 // InstanceNames returns both the provider instance id and the user 356 // friendly name. If the machine isn't found, empty strings are returned. 357 func (d *ModelInstanceData) InstanceNames(machineID string) (instance.Id, string) { 358 instData, found := d.data[machineID] 359 if !found { 360 return "", "" 361 } 362 return instData.InstanceId, instData.DisplayName 363 } 364 365 // Tag returns a tag identifying the machine. The String method provides a 366 // string representation that is safe to use as a file name. The returned name 367 // will be different from other Tag values returned by any other entities 368 // from the same state. 369 func (m *Machine) Tag() names.Tag { 370 return m.MachineTag() 371 } 372 373 // MachineTag returns the more specific MachineTag type as opposed 374 // to the more generic Tag type. 375 func (m *Machine) MachineTag() names.MachineTag { 376 return names.NewMachineTag(m.Id()) 377 } 378 379 // Life returns whether the machine is Alive, Dying or Dead. 380 func (m *Machine) Life() Life { 381 return m.doc.Life 382 } 383 384 // Jobs returns the responsibilities that must be fulfilled by m's agent. 385 func (m *Machine) Jobs() []MachineJob { 386 return m.doc.Jobs 387 } 388 389 // SetKeepInstance sets whether the cloud machine instance 390 // will be retained when the machine is removed from Juju. 391 // This is only relevant if an instance exists. 392 func (m *Machine) SetKeepInstance(keepInstance bool) error { 393 ops := []txn.Op{{ 394 C: instanceDataC, 395 Id: m.doc.DocID, 396 Assert: txn.DocExists, 397 Update: bson.D{{"$set", bson.D{{"keep-instance", keepInstance}}}}, 398 }} 399 if err := m.st.db().RunTransaction(ops); err != nil { 400 // If instance doc doesn't exist, that's ok; there's nothing to keep, 401 // but that's not an error we care about. 402 return errors.Annotatef(onAbort(err, nil), "cannot set KeepInstance on machine %v", m) 403 } 404 return nil 405 } 406 407 // KeepInstance reports whether a machine, when removed from 408 // Juju, will cause the corresponding cloud instance to be stopped. 409 func (m *Machine) KeepInstance() (bool, error) { 410 instData, err := getInstanceData(m.st, m.Id()) 411 if err != nil { 412 return false, err 413 } 414 return instData.KeepInstance, nil 415 } 416 417 // CharmProfiles returns the names of any LXD profiles used by the machine, 418 // which were defined in the charm deployed to that machine. 419 func (m *Machine) CharmProfiles() ([]string, error) { 420 instData, err := getInstanceData(m.st, m.Id()) 421 if errors.IsNotFound(err) { 422 err = errors.NotProvisionedf("machine %v", m.Id()) 423 } 424 if err != nil { 425 return nil, err 426 } 427 return instData.CharmProfiles, nil 428 } 429 430 // SetCharmProfiles sets the names of the charm profiles used on a machine 431 // in its instanceData. 432 func (m *Machine) SetCharmProfiles(profiles []string) error { 433 if len(profiles) == 0 { 434 return nil 435 } 436 buildTxn := func(attempt int) ([]txn.Op, error) { 437 if attempt > 0 { 438 if err := m.Refresh(); err != nil { 439 return nil, errors.Trace(err) 440 } 441 } 442 // Exit early if the Machine profiles doesn't need to change. 443 mProfiles, err := m.CharmProfiles() 444 if err != nil { 445 return nil, errors.Trace(err) 446 } 447 mProfilesSet := set.NewStrings(mProfiles...) 448 if mProfilesSet.Union(set.NewStrings(profiles...)).Size() == mProfilesSet.Size() { 449 return nil, jujutxn.ErrNoOperations 450 } 451 452 ops := []txn.Op{{ 453 C: instanceDataC, 454 Id: m.doc.DocID, 455 Assert: txn.DocExists, 456 Update: bson.D{{"$set", bson.D{{"charm-profiles", profiles}}}}, 457 }} 458 459 return ops, nil 460 } 461 err := m.st.db().Run(buildTxn) 462 return errors.Annotatef(err, "cannot update profiles for %q to %s", m, strings.Join(profiles, ", ")) 463 } 464 465 // IsManager returns true if the machine has JobManageModel. 466 func (m *Machine) IsManager() bool { 467 return isController(&m.doc) 468 } 469 470 // IsManual returns true if the machine was manually provisioned. 471 func (m *Machine) IsManual() (bool, error) { 472 // To avoid unnecessary db lookups, a little of the 473 // logic from isManualMachine() below is duplicated here 474 // so we can exit early if possible. 475 if strings.HasPrefix(m.doc.Nonce, manualMachinePrefix) { 476 return true, nil 477 } 478 if m.doc.Id != "0" { 479 return false, nil 480 } 481 modelSettings, err := readSettings(m.st.db(), settingsC, modelGlobalKey) 482 if err != nil { 483 return false, errors.Trace(err) 484 } 485 providerRaw, _ := modelSettings.Get("type") 486 providerType, _ := providerRaw.(string) 487 return isManualMachine(m.doc.Id, m.doc.Nonce, providerType), nil 488 } 489 490 func isManualMachine(id, nonce, providerType string) bool { 491 // Apart from the bootstrap machine, manually provisioned 492 // machines have a nonce prefixed with "manual:". This is 493 // unique to manual provisioning. 494 if strings.HasPrefix(nonce, manualMachinePrefix) { 495 return true 496 } 497 // The bootstrap machine uses BootstrapNonce, so in that 498 // case we need to check if its provider type is "manual". 499 // We also check for "null", which is an alias for manual. 500 return id == "0" && (providerType == "null" || providerType == "manual") 501 } 502 503 // AgentTools returns the tools that the agent is currently running. 504 // It returns an error that satisfies errors.IsNotFound if the tools 505 // have not yet been set. 506 func (m *Machine) AgentTools() (*tools.Tools, error) { 507 if m.doc.Tools == nil { 508 return nil, errors.NotFoundf("agent binaries for machine %v", m) 509 } 510 tools := *m.doc.Tools 511 return &tools, nil 512 } 513 514 // checkVersionValidity checks whether the given version is suitable 515 // for passing to SetAgentVersion. 516 func checkVersionValidity(v version.Binary) error { 517 if v.Release == "" || v.Arch == "" { 518 return fmt.Errorf("empty series or arch") 519 } 520 return nil 521 } 522 523 // SetAgentVersion sets the version of juju that the agent is 524 // currently running. 525 func (m *Machine) SetAgentVersion(v version.Binary) (err error) { 526 defer errors.DeferredAnnotatef(&err, "cannot set agent version for machine %v", m) 527 ops, tools, err := m.setAgentVersionOps(v) 528 if err != nil { 529 return errors.Trace(err) 530 } 531 // A "raw" transaction is needed here because this function gets 532 // called before database migrations have run so we don't 533 // necessarily want the model UUID added to the id. 534 if err := m.st.runRawTransaction(ops); err != nil { 535 return onAbort(err, stateerrors.ErrDead) 536 } 537 m.doc.Tools = tools 538 return nil 539 } 540 541 func (m *Machine) setAgentVersionOps(v version.Binary) ([]txn.Op, *tools.Tools, error) { 542 if err := checkVersionValidity(v); err != nil { 543 return nil, nil, err 544 } 545 tools := &tools.Tools{Version: v} 546 ops := []txn.Op{{ 547 C: machinesC, 548 Id: m.doc.DocID, 549 Assert: notDeadDoc, 550 Update: bson.D{{"$set", bson.D{{"tools", tools}}}}, 551 }} 552 return ops, tools, nil 553 } 554 555 // SetMongoPassword sets the password the agent responsible for the machine 556 // should use to communicate with the controllers. Previous passwords 557 // are invalidated. 558 func (m *Machine) SetMongoPassword(password string) error { 559 if !m.IsManager() { 560 return errors.NotSupportedf("setting mongo password for non-controller machine %v", m) 561 } 562 return mongo.SetAdminMongoPassword(m.st.session, m.Tag().String(), password) 563 } 564 565 // SetPassword sets the password for the machine's agent. 566 func (m *Machine) SetPassword(password string) error { 567 if len(password) < utils.MinAgentPasswordLength { 568 return errors.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password)) 569 } 570 passwordHash := utils.AgentPasswordHash(password) 571 op := m.UpdateOperation() 572 op.PasswordHash = &passwordHash 573 if err := m.st.ApplyOperation(op); err != nil { 574 return errors.Trace(err) 575 } 576 m.doc.PasswordHash = passwordHash 577 return nil 578 } 579 580 func (m *Machine) setPasswordHashOps(passwordHash string) ([]txn.Op, error) { 581 if m.doc.Life == Dead { 582 return nil, stateerrors.ErrDead 583 } 584 ops := []txn.Op{{ 585 C: machinesC, 586 Id: m.doc.DocID, 587 Assert: notDeadDoc, 588 Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}}, 589 }} 590 return ops, nil 591 } 592 593 // PasswordValid returns whether the given password is valid 594 // for the given machine. 595 func (m *Machine) PasswordValid(password string) bool { 596 agentHash := utils.AgentPasswordHash(password) 597 return agentHash == m.doc.PasswordHash 598 } 599 600 // Destroy sets the machine lifecycle to Dying if it is Alive. It does 601 // nothing otherwise. Destroy will fail if the machine has principal 602 // units assigned, or if the machine has JobManageModel. 603 // If the machine has assigned units, Destroy will return 604 // a HasAssignedUnitsError. If the machine has containers, Destroy 605 // will return HasContainersError. 606 func (m *Machine) Destroy() error { 607 if m.IsManager() && len(m.doc.Principals) > 0 { 608 return errors.Trace(m.destroyControllerWithPrincipals()) 609 } 610 return errors.Trace(m.advanceLifecycle(Dying, false, false, 0)) 611 } 612 613 // destroyControllerWithPrincipals is called if the controller has 614 // principal units and they are juju- system charms, then gracefully 615 // remove them before destroying the machine. 616 func (m *Machine) destroyControllerWithPrincipals() error { 617 units, err := m.Units() 618 if err != nil { 619 return errors.Trace(err) 620 } 621 unitsMap := make(map[string]*Unit) 622 for _, unit := range units { 623 unitsMap[unit.String()] = unit 624 } 625 var evacuablePrincipals []string 626 for _, principalUnit := range m.doc.Principals { 627 unit, ok := unitsMap[principalUnit] 628 if !ok { 629 continue 630 } 631 curlStr := unit.CharmURL() 632 if curlStr == nil { 633 continue 634 } 635 curl, err := charm.ParseURL(*curlStr) 636 if err != nil { 637 return errors.Trace(err) 638 } 639 if curl != nil && strings.HasPrefix(curl.Name, "juju-") { 640 evacuablePrincipals = append(evacuablePrincipals, principalUnit) 641 } 642 } 643 if len(m.doc.Principals) != len(evacuablePrincipals) { 644 return stateerrors.NewHasAssignedUnitsError(m.doc.Id, m.doc.Principals) 645 } 646 ops := []txn.Op{ 647 { 648 C: machinesC, 649 Id: m.doc.DocID, 650 Assert: bson.D{ 651 {"life", Alive}, 652 advanceLifecycleUnitAsserts(evacuablePrincipals), 653 }, 654 // To prevent race conditions, we remove the ability for new 655 // units to be deployed to the machine. 656 Update: bson.D{{"$pull", bson.D{{"jobs", JobHostUnits}}}}, 657 }, 658 newCleanupOp(cleanupEvacuateMachine, m.doc.Id), 659 } 660 if err := m.st.db().RunTransaction(ops); err != nil { 661 return errors.Annotatef(err, "failed to run transaction: %s", pretty.Sprint(ops)) 662 } 663 for i, v := range m.doc.Jobs { 664 if v == JobHostUnits { 665 m.doc.Jobs = append(m.doc.Jobs[:i], m.doc.Jobs[i+1:]...) 666 break 667 } 668 } 669 err = m.SetStatus(status.StatusInfo{ 670 Status: status.Stopped, 671 Message: fmt.Sprintf("waiting on dying units %s", strings.Join(evacuablePrincipals, ", ")), 672 }) 673 if err != nil { 674 return errors.Trace(err) 675 } 676 return nil 677 } 678 679 // DestroyWithContainers sets the machine lifecycle to Dying if it is Alive. 680 // It does nothing otherwise. DestroyWithContainers will fail if the machine 681 // has principal units assigned, or if the machine has JobManageModel. If the 682 // machine has assigned units, DestroyWithContainers will return a 683 // HasAssignedUnitsError. The machine is allowed to have containers. Use with 684 // caution. Intended for model tear down. 685 func (m *Machine) DestroyWithContainers() error { 686 return m.advanceLifecycle(Dying, false, true, 0) 687 } 688 689 // ForceDestroy queues the machine for complete removal, including the 690 // destruction of all units and containers on the machine. 691 func (m *Machine) ForceDestroy(maxWait time.Duration) error { 692 ops, err := m.forceDestroyOps(maxWait) 693 if err != nil { 694 return errors.Trace(err) 695 } 696 if err := m.st.db().RunTransaction(ops); err != txn.ErrAborted { 697 return errors.Annotatef(err, "failed to run transaction: %s", pretty.Sprint(ops)) 698 } 699 return nil 700 } 701 702 func (m *Machine) forceDestroyOps(maxWait time.Duration) ([]txn.Op, error) { 703 if m.IsManager() { 704 controllerIds, err := m.st.ControllerIds() 705 if err != nil { 706 return nil, errors.Annotatef(err, "reading controller info") 707 } 708 if len(controllerIds) <= 1 { 709 return nil, errors.Errorf("controller %s is the only controller", m.Id()) 710 } 711 // We set the machine to Dying if it isn't already dead. 712 var machineOp txn.Op 713 if m.Life() < Dead { 714 // Make sure we don't want the vote, and we are queued to be Dying. 715 // Since we are force deleting, life assert should be current machine's life. 716 machineOp = txn.Op{ 717 C: machinesC, 718 Id: m.doc.DocID, 719 Assert: bson.D{{"life", bson.D{{"$in", []Life{Alive, Dying}}}}}, 720 Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, 721 } 722 } 723 controllerOp := txn.Op{ 724 C: controllersC, 725 Id: modelGlobalKey, 726 Assert: bson.D{{"controller-ids", controllerIds}}, 727 } 728 // Note that ForceDestroy does *not* cleanup the replicaset, so it might cause problems. 729 // However, we're letting the user handle times when the machine agent isn't running, etc. 730 // We may need to update the peergrouper for this. 731 return []txn.Op{ 732 machineOp, 733 controllerOp, 734 setControllerWantsVoteOp(m.st, m.Id(), false), 735 newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id, maxWait), 736 }, nil 737 } else { 738 // Make sure the machine doesn't become a manager while we're destroying it 739 return []txn.Op{{ 740 C: machinesC, 741 Id: m.doc.DocID, 742 Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageModel}}}}}, 743 }, newCleanupOp(cleanupForceDestroyedMachine, m.doc.Id, maxWait), 744 }, nil 745 } 746 } 747 748 // EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying. 749 // It does nothing otherwise. EnsureDead will fail if the machine has 750 // principal units assigned, or if the machine has JobManageModel. 751 // If the machine has assigned units, EnsureDead will return 752 // a HasAssignedUnitsError. 753 func (m *Machine) EnsureDead() error { 754 return m.advanceLifecycle(Dead, false, false, 0) 755 } 756 757 // Containers returns the container ids belonging to a parent machine. 758 // TODO(wallyworld): move this method to a service 759 func (m *Machine) Containers() ([]string, error) { 760 containerRefs, closer := m.st.db().GetCollection(containerRefsC) 761 defer closer() 762 763 var mc machineContainers 764 err := containerRefs.FindId(m.doc.DocID).One(&mc) 765 if err == nil { 766 return mc.Children, nil 767 } 768 if err == mgo.ErrNotFound { 769 return nil, errors.NotFoundf("container info for machine %v", m.Id()) 770 } 771 return nil, err 772 } 773 774 // ParentId returns the Id of the host machine if this machine is a container. 775 func (m *Machine) ParentId() (string, bool) { 776 parentId := corecontainer.ParentId(m.Id()) 777 return parentId, parentId != "" 778 } 779 780 // IsContainer returns true if the machine is a container. 781 func (m *Machine) IsContainer() bool { 782 _, isContainer := m.ParentId() 783 return isContainer 784 } 785 786 // advanceLifecycle ensures that the machine's lifecycle is no earlier 787 // than the supplied value. If the machine already has that lifecycle 788 // value, or a later one, no changes will be made to remote state. If 789 // the machine has any responsibilities that preclude a valid change in 790 // lifecycle, it will return an error. dyingAllowContainers indicates 791 // whether the machine can have containers when moving to the dying state. 792 // Not allowed for moving to dead. 793 func (original *Machine) advanceLifecycle(life Life, force, dyingAllowContainers bool, maxWait time.Duration) (err error) { 794 logger.Debugf("%s.advanceLifecycle(%s, %t, %t)", original.Id(), life, force, dyingAllowContainers) 795 796 if life == Dead && dyingAllowContainers { 797 return errors.BadRequestf("life cannot be Dead if dyingAllowContainers true.") 798 } 799 800 // A machine can be set to dying with containers, but cannot have any when 801 // advanced to dead. 802 if !dyingAllowContainers && life == Dying { 803 if err := original.advanceLifecycleIfNoContainers(); err != nil { 804 return err 805 } 806 } 807 808 locked, err := original.IsLockedForSeriesUpgrade() 809 if err != nil { 810 return errors.Annotatef(err, "reading machine %s upgrade-series lock", original.Id()) 811 } 812 if locked { 813 return errors.Errorf("machine %s is locked for series upgrade", original.Id()) 814 } 815 816 m := original 817 defer func() { 818 if err == nil { 819 // The machine's lifecycle is known to have advanced; it may be 820 // known to have already advanced further than requested, in 821 // which case we set the latest known valid value. 822 if m == nil { 823 life = Dead 824 } else if m.doc.Life > life { 825 life = m.doc.Life 826 } 827 original.doc.Life = life 828 } 829 }() 830 831 ops := m.advanceLifecyleInitialOps(life) 832 833 // multiple attempts: one with original data, one with refreshed data, and a final 834 // one intended to determine the cause of failure of the preceding attempt. 835 buildTxn := func(attempt int) ([]txn.Op, error) { 836 var asserts bson.D 837 // Grab a fresh copy of the machine data. 838 // We don't write to original, because the expectation is that state- 839 // changing methods only set the requested change on the receiver; a case 840 // could perhaps be made that this is not a helpful convention in the 841 // context of the new state API, but we maintain consistency in the 842 // face of uncertainty. 843 if m, err = m.st.Machine(m.doc.Id); errors.IsNotFound(err) { 844 return nil, jujutxn.ErrNoOperations 845 } else if err != nil { 846 return nil, err 847 } 848 node, err := m.st.ControllerNode(m.doc.Id) 849 if err != nil && !errors.IsNotFound(err) { 850 return nil, err 851 } 852 hasVote := err == nil && node.HasVote() 853 854 // Check that the life change is sane, and collect the assertions 855 // necessary to determine that it remains so. 856 switch life { 857 case Dying: 858 if m.doc.Life != Alive { 859 return nil, jujutxn.ErrNoOperations 860 } 861 // Manager nodes are allowed to go to dying even when they have 862 // the vote, as that is used as the signal that they should lose 863 // their vote. 864 asserts = append(asserts, isAliveDoc...) 865 case Dead: 866 if m.doc.Life == Dead { 867 return nil, jujutxn.ErrNoOperations 868 } 869 if hasVote || m.IsManager() { 870 return nil, stateerrors.NewIsControllerMemberError(m.Id(), hasVote) 871 } 872 asserts = append(asserts, bson.DocElem{ 873 Name: "jobs", Value: bson.D{{Name: "$nin", Value: []MachineJob{JobManageModel}}}}) 874 asserts = append(asserts, notDeadDoc...) 875 ops = append(ops, controllerAdvanceLifecyleVoteOp()) 876 default: 877 panic(fmt.Errorf("cannot advance lifecycle to %v", life)) 878 } 879 880 // Check that the machine does not have any responsibilities that 881 // prevent a lifecycle change. 882 // If there are no alive units left on the machine, or all the applications are dying, 883 // then the machine may be soon destroyed by a cleanup worker. 884 // In that case, we don't want to return any error about not being able to 885 // destroy a machine with units as it will be a lie. 886 if life == Dying { 887 canDie := true 888 if isController(&m.doc) || hasVote { 889 // If we're responsible for managing the model, make sure we ask to drop our vote 890 ops[0].Update = bson.D{ 891 {"$set", bson.D{{"life", life}}}, 892 } 893 controllerOp, err := m.controllerIDsOp() 894 if err != nil { 895 return nil, errors.Trace(err) 896 } 897 ops = append(ops, controllerOp) 898 ops = append(ops, setControllerWantsVoteOp(m.st, m.doc.Id, false)) 899 } 900 901 var principalUnitNames []string 902 for _, principalUnit := range m.doc.Principals { 903 principalUnitNames = append(principalUnitNames, principalUnit) 904 canDie, err = m.assessCanDieUnit(principalUnit) 905 if err != nil { 906 return nil, errors.Trace(err) 907 } 908 if !canDie { 909 break 910 } 911 } 912 913 if canDie && !dyingAllowContainers { 914 err := m.advanceLifecycleIfNoContainers() 915 if errors.Is(err, stateerrors.HasContainersError) { 916 canDie = false 917 } else if err != nil { 918 return nil, err 919 } 920 ops = append(ops, m.noContainersOp()) 921 } 922 923 cleanupOp := newCleanupOp(cleanupDyingMachine, m.doc.Id, force, maxWait) 924 ops = append(ops, cleanupOp) 925 926 if canDie { 927 ops[0].Assert = append(asserts, advanceLifecycleUnitAsserts(principalUnitNames)) 928 txnLogger.Debugf("txn moving machine %q to %s", m.Id(), life) 929 return ops, nil 930 } 931 } 932 933 if len(m.doc.Principals) > 0 { 934 return nil, stateerrors.NewHasAssignedUnitsError(m.doc.Id, m.doc.Principals) 935 } 936 asserts = append(asserts, noUnitAsserts()) 937 938 if life == Dead { 939 // A machine may not become Dead until it has no 940 // containers. 941 if err := m.advanceLifecycleIfNoContainers(); err != nil { 942 return nil, err 943 } 944 ops = append(ops, m.noContainersOp()) 945 if isController(&m.doc) { 946 return nil, errors.Errorf("machine %s is still responsible for being a controller", m.Id()) 947 } 948 // A machine may not become Dead until it has no more 949 // attachments to detachable storage. 950 storageAsserts, err := m.assertNoPersistentStorage() 951 if err != nil { 952 return nil, errors.Trace(err) 953 } 954 asserts = append(asserts, storageAsserts...) 955 } 956 957 // Add the additional asserts needed for this transaction. 958 ops[0].Assert = asserts 959 return ops, nil 960 } 961 962 if err = m.st.db().Run(buildTxn); err == jujutxn.ErrExcessiveContention { 963 err = errors.Annotatef(err, "machine %s cannot advance lifecycle", m) 964 } 965 return err 966 } 967 968 func (m *Machine) advanceLifecyleInitialOps(life Life) []txn.Op { 969 return []txn.Op{ 970 { 971 C: machinesC, 972 Id: m.doc.DocID, 973 Update: bson.D{{"$set", bson.D{{"life", life}}}}, 974 }, 975 { 976 C: machineUpgradeSeriesLocksC, 977 Id: m.doc.Id, 978 Assert: txn.DocMissing, 979 }, 980 } 981 } 982 983 func controllerAdvanceLifecyleVoteOp() txn.Op { 984 return txn.Op{ 985 C: controllersC, 986 Id: modelGlobalKey, 987 Assert: bson.D{ 988 {"has-vote", bson.M{"$ne": true}}, 989 {"wants-vote", bson.M{"$ne": true}}, 990 }, 991 } 992 } 993 994 // controllerIDsOp returns an Op to assert that the machine's 995 // controllerIDs do not change. 996 func (m *Machine) controllerIDsOp() (txn.Op, error) { 997 controllerIds, err := m.st.ControllerIds() 998 if err != nil { 999 return txn.Op{}, errors.Annotatef(err, "reading controller info") 1000 } 1001 if len(controllerIds) <= 1 { 1002 return txn.Op{}, errors.Errorf("controller %s is the only controller", m.Id()) 1003 } 1004 return txn.Op{ 1005 C: controllersC, 1006 Id: modelGlobalKey, 1007 Assert: bson.D{{"controller-ids", controllerIds}}, 1008 }, nil 1009 } 1010 1011 // noContainersOp returns an Op to assert that the machine 1012 // has no containers. 1013 func (m *Machine) noContainersOp() txn.Op { 1014 return txn.Op{ 1015 C: containerRefsC, 1016 Id: m.doc.DocID, 1017 Assert: bson.D{{"$or", []bson.D{ 1018 {{"children", bson.D{{"$size", 0}}}}, 1019 {{"children", bson.D{{"$exists", false}}}}, 1020 }}}, 1021 } 1022 } 1023 1024 // assessCanDieUnit returns true if the machine can die, based on 1025 // evaluating the provided unit. 1026 func (m *Machine) assessCanDieUnit(principalUnit string) (bool, error) { 1027 canDie := true 1028 u, err := m.st.Unit(principalUnit) 1029 if err != nil { 1030 return false, errors.Annotatef(err, "reading machine %s principal unit %v", m, m.doc.Principals[0]) 1031 } 1032 app, err := u.Application() 1033 if err != nil { 1034 return false, errors.Annotatef(err, "reading machine %s principal unit application %v", m, u.doc.Application) 1035 } 1036 if u.Life() == Alive && app.Life() == Alive { 1037 canDie = false 1038 } 1039 return canDie, nil 1040 } 1041 1042 // noUnitAsserts returns bson DocElem which assert that there are 1043 // no units for the machine. 1044 func noUnitAsserts() bson.DocElem { 1045 return bson.DocElem{ 1046 Name: "$or", Value: []bson.D{ 1047 {{"principals", bson.D{{"$size", 0}}}}, 1048 {{"principals", bson.D{{"$exists", false}}}}, 1049 }, 1050 } 1051 } 1052 1053 // advanceLifecycleUnitAsserts returns bson DocElem which assert that there are 1054 // no units for the machine, or that the list of units has not changed. 1055 func advanceLifecycleUnitAsserts(principalUnitNames []string) bson.DocElem { 1056 return bson.DocElem{ 1057 Name: "$or", Value: []bson.D{ 1058 {{Name: "principals", Value: principalUnitNames}}, 1059 {{Name: "principals", Value: bson.D{{"$size", 0}}}}, 1060 {{Name: "principals", Value: bson.D{{"$exists", false}}}}, 1061 }, 1062 } 1063 } 1064 1065 // advanceLifecycleIfNoContainers determines if the machine has 1066 // containers, if so, returns the appropriate error. 1067 func (m *Machine) advanceLifecycleIfNoContainers() error { 1068 containers, err := m.Containers() 1069 if err != nil { 1070 return errors.Annotatef(err, "reading machine %s containers", m) 1071 } 1072 1073 if len(containers) > 0 { 1074 return stateerrors.NewHasContainersError(m.doc.Id, containers) 1075 } 1076 return nil 1077 } 1078 1079 // assertNoPersistentStorage ensures that there are no persistent volumes or 1080 // filesystems attached to the machine, and returns any mgo/txn assertions 1081 // required to ensure that remains true. 1082 func (m *Machine) assertNoPersistentStorage() (bson.D, error) { 1083 attachments := names.NewSet() 1084 for _, v := range m.doc.Volumes { 1085 tag := names.NewVolumeTag(v) 1086 detachable, err := isDetachableVolumeTag(m.st.db(), tag) 1087 if err != nil { 1088 return nil, errors.Trace(err) 1089 } 1090 if detachable { 1091 attachments.Add(tag) 1092 } 1093 } 1094 for _, f := range m.doc.Filesystems { 1095 tag := names.NewFilesystemTag(f) 1096 detachable, err := isDetachableFilesystemTag(m.st.db(), tag) 1097 if err != nil { 1098 return nil, errors.Trace(err) 1099 } 1100 if detachable { 1101 attachments.Add(tag) 1102 } 1103 } 1104 if len(attachments) > 0 { 1105 return nil, stateerrors.NewHasAttachmentsError(m.doc.Id, attachments.SortedValues()) 1106 } 1107 if m.doc.Life == Dying { 1108 return nil, nil 1109 } 1110 // A Dying machine cannot have attachments added to it, 1111 // but if we're advancing from Alive to Dead then we 1112 // must ensure no concurrent attachments are made. 1113 noNewVolumes := bson.DocElem{ 1114 Name: "volumes", Value: bson.D{{ 1115 "$not", bson.D{{ 1116 "$elemMatch", bson.D{{ 1117 "$nin", m.doc.Volumes, 1118 }}, 1119 }}, 1120 }}, 1121 // There are no volumes that are not in 1122 // the set of volumes we previously knew 1123 // about => the current set of volumes 1124 // is a subset of the previously known set. 1125 } 1126 noNewFilesystems := bson.DocElem{ 1127 Name: "filesystems", Value: bson.D{{ 1128 "$not", bson.D{{ 1129 "$elemMatch", bson.D{{ 1130 "$nin", m.doc.Filesystems, 1131 }}, 1132 }}, 1133 }}, 1134 } 1135 return bson.D{noNewVolumes, noNewFilesystems}, nil 1136 } 1137 1138 func (m *Machine) removePortsOps() ([]txn.Op, error) { 1139 if m.doc.Life != Dead { 1140 return nil, errors.Errorf("machine is not dead") 1141 } 1142 machRanges, err := m.OpenedPortRanges() 1143 if err != nil { 1144 return nil, err 1145 } 1146 1147 mpr := machRanges.(*machinePortRanges) 1148 if !mpr.Persisted() { 1149 return nil, nil 1150 } 1151 return mpr.removeOps(), nil 1152 } 1153 1154 func (m *Machine) removeOps() ([]txn.Op, error) { 1155 if m.doc.Life != Dead { 1156 return nil, fmt.Errorf("machine is not dead") 1157 } 1158 ops := []txn.Op{ 1159 { 1160 C: machinesC, 1161 Id: m.doc.DocID, 1162 Assert: txn.DocExists, 1163 Remove: true, 1164 }, 1165 { 1166 C: machinesC, 1167 Id: m.doc.DocID, 1168 Assert: isDeadDoc, 1169 }, 1170 removeStatusOp(m.st, m.globalKey()), 1171 removeStatusOp(m.st, m.globalInstanceKey()), 1172 removeStatusOp(m.st, m.globalModificationKey()), 1173 removeConstraintsOp(m.globalKey()), 1174 annotationRemoveOp(m.st, m.globalKey()), 1175 removeRebootDocOp(m.st, m.globalKey()), 1176 removeMachineBlockDevicesOp(m.Id()), 1177 removeModelMachineRefOp(m.st, m.Id()), 1178 removeSSHHostKeyOp(m.globalKey()), 1179 removeInstanceDataOp(m.doc.DocID), 1180 } 1181 linkLayerDevicesOps, err := m.removeAllLinkLayerDevicesOps() 1182 if err != nil { 1183 return nil, errors.Trace(err) 1184 } 1185 devicesAddressesOps, err := m.removeAllAddressesOps() 1186 if err != nil { 1187 return nil, errors.Trace(err) 1188 } 1189 portsOps, err := m.removePortsOps() 1190 if err != nil { 1191 return nil, errors.Trace(err) 1192 } 1193 1194 sb, err := NewStorageBackend(m.st) 1195 if err != nil { 1196 return nil, errors.Trace(err) 1197 } 1198 filesystemOps, err := sb.removeMachineFilesystemsOps(m) 1199 if err != nil { 1200 return nil, errors.Trace(err) 1201 } 1202 volumeOps, err := sb.removeMachineVolumesOps(m) 1203 if err != nil { 1204 return nil, errors.Trace(err) 1205 } 1206 1207 ops = append(ops, removeControllerNodeOp(m.st, m.Id())) 1208 ops = append(ops, linkLayerDevicesOps...) 1209 ops = append(ops, devicesAddressesOps...) 1210 ops = append(ops, portsOps...) 1211 ops = append(ops, removeContainerRefOps(m.st, m.Id())...) 1212 ops = append(ops, filesystemOps...) 1213 ops = append(ops, volumeOps...) 1214 return ops, nil 1215 } 1216 1217 // Remove removes the machine from state. It will fail if the machine 1218 // is not Dead. 1219 func (m *Machine) Remove() (err error) { 1220 defer errors.DeferredAnnotatef(&err, "cannot remove machine %s", m.doc.Id) 1221 logger.Tracef("removing machine %q", m.Id()) 1222 // Local variable so we can re-get the machine without disrupting 1223 // the caller. 1224 machine := m 1225 buildTxn := func(attempt int) ([]txn.Op, error) { 1226 if attempt != 0 { 1227 machine, err = machine.st.Machine(machine.Id()) 1228 if errors.IsNotFound(err) { 1229 // The machine's gone away, that's fine. 1230 return nil, jujutxn.ErrNoOperations 1231 } 1232 if err != nil { 1233 return nil, errors.Trace(err) 1234 } 1235 } 1236 ops, err := machine.removeOps() 1237 if err != nil { 1238 return nil, errors.Trace(err) 1239 } 1240 return ops, nil 1241 } 1242 return m.st.db().Run(buildTxn) 1243 } 1244 1245 // Refresh refreshes the contents of the machine from the underlying 1246 // state. It returns an error that satisfies errors.IsNotFound if the 1247 // machine has been removed. 1248 func (m *Machine) Refresh() error { 1249 mdoc, err := m.st.getMachineDoc(m.Id()) 1250 if err != nil { 1251 if errors.IsNotFound(err) { 1252 return err 1253 } 1254 return errors.Annotatef(err, "cannot refresh machine %v", m) 1255 } 1256 m.doc = *mdoc 1257 return nil 1258 } 1259 1260 // InstanceId returns the provider specific instance id for this 1261 // machine, or a NotProvisionedError, if not set. 1262 func (m *Machine) InstanceId() (instance.Id, error) { 1263 instId, _, err := m.InstanceNames() 1264 return instId, err 1265 } 1266 1267 // InstanceNames returns both the provider's instance id and a user-friendly 1268 // display name. The display name is intended used for human input and 1269 // is ignored internally. 1270 func (m *Machine) InstanceNames() (instance.Id, string, error) { 1271 instData, err := getInstanceData(m.st, m.Id()) 1272 if errors.IsNotFound(err) { 1273 err = errors.NotProvisionedf("machine %v", m.Id()) 1274 } 1275 if err != nil { 1276 return "", "", err 1277 } 1278 return instData.InstanceId, instData.DisplayName, nil 1279 } 1280 1281 // InstanceStatus returns the provider specific instance status for this machine, 1282 // or a NotProvisionedError if instance is not yet provisioned. 1283 func (m *Machine) InstanceStatus() (status.StatusInfo, error) { 1284 machineStatus, err := getStatus(m.st.db(), m.globalInstanceKey(), "instance") 1285 if err != nil { 1286 logger.Warningf("error when retrieving instance status for machine: %s, %v", m.Id(), err) 1287 return status.StatusInfo{}, err 1288 } 1289 return machineStatus, nil 1290 } 1291 1292 // SetInstanceStatus sets the provider specific instance status for a machine. 1293 func (m *Machine) SetInstanceStatus(sInfo status.StatusInfo) (err error) { 1294 return setStatus(m.st.db(), setStatusParams{ 1295 badge: "instance", 1296 globalKey: m.globalInstanceKey(), 1297 status: sInfo.Status, 1298 message: sInfo.Message, 1299 rawData: sInfo.Data, 1300 updated: timeOrNow(sInfo.Since, m.st.clock()), 1301 }) 1302 } 1303 1304 // InstanceStatusHistory returns a slice of at most filter.Size StatusInfo items 1305 // or items as old as filter.Date or items newer than now - filter.Delta time 1306 // representing past statuses for this machine instance. 1307 // Instance represents the provider underlying [v]hardware or container where 1308 // this juju machine is deployed. 1309 func (m *Machine) InstanceStatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1310 args := &statusHistoryArgs{ 1311 db: m.st.db(), 1312 globalKey: m.globalInstanceKey(), 1313 filter: filter, 1314 clock: m.st.clock(), 1315 } 1316 return statusHistory(args) 1317 } 1318 1319 // ModificationStatus returns the provider specific modification status for 1320 // this machine or NotProvisionedError if instance is not yet provisioned. 1321 func (m *Machine) ModificationStatus() (status.StatusInfo, error) { 1322 machineStatus, err := getStatus(m.st.db(), m.globalModificationKey(), "modification") 1323 if err != nil { 1324 logger.Warningf("error when retrieving instance status for machine: %s, %v", m.Id(), err) 1325 return status.StatusInfo{}, err 1326 } 1327 return machineStatus, nil 1328 } 1329 1330 // SetModificationStatus sets the provider specific modification status 1331 // for a machine. Allowing the propagation of status messages to the 1332 // operator. 1333 func (m *Machine) SetModificationStatus(sInfo status.StatusInfo) (err error) { 1334 return setStatus(m.st.db(), setStatusParams{ 1335 badge: "modification", 1336 globalKey: m.globalModificationKey(), 1337 status: sInfo.Status, 1338 message: sInfo.Message, 1339 rawData: sInfo.Data, 1340 updated: timeOrNow(sInfo.Since, m.st.clock()), 1341 }) 1342 } 1343 1344 // AvailabilityZone returns the provider-specific instance availability 1345 // zone in which the machine was provisioned. 1346 func (m *Machine) AvailabilityZone() (string, error) { 1347 instData, err := getInstanceData(m.st, m.Id()) 1348 if errors.IsNotFound(err) { 1349 return "", errors.Trace(errors.NotProvisionedf("machine %v", m.Id())) 1350 } 1351 if err != nil { 1352 return "", errors.Trace(err) 1353 } 1354 var zone string 1355 if instData.AvailZone != nil { 1356 zone = *instData.AvailZone 1357 } 1358 return zone, nil 1359 } 1360 1361 // ApplicationNames returns the names of applications 1362 // represented by units running on the machine. 1363 func (m *Machine) ApplicationNames() ([]string, error) { 1364 units, err := m.Units() 1365 if err != nil { 1366 return nil, errors.Trace(err) 1367 } 1368 apps := set.NewStrings() 1369 for _, unit := range units { 1370 apps.Add(unit.ApplicationName()) 1371 } 1372 return apps.SortedValues(), nil 1373 } 1374 1375 // Units returns all the units that have been assigned to the machine. 1376 func (m *Machine) Units() (units []*Unit, err error) { 1377 defer errors.DeferredAnnotatef(&err, "cannot get units assigned to machine %v", m) 1378 unitsCollection, closer := m.st.db().GetCollection(unitsC) 1379 defer closer() 1380 1381 pudocs := []unitDoc{} 1382 err = unitsCollection.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs) 1383 if err != nil { 1384 return nil, err 1385 } 1386 model, err := m.st.Model() 1387 if err != nil { 1388 return nil, errors.Trace(err) 1389 } 1390 for _, pudoc := range pudocs { 1391 units = append(units, newUnit(m.st, model.Type(), &pudoc)) 1392 } 1393 return units, nil 1394 } 1395 1396 // SetProvisioned stores the machine's provider-specific details in the 1397 // database. These details are used to infer that the machine has 1398 // been provisioned. 1399 // 1400 // When provisioning an instance, a nonce should be created and passed 1401 // when starting it, before adding the machine to the state. This means 1402 // that if the provisioner crashes (or its connection to the state is 1403 // lost) after starting the instance, we can be sure that only a single 1404 // instance will be able to act for that machine. 1405 // 1406 // Once set, the instance id cannot be changed. A non-empty instance id 1407 // will be detected as a provisioned machine. 1408 func (m *Machine) SetProvisioned( 1409 id instance.Id, 1410 displayName string, 1411 nonce string, 1412 characteristics *instance.HardwareCharacteristics, 1413 ) (err error) { 1414 defer errors.DeferredAnnotatef(&err, "cannot set instance data for machine %q", m) 1415 1416 if id == "" || nonce == "" { 1417 return fmt.Errorf("instance id and nonce cannot be empty") 1418 } 1419 1420 coll, closer := m.st.db().GetCollection(instanceDataC) 1421 defer closer() 1422 count, err := coll.Find(bson.D{{"instanceid", id}}).Count() 1423 if err != nil { 1424 return errors.Trace(err) 1425 } 1426 if count > 0 { 1427 logger.Warningf("duplicate instance id %q already saved", id) 1428 } 1429 1430 if characteristics == nil { 1431 characteristics = &instance.HardwareCharacteristics{} 1432 } 1433 instData := &instanceData{ 1434 DocID: m.doc.DocID, 1435 MachineId: m.doc.Id, 1436 InstanceId: id, 1437 DisplayName: displayName, 1438 ModelUUID: m.doc.ModelUUID, 1439 Arch: characteristics.Arch, 1440 Mem: characteristics.Mem, 1441 RootDisk: characteristics.RootDisk, 1442 RootDiskSource: characteristics.RootDiskSource, 1443 CpuCores: characteristics.CpuCores, 1444 CpuPower: characteristics.CpuPower, 1445 Tags: characteristics.Tags, 1446 AvailZone: characteristics.AvailabilityZone, 1447 VirtType: characteristics.VirtType, 1448 } 1449 1450 ops := []txn.Op{ 1451 { 1452 C: machinesC, 1453 Id: m.doc.DocID, 1454 Assert: append(isAliveDoc, bson.DocElem{Name: "nonce", Value: ""}), 1455 Update: bson.D{{"$set", bson.D{{"nonce", nonce}}}}, 1456 }, { 1457 C: instanceDataC, 1458 Id: m.doc.DocID, 1459 Assert: txn.DocMissing, 1460 Insert: instData, 1461 }, 1462 } 1463 1464 if err = m.st.db().RunTransaction(ops); err == nil { 1465 m.doc.Nonce = nonce 1466 return nil 1467 } else if err != txn.ErrAborted { 1468 return err 1469 } else if alive, err := isAlive(m.st, machinesC, m.doc.DocID); err != nil { 1470 return err 1471 } else if !alive { 1472 return machineNotAliveErr 1473 } 1474 return fmt.Errorf("already set") 1475 } 1476 1477 // SetInstanceInfo is used to provision a machine and in one step sets its 1478 // instance ID, nonce, hardware characteristics, add link-layer devices and set 1479 // their addresses as needed. After, set charm profiles if needed. 1480 func (m *Machine) SetInstanceInfo( 1481 id instance.Id, displayName string, nonce string, characteristics *instance.HardwareCharacteristics, 1482 devicesArgs []LinkLayerDeviceArgs, devicesAddrs []LinkLayerDeviceAddress, 1483 volumes map[names.VolumeTag]VolumeInfo, 1484 volumeAttachments map[names.VolumeTag]VolumeAttachmentInfo, 1485 charmProfiles []string, 1486 ) error { 1487 logger.Tracef( 1488 "setting instance info: machine %v, deviceAddrs: %#v, devicesArgs: %#v", 1489 m.Id(), devicesAddrs, devicesArgs) 1490 1491 sb, err := NewStorageBackend(m.st) 1492 if err != nil { 1493 return errors.Trace(err) 1494 } 1495 1496 // Record volumes and volume attachments, and set the initial 1497 // status: attached or attaching. 1498 if err := setProvisionedVolumeInfo(sb, volumes); err != nil { 1499 return errors.Trace(err) 1500 } 1501 if err := setMachineVolumeAttachmentInfo(sb, m.Id(), volumeAttachments); err != nil { 1502 return errors.Trace(err) 1503 } 1504 volumeStatus := make(map[names.VolumeTag]status.Status) 1505 for tag := range volumes { 1506 volumeStatus[tag] = status.Attaching 1507 } 1508 for tag := range volumeAttachments { 1509 volumeStatus[tag] = status.Attached 1510 } 1511 for tag, volStatus := range volumeStatus { 1512 vol, err := sb.Volume(tag) 1513 if err != nil { 1514 return errors.Trace(err) 1515 } 1516 if err := vol.SetStatus(status.StatusInfo{ 1517 Status: volStatus, 1518 }); err != nil { 1519 return errors.Annotatef( 1520 err, "setting status of %s", names.ReadableString(tag), 1521 ) 1522 } 1523 } 1524 1525 if err := m.SetProvisioned(id, displayName, nonce, characteristics); err != nil { 1526 return errors.Trace(err) 1527 } 1528 return m.SetCharmProfiles(charmProfiles) 1529 } 1530 1531 // Addresses returns any hostnames and ips associated with a machine, 1532 // determined both by the machine itself, and by asking the provider. 1533 // 1534 // The addresses returned by the provider shadow any of the addresses 1535 // that the machine reported with the same address value. 1536 // Provider-reported addresses always come before machine-reported 1537 // addresses. Duplicates are removed. 1538 func (m *Machine) Addresses() (addresses network.SpaceAddresses) { 1539 return network.MergedAddresses(networkAddresses(m.doc.MachineAddresses), networkAddresses(m.doc.Addresses)) 1540 } 1541 1542 func containsAddress(addresses []address, address address) bool { 1543 for _, addr := range addresses { 1544 if addr.Value == address.Value { 1545 return true 1546 } 1547 } 1548 return false 1549 } 1550 1551 // PublicAddress returns a public address for the machine. If no address is 1552 // available it returns an error that satisfies network.IsNoAddressError(). 1553 func (m *Machine) PublicAddress() (network.SpaceAddress, error) { 1554 publicAddress := m.doc.PreferredPublicAddress.networkAddress() 1555 var err error 1556 if publicAddress.Value == "" { 1557 err = network.NoAddressError("public") 1558 } 1559 return publicAddress, err 1560 } 1561 1562 // maybeGetNewAddress determines if the current address is the most appropriate 1563 // match, and if not it selects the best from the slice of all available 1564 // addresses. It returns the new address and a bool indicating if a different 1565 // one was picked. 1566 func maybeGetNewAddress( 1567 addr address, 1568 providerAddresses, 1569 machineAddresses []address, 1570 getAddr func([]address) network.SpaceAddress, 1571 checkScope func(address) bool, 1572 ) (address, bool) { 1573 // For picking the best address, try provider addresses first. 1574 var newAddr address 1575 netAddr := getAddr(providerAddresses) 1576 if netAddr.Value == "" { 1577 netAddr = getAddr(machineAddresses) 1578 newAddr = fromNetworkAddress(netAddr, network.OriginMachine) 1579 } else { 1580 newAddr = fromNetworkAddress(netAddr, network.OriginProvider) 1581 } 1582 // The order of these checks is important. If the stored address is 1583 // empty we *always* want to check for a new address so we do that 1584 // first. If the stored address is unavailable we also *must* check for 1585 // a new address so we do that next. If the original is a machine 1586 // address and a provider address is available we want to switch to 1587 // that. Finally we check to see if a better match on scope from the 1588 // same origin is available. 1589 if addr.Value == "" { 1590 return newAddr, newAddr.Value != "" 1591 } 1592 if !containsAddress(providerAddresses, addr) && !containsAddress(machineAddresses, addr) { 1593 return newAddr, true 1594 } 1595 if network.Origin(addr.Origin) != network.OriginProvider && 1596 network.Origin(newAddr.Origin) == network.OriginProvider { 1597 return newAddr, true 1598 } 1599 if !checkScope(addr) { 1600 // If addr.Origin is machine and newAddr.Origin is provider we will 1601 // have already caught that, and for the inverse we don't want to 1602 // replace the address. 1603 if addr.Origin == newAddr.Origin { 1604 return newAddr, checkScope(newAddr) 1605 } 1606 } 1607 return addr, false 1608 } 1609 1610 // PrivateAddress returns a private address for the machine. If no address is 1611 // available it returns an error that satisfies network.IsNoAddressError(). 1612 func (m *Machine) PrivateAddress() (network.SpaceAddress, error) { 1613 privateAddress := m.doc.PreferredPrivateAddress.networkAddress() 1614 var err error 1615 if privateAddress.Value == "" { 1616 err = network.NoAddressError("private") 1617 } 1618 return privateAddress, err 1619 } 1620 1621 func (m *Machine) setPreferredAddressOps(addr address, isPublic bool) []txn.Op { 1622 fieldName := "preferredprivateaddress" 1623 current := m.doc.PreferredPrivateAddress 1624 if isPublic { 1625 fieldName = "preferredpublicaddress" 1626 current = m.doc.PreferredPublicAddress 1627 } 1628 // Assert that the field is either missing (never been set) or is 1629 // unchanged from its previous value. 1630 1631 // Since using a struct in the assert also asserts ordering, and we know that mgo 1632 // can change the ordering, we assert on the dotted values, effectively checking each 1633 // of the attributes of the address. 1634 currentD := []bson.D{ 1635 {{fieldName + ".value", current.Value}}, 1636 {{fieldName + ".addresstype", current.AddressType}}, 1637 } 1638 // Since scope, origin, and space have omitempty, we don't add them if they are empty. 1639 if current.Scope != "" { 1640 currentD = append(currentD, bson.D{{fieldName + ".networkscope", current.Scope}}) 1641 } 1642 if current.Origin != "" { 1643 currentD = append(currentD, bson.D{{fieldName + ".origin", current.Origin}}) 1644 } 1645 if current.SpaceID != "" { 1646 currentD = append(currentD, bson.D{{fieldName + ".spaceid", current.SpaceID}}) 1647 } 1648 1649 assert := bson.D{{"$or", []bson.D{ 1650 {{"$and", currentD}}, 1651 {{fieldName, nil}}}}} 1652 1653 ops := []txn.Op{{ 1654 C: machinesC, 1655 Id: m.doc.DocID, 1656 Update: bson.D{{"$set", bson.D{{fieldName, addr}}}}, 1657 Assert: assert, 1658 }} 1659 logger.Tracef("setting preferred address to %v (isPublic %#v)", addr, isPublic) 1660 return ops 1661 } 1662 1663 func (m *Machine) setPublicAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, *address) { 1664 publicAddress := m.doc.PreferredPublicAddress 1665 logger.Tracef( 1666 "machine %v: current public address: %#v \nprovider addresses: %#v \nmachine addresses: %#v", 1667 m.Id(), publicAddress, providerAddresses, machineAddresses) 1668 1669 // Always prefer an exact match if available. 1670 checkScope := func(addr address) bool { 1671 return network.ExactScopeMatch(addr.networkAddress(), network.ScopePublic) 1672 } 1673 // Without an exact match, prefer a fallback match. 1674 getAddr := func(addresses []address) network.SpaceAddress { 1675 addr, _ := networkAddresses(addresses).OneMatchingScope(network.ScopeMatchPublic) 1676 return addr 1677 } 1678 1679 newAddr, changed := maybeGetNewAddress(publicAddress, providerAddresses, machineAddresses, getAddr, checkScope) 1680 if !changed { 1681 // No change, so no ops. 1682 return []txn.Op{}, nil 1683 } 1684 1685 ops := m.setPreferredAddressOps(newAddr, true) 1686 return ops, &newAddr 1687 } 1688 1689 func (m *Machine) setPrivateAddressOps(providerAddresses []address, machineAddresses []address) ([]txn.Op, *address) { 1690 privateAddress := m.doc.PreferredPrivateAddress 1691 // Always prefer an exact match if available. 1692 checkScope := func(addr address) bool { 1693 return network.ExactScopeMatch( 1694 addr.networkAddress(), network.ScopeMachineLocal, network.ScopeCloudLocal, network.ScopeFanLocal) 1695 } 1696 // Without an exact match, prefer a fallback match. 1697 getAddr := func(addresses []address) network.SpaceAddress { 1698 addr, _ := networkAddresses(addresses).OneMatchingScope(network.ScopeMatchCloudLocal) 1699 return addr 1700 } 1701 1702 newAddr, changed := maybeGetNewAddress(privateAddress, providerAddresses, machineAddresses, getAddr, checkScope) 1703 if !changed { 1704 // No change, so no ops. 1705 return []txn.Op{}, nil 1706 } 1707 ops := m.setPreferredAddressOps(newAddr, false) 1708 return ops, &newAddr 1709 } 1710 1711 // SetProviderAddresses records any addresses related to the machine, sourced 1712 // by asking the provider. 1713 func (m *Machine) SetProviderAddresses(addresses ...network.SpaceAddress) error { 1714 err := m.setAddresses(nil, &addresses) 1715 return errors.Annotatef(err, "cannot set addresses of machine %v", m) 1716 } 1717 1718 // ProviderAddresses returns any hostnames and ips associated with a machine, 1719 // as determined by asking the provider. 1720 func (m *Machine) ProviderAddresses() (addresses network.SpaceAddresses) { 1721 for _, address := range m.doc.Addresses { 1722 addresses = append(addresses, address.networkAddress()) 1723 } 1724 return 1725 } 1726 1727 // MachineAddresses returns any hostnames and ips associated with a machine, 1728 // determined by asking the machine itself. 1729 func (m *Machine) MachineAddresses() (addresses network.SpaceAddresses) { 1730 for _, address := range m.doc.MachineAddresses { 1731 addresses = append(addresses, address.networkAddress()) 1732 } 1733 return 1734 } 1735 1736 // SetMachineAddresses records any addresses related to the machine, sourced 1737 // by asking the machine. 1738 func (m *Machine) SetMachineAddresses(addresses ...network.SpaceAddress) error { 1739 err := m.setAddresses(&addresses, nil) 1740 return errors.Annotatef(err, "cannot set machine addresses of machine %v", m) 1741 } 1742 1743 // setAddresses updates the machine's addresses (either Addresses or 1744 // MachineAddresses, depending on the field argument). Changes are 1745 // only predicated on the machine not being Dead; concurrent address 1746 // changes are ignored. 1747 func (m *Machine) setAddresses(machineAddresses, providerAddresses *[]network.SpaceAddress) error { 1748 var ( 1749 machineStateAddresses, providerStateAddresses []address 1750 newPrivate, newPublic *address 1751 err error 1752 ) 1753 machine := m 1754 buildTxn := func(attempt int) ([]txn.Op, error) { 1755 if attempt != 0 { 1756 if machine, err = machine.st.Machine(machine.doc.Id); err != nil { 1757 return nil, err 1758 } 1759 } 1760 var ops []txn.Op 1761 ops, machineStateAddresses, providerStateAddresses, newPrivate, newPublic, err = machine.setAddressesOps( 1762 machineAddresses, providerAddresses, 1763 ) 1764 if err != nil { 1765 return nil, err 1766 } 1767 return ops, nil 1768 } 1769 if err := m.st.db().Run(buildTxn); err != nil { 1770 return errors.Trace(err) 1771 } 1772 1773 m.doc.MachineAddresses = machineStateAddresses 1774 m.doc.Addresses = providerStateAddresses 1775 if newPrivate != nil { 1776 oldPrivate := m.doc.PreferredPrivateAddress.networkAddress() 1777 m.doc.PreferredPrivateAddress = *newPrivate 1778 logger.Infof( 1779 "machine %q preferred private address changed from %q to %q", 1780 m.Id(), oldPrivate, newPrivate.networkAddress(), 1781 ) 1782 } 1783 if newPublic != nil { 1784 oldPublic := m.doc.PreferredPublicAddress.networkAddress() 1785 m.doc.PreferredPublicAddress = *newPublic 1786 logger.Infof( 1787 "machine %q preferred public address changed from %q to %q", 1788 m.Id(), oldPublic, newPublic.networkAddress(), 1789 ) 1790 if isController(&m.doc) { 1791 if err := m.st.maybeUpdateControllerCharm(m.doc.PreferredPublicAddress.Value); err != nil { 1792 return errors.Trace(err) 1793 } 1794 } 1795 } 1796 return nil 1797 } 1798 1799 func (st *State) maybeUpdateControllerCharm(publicAddr string) error { 1800 controllerApp, err := st.Application(bootstrap.ControllerApplicationName) 1801 if errors.IsNotFound(err) { 1802 return nil 1803 } 1804 if err != nil { 1805 return errors.Trace(err) 1806 } 1807 controllerCfg, err := st.ControllerConfig() 1808 if err != nil { 1809 return errors.Trace(err) 1810 } 1811 return controllerApp.UpdateCharmConfig(model.GenerationMaster, charm.Settings{ 1812 "controller-url": api.ControllerAPIURL(publicAddr, controllerCfg.APIPort()), 1813 }) 1814 } 1815 1816 func (m *Machine) setAddressesOps( 1817 machineAddresses, providerAddresses *[]network.SpaceAddress, 1818 ) (_ []txn.Op, machineStateAddresses, providerStateAddresses []address, newPrivate, newPublic *address, _ error) { 1819 1820 if m.doc.Life == Dead { 1821 return nil, nil, nil, nil, nil, stateerrors.ErrDead 1822 } 1823 1824 fromNetwork := func(in network.SpaceAddresses, origin network.Origin) []address { 1825 sorted := make(network.SpaceAddresses, len(in)) 1826 copy(sorted, in) 1827 sort.Sort(sorted) 1828 return fromNetworkAddresses(sorted, origin) 1829 } 1830 1831 var set bson.D 1832 machineStateAddresses = m.doc.MachineAddresses 1833 providerStateAddresses = m.doc.Addresses 1834 if machineAddresses != nil { 1835 machineStateAddresses = fromNetwork(*machineAddresses, network.OriginMachine) 1836 set = append(set, bson.DocElem{Name: "machineaddresses", Value: machineStateAddresses}) 1837 } 1838 if providerAddresses != nil { 1839 providerStateAddresses = fromNetwork(*providerAddresses, network.OriginProvider) 1840 set = append(set, bson.DocElem{Name: "addresses", Value: providerStateAddresses}) 1841 } 1842 1843 ops := []txn.Op{{ 1844 C: machinesC, 1845 Id: m.doc.DocID, 1846 Assert: notDeadDoc, 1847 Update: bson.D{{"$set", set}}, 1848 }} 1849 1850 setPrivateAddressOps, newPrivate := m.setPrivateAddressOps(providerStateAddresses, machineStateAddresses) 1851 setPublicAddressOps, newPublic := m.setPublicAddressOps(providerStateAddresses, machineStateAddresses) 1852 ops = append(ops, setPrivateAddressOps...) 1853 ops = append(ops, setPublicAddressOps...) 1854 return ops, machineStateAddresses, providerStateAddresses, newPrivate, newPublic, nil 1855 } 1856 1857 // CheckProvisioned returns true if the machine was provisioned with the given nonce. 1858 func (m *Machine) CheckProvisioned(nonce string) bool { 1859 return nonce == m.doc.Nonce && nonce != "" 1860 } 1861 1862 // String returns a unique description of this machine. 1863 func (m *Machine) String() string { 1864 return m.doc.Id 1865 } 1866 1867 // Placement returns the machine's Placement structure that should be used when 1868 // provisioning an instance for the machine. 1869 func (m *Machine) Placement() string { 1870 return m.doc.Placement 1871 } 1872 1873 // Constraints returns the exact constraints that should apply when provisioning 1874 // an instance for the machine. 1875 func (m *Machine) Constraints() (constraints.Value, error) { 1876 return readConstraints(m.st, m.globalKey()) 1877 } 1878 1879 // SetConstraints sets the exact constraints to apply when provisioning an 1880 // instance for the machine. It will fail if the machine is Dead, or if it 1881 // is already provisioned. 1882 func (m *Machine) SetConstraints(cons constraints.Value) (err error) { 1883 op := m.UpdateOperation() 1884 op.Constraints = &cons 1885 return m.st.ApplyOperation(op) 1886 } 1887 1888 func (m *Machine) setConstraintsOps(cons constraints.Value) ([]txn.Op, error) { 1889 unsupported, err := m.st.validateConstraints(cons) 1890 if len(unsupported) > 0 { 1891 logger.Warningf( 1892 "setting constraints on machine %q: unsupported constraints: %v", 1893 m.Id(), strings.Join(unsupported, ","), 1894 ) 1895 } else if err != nil { 1896 return nil, err 1897 } 1898 1899 if m.doc.Life != Alive { 1900 return nil, machineNotAliveErr 1901 } 1902 if _, err := m.InstanceId(); err == nil { 1903 return nil, fmt.Errorf("machine is already provisioned") 1904 } else if !errors.IsNotProvisioned(err) { 1905 return nil, err 1906 } 1907 1908 notSetYet := bson.D{{"nonce", ""}} 1909 ops := []txn.Op{{ 1910 C: machinesC, 1911 Id: m.doc.DocID, 1912 Assert: append(isAliveDoc, notSetYet...), 1913 }} 1914 mcons, err := m.st.resolveMachineConstraints(cons) 1915 if err != nil { 1916 return nil, err 1917 } 1918 ops = append(ops, setConstraintsOp(m.globalKey(), mcons)) 1919 return ops, nil 1920 } 1921 1922 // Status returns the status of the machine. 1923 func (m *Machine) Status() (status.StatusInfo, error) { 1924 mStatus, err := getStatus(m.st.db(), m.globalKey(), "machine") 1925 if err != nil { 1926 return mStatus, err 1927 } 1928 return mStatus, nil 1929 } 1930 1931 // SetStatus sets the status of the machine. 1932 func (m *Machine) SetStatus(statusInfo status.StatusInfo) error { 1933 switch statusInfo.Status { 1934 case status.Started, status.Stopped: 1935 case status.Error: 1936 if statusInfo.Message == "" { 1937 return errors.Errorf("cannot set status %q without info", statusInfo.Status) 1938 } 1939 case status.Pending: 1940 // If a machine is not yet provisioned, we allow its status 1941 // to be set back to pending (when a retry is to occur). 1942 _, err := m.InstanceId() 1943 allowPending := errors.IsNotProvisioned(err) 1944 if allowPending { 1945 break 1946 } 1947 fallthrough 1948 case status.Down: 1949 return errors.Errorf("cannot set status %q", statusInfo.Status) 1950 default: 1951 return errors.Errorf("cannot set invalid status %q", statusInfo.Status) 1952 } 1953 return setStatus(m.st.db(), setStatusParams{ 1954 badge: "machine", 1955 globalKey: m.globalKey(), 1956 status: statusInfo.Status, 1957 message: statusInfo.Message, 1958 rawData: statusInfo.Data, 1959 updated: timeOrNow(statusInfo.Since, m.st.clock()), 1960 }) 1961 } 1962 1963 // StatusHistory returns a slice of at most filter.Size StatusInfo items 1964 // or items as old as filter.Date or items newer than now - filter.Delta time 1965 // representing past statuses for this machine. 1966 func (m *Machine) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) { 1967 args := &statusHistoryArgs{ 1968 db: m.st.db(), 1969 globalKey: m.globalKey(), 1970 filter: filter, 1971 clock: m.st.clock(), 1972 } 1973 return statusHistory(args) 1974 } 1975 1976 // Clean returns true if the machine does not have any deployed units or containers. 1977 func (m *Machine) Clean() bool { 1978 return m.doc.Clean 1979 } 1980 1981 // SupportedContainers returns any containers this machine is capable of hosting, and a bool 1982 // indicating if the supported containers have been determined or not. 1983 func (m *Machine) SupportedContainers() ([]instance.ContainerType, bool) { 1984 return m.doc.SupportedContainers, m.doc.SupportedContainersKnown 1985 } 1986 1987 // SupportsNoContainers records the fact that this machine doesn't support any containers. 1988 func (m *Machine) SupportsNoContainers() (err error) { 1989 if err = m.updateSupportedContainers([]instance.ContainerType{}); err != nil { 1990 return err 1991 } 1992 return m.markInvalidContainers() 1993 } 1994 1995 // SetSupportedContainers sets the list of containers supported by this machine. 1996 func (m *Machine) SetSupportedContainers(containers []instance.ContainerType) (err error) { 1997 if len(containers) == 0 { 1998 return fmt.Errorf("at least one valid container type is required") 1999 } 2000 for _, container := range containers { 2001 if container == instance.NONE { 2002 return fmt.Errorf("%q is not a valid container type", container) 2003 } 2004 } 2005 if err = m.updateSupportedContainers(containers); err != nil { 2006 return err 2007 } 2008 return m.markInvalidContainers() 2009 } 2010 2011 func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool { 2012 for _, supportedContainer := range supportedContainers { 2013 if supportedContainer == container { 2014 return true 2015 } 2016 } 2017 return false 2018 } 2019 2020 // updateSupportedContainers sets the supported containers on this host machine. 2021 func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) { 2022 if m.doc.SupportedContainersKnown { 2023 if len(m.doc.SupportedContainers) == len(supportedContainers) { 2024 equal := true 2025 types := make(map[instance.ContainerType]struct{}, len(m.doc.SupportedContainers)) 2026 for _, v := range m.doc.SupportedContainers { 2027 types[v] = struct{}{} 2028 } 2029 for _, v := range supportedContainers { 2030 if _, ok := types[v]; !ok { 2031 equal = false 2032 break 2033 } 2034 } 2035 if equal { 2036 return nil 2037 } 2038 } 2039 } 2040 ops := []txn.Op{ 2041 { 2042 C: machinesC, 2043 Id: m.doc.DocID, 2044 Assert: notDeadDoc, 2045 Update: bson.D{ 2046 {"$set", bson.D{ 2047 {"supportedcontainers", supportedContainers}, 2048 {"supportedcontainersknown", true}, 2049 }}}, 2050 }, 2051 } 2052 if err = m.st.db().RunTransaction(ops); err != nil { 2053 err = onAbort(err, stateerrors.ErrDead) 2054 logger.Errorf("cannot update supported containers of machine %v: %v", m, err) 2055 return err 2056 } 2057 m.doc.SupportedContainers = supportedContainers 2058 m.doc.SupportedContainersKnown = true 2059 return nil 2060 } 2061 2062 // markInvalidContainers sets the status of any container belonging to this machine 2063 // as being in error if the container type is not supported. 2064 func (m *Machine) markInvalidContainers() error { 2065 currentContainers, err := m.Containers() 2066 if err != nil { 2067 return err 2068 } 2069 for _, containerId := range currentContainers { 2070 if !isSupportedContainer(corecontainer.ContainerTypeFromId(containerId), m.doc.SupportedContainers) { 2071 container, err := m.st.Machine(containerId) 2072 if err != nil { 2073 logger.Errorf("loading container %v to mark as invalid: %v", containerId, err) 2074 continue 2075 } 2076 // There should never be a circumstance where an unsupported container is started. 2077 // Nonetheless, we check and log an error if such a situation arises. 2078 statusInfo, err := container.Status() 2079 if err != nil { 2080 logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err) 2081 continue 2082 } 2083 if statusInfo.Status == status.Pending { 2084 containerType := corecontainer.ContainerTypeFromId(containerId) 2085 now := m.st.clock().Now() 2086 s := status.StatusInfo{ 2087 Status: status.Error, 2088 Message: "unsupported container", 2089 Data: map[string]interface{}{"type": containerType}, 2090 Since: &now, 2091 } 2092 _ = container.SetStatus(s) 2093 } else { 2094 logger.Errorf("unsupported container %v has unexpected status %v", containerId, statusInfo.Status) 2095 } 2096 } 2097 } 2098 return nil 2099 } 2100 2101 // SetMachineBlockDevices sets the block devices visible on the machine. 2102 func (m *Machine) SetMachineBlockDevices(info ...BlockDeviceInfo) error { 2103 return setMachineBlockDevices(m.st, m.Id(), info) 2104 } 2105 2106 // VolumeAttachments returns the machine's volume attachments. 2107 func (m *Machine) VolumeAttachments() ([]VolumeAttachment, error) { 2108 sb, err := NewStorageBackend(m.st) 2109 if err != nil { 2110 return nil, errors.Trace(err) 2111 } 2112 return sb.MachineVolumeAttachments(m.MachineTag()) 2113 } 2114 2115 // PrepareActionPayload returns the payload to use in creating an action for this machine. 2116 // Note that the use of spec.InsertDefaults mutates payload. 2117 func (m *Machine) PrepareActionPayload(name string, payload map[string]interface{}, parallel *bool, executionGroup *string) (map[string]interface{}, bool, string, error) { 2118 if len(name) == 0 { 2119 return nil, false, "", errors.New("no action name given") 2120 } 2121 2122 spec, ok := actions.PredefinedActionsSpec[name] 2123 if !ok { 2124 return nil, false, "", errors.Errorf("cannot add action %q to a machine; only predefined actions allowed", name) 2125 } 2126 2127 // Reject bad payloads before attempting to insert defaults. 2128 err := spec.ValidateParams(payload) 2129 if err != nil { 2130 return nil, false, "", errors.Trace(err) 2131 } 2132 payloadWithDefaults, err := spec.InsertDefaults(payload) 2133 if err != nil { 2134 return nil, false, "", errors.Trace(err) 2135 } 2136 2137 runParallel := spec.Parallel 2138 if parallel != nil { 2139 runParallel = *parallel 2140 } 2141 runExecutionGroup := spec.ExecutionGroup 2142 if executionGroup != nil { 2143 runExecutionGroup = *executionGroup 2144 } 2145 2146 return payloadWithDefaults, runParallel, runExecutionGroup, nil 2147 } 2148 2149 // CancelAction is part of the ActionReceiver interface. 2150 func (m *Machine) CancelAction(action Action) (Action, error) { 2151 return action.Finish(ActionResults{Status: ActionCancelled}) 2152 } 2153 2154 // WatchActionNotifications is part of the ActionReceiver interface. 2155 func (m *Machine) WatchActionNotifications() StringsWatcher { 2156 return m.st.watchActionNotificationsFilteredBy(m) 2157 } 2158 2159 // WatchPendingActionNotifications is part of the ActionReceiver interface. 2160 func (m *Machine) WatchPendingActionNotifications() StringsWatcher { 2161 return m.st.watchEnqueuedActionsFilteredBy(m) 2162 } 2163 2164 // Actions is part of the ActionReceiver interface. 2165 func (m *Machine) Actions() ([]Action, error) { 2166 return m.st.matchingActions(m) 2167 } 2168 2169 // CompletedActions is part of the ActionReceiver interface. 2170 func (m *Machine) CompletedActions() ([]Action, error) { 2171 return m.st.matchingActionsCompleted(m) 2172 } 2173 2174 // PendingActions is part of the ActionReceiver interface. 2175 func (m *Machine) PendingActions() ([]Action, error) { 2176 return m.st.matchingActionsPending(m) 2177 } 2178 2179 // RunningActions is part of the ActionReceiver interface. 2180 func (m *Machine) RunningActions() ([]Action, error) { 2181 return m.st.matchingActionsRunning(m) 2182 } 2183 2184 // UpdateMachineSeries updates the base for the Machine. 2185 func (m *Machine) UpdateMachineSeries(base Base) error { 2186 buildTxn := func(attempt int) ([]txn.Op, error) { 2187 if attempt > 0 { 2188 if err := m.Refresh(); err != nil { 2189 return nil, errors.Trace(err) 2190 } 2191 } 2192 // Exit early if the Machine base doesn't need to change. 2193 if m.Base().String() == base.String() { 2194 return nil, jujutxn.ErrNoOperations 2195 } 2196 2197 units, err := m.Units() 2198 if err != nil { 2199 return nil, errors.Trace(err) 2200 } 2201 2202 ops := []txn.Op{{ 2203 C: machinesC, 2204 Id: m.doc.DocID, 2205 Assert: bson.D{{"life", Alive}, {"principals", m.Principals()}}, 2206 Update: bson.D{{"$set", bson.D{{"base", base}}}}, 2207 }} 2208 for _, unit := range units { 2209 ops = append(ops, txn.Op{ 2210 C: unitsC, 2211 Id: unit.doc.DocID, 2212 Assert: bson.D{{"life", Alive}, 2213 {"charmurl", unit.CharmURL()}, 2214 {"subordinates", unit.SubordinateNames()}}, 2215 Update: bson.D{{"$set", 2216 bson.D{{"base", base}}}}, 2217 }) 2218 } 2219 2220 return ops, nil 2221 } 2222 err := m.st.db().Run(buildTxn) 2223 return errors.Annotatef(err, "updating series for machine %q", m) 2224 } 2225 2226 // RecordAgentStartInformation updates the host name (if non-empty) reported 2227 // by the machine agent and sets the agent start time to the current time. 2228 func (m *Machine) RecordAgentStartInformation(hostname string) error { 2229 now := m.st.clock().Now() 2230 update := bson.D{ 2231 {"agent-started-at", now}, 2232 } 2233 2234 if hostname != "" { 2235 update = append(update, bson.DocElem{"hostname", hostname}) 2236 } 2237 2238 ops := []txn.Op{{ 2239 C: machinesC, 2240 Id: m.doc.DocID, 2241 Assert: notDeadDoc, 2242 Update: bson.D{{"$set", update}}, 2243 }} 2244 2245 if err := m.st.db().RunTransaction(ops); err != nil { 2246 // If instance doc doesn't exist, that's ok; there's nothing to keep, 2247 // but that's not an error we care about. 2248 return errors.Annotatef(onAbort(err, nil), "cannot update agent hostname/start time for machine %q", m) 2249 } 2250 m.doc.AgentStartedAt = now 2251 if hostname != "" { 2252 m.doc.Hostname = hostname 2253 } 2254 return nil 2255 } 2256 2257 // AgentStartTime returns the last recorded timestamp when the machine agent 2258 // was started. 2259 func (m *Machine) AgentStartTime() time.Time { 2260 return m.doc.AgentStartedAt 2261 } 2262 2263 // Hostname returns the hostname reported by the machine agent. 2264 func (m *Machine) Hostname() string { 2265 return m.doc.Hostname 2266 } 2267 2268 // AssertAliveOp returns an assert-only transaction operation 2269 // that ensures the machine is alive. 2270 func (m *Machine) AssertAliveOp() txn.Op { 2271 return txn.Op{ 2272 C: machinesC, 2273 Id: m.doc.DocID, 2274 Assert: isAliveDoc, 2275 } 2276 } 2277 2278 // assertMachineNotDeadOp returns an assert-only transaction operation that 2279 // ensures the machine is not dead. 2280 func assertMachineNotDeadOp(st *State, machineID string) txn.Op { 2281 return txn.Op{ 2282 C: machinesC, 2283 Id: st.docID(machineID), 2284 Assert: notDeadDoc, 2285 } 2286 } 2287 2288 // UpdateOperation returns a model operation that will update the machine. 2289 func (m *Machine) UpdateOperation() *UpdateMachineOperation { 2290 return &UpdateMachineOperation{m: &Machine{st: m.st, doc: m.doc}} 2291 } 2292 2293 // UpdateMachineOperation is a model operation for updating a machine. 2294 type UpdateMachineOperation struct { 2295 // m holds the machine to update. 2296 m *Machine 2297 2298 AgentVersion *version.Binary 2299 Constraints *constraints.Value 2300 MachineAddresses *[]network.SpaceAddress 2301 ProviderAddresses *[]network.SpaceAddress 2302 PasswordHash *string 2303 } 2304 2305 // Build is part of the ModelOperation interface. 2306 func (op *UpdateMachineOperation) Build(attempt int) ([]txn.Op, error) { 2307 if attempt > 0 { 2308 if err := op.m.Refresh(); err != nil { 2309 return nil, err 2310 } 2311 } 2312 2313 var allOps []txn.Op 2314 2315 if op.AgentVersion != nil { 2316 ops, _, err := op.m.setAgentVersionOps(*op.AgentVersion) 2317 if err != nil { 2318 return nil, errors.Annotate(err, "cannot set agent version") 2319 } 2320 allOps = append(allOps, ops...) 2321 } 2322 2323 if op.Constraints != nil { 2324 ops, err := op.m.setConstraintsOps(*op.Constraints) 2325 if err != nil { 2326 return nil, errors.Annotate(err, "cannot set constraints") 2327 } 2328 allOps = append(allOps, ops...) 2329 } 2330 2331 if op.MachineAddresses != nil || op.ProviderAddresses != nil { 2332 ops, _, _, _, _, err := op.m.setAddressesOps(op.MachineAddresses, op.ProviderAddresses) 2333 if err != nil { 2334 return nil, errors.Annotate(err, "cannot set addresses") 2335 } 2336 allOps = append(allOps, ops...) 2337 } 2338 2339 if op.PasswordHash != nil { 2340 ops, err := op.m.setPasswordHashOps(*op.PasswordHash) 2341 if err != nil { 2342 return nil, errors.Annotate(err, "cannot set password") 2343 } 2344 allOps = append(allOps, ops...) 2345 } 2346 2347 return allOps, nil 2348 } 2349 2350 // Done is part of the ModelOperation interface. 2351 func (op *UpdateMachineOperation) Done(err error) error { 2352 return errors.Annotatef(err, "updating machine %q", op.m) 2353 } 2354 2355 func (st *State) GetManualMachineArches() (set.Strings, error) { 2356 instanceDataCollection, closer := st.db().GetCollection(instanceDataC) 2357 defer closer() 2358 2359 var archDocs []struct { 2360 Arch string `bson:"arch"` 2361 } 2362 2363 err := instanceDataCollection.Find(bson.M{ 2364 "instanceid": bson.M{ 2365 "$regex": "^" + manualMachinePrefix, 2366 }, 2367 }).Select(bson.M{"arch": 1}).All(&archDocs) 2368 if err != nil { 2369 return nil, fmt.Errorf("cannot get the set of architectures for manual machines: %v", err) 2370 } 2371 2372 archSet := set.NewStrings() 2373 for _, archDoc := range archDocs { 2374 archSet.Add(archDoc.Arch) 2375 } 2376 return archSet, nil 2377 }