github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/uniter/unit.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/charm.v6" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/api/common" 12 apiwatcher "github.com/juju/juju/api/watcher" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/core/model" 15 "github.com/juju/juju/core/status" 16 "github.com/juju/juju/core/watcher" 17 ) 18 19 // Unit represents a juju unit as seen by a uniter worker. 20 type Unit struct { 21 st *State 22 tag names.UnitTag 23 life params.Life 24 resolvedMode params.ResolvedMode 25 } 26 27 // Tag returns the unit's tag. 28 func (u *Unit) Tag() names.UnitTag { 29 return u.tag 30 } 31 32 // Name returns the name of the unit. 33 func (u *Unit) Name() string { 34 return u.tag.Id() 35 } 36 37 // String returns the unit as a string. 38 func (u *Unit) String() string { 39 return u.Name() 40 } 41 42 // Life returns the unit's lifecycle value. 43 func (u *Unit) Life() params.Life { 44 return u.life 45 } 46 47 // Resolved returns the unit's resolved mode value. 48 func (u *Unit) Resolved() params.ResolvedMode { 49 return u.resolvedMode 50 } 51 52 // Refresh updates the cached local copy of the unit's data. 53 func (u *Unit) Refresh() error { 54 var results params.UnitRefreshResults 55 args := params.Entities{ 56 Entities: []params.Entity{ 57 {Tag: u.tag.String()}, 58 }, 59 } 60 err := u.st.facade.FacadeCall("Refresh", args, &results) 61 if err != nil { 62 return errors.Trace(err) 63 } 64 if len(results.Results) != 1 { 65 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 66 } 67 result := results.Results[0] 68 if result.Error != nil { 69 return errors.Trace(result.Error) 70 } 71 72 u.life = result.Life 73 u.resolvedMode = result.Resolved 74 return nil 75 } 76 77 // SetUnitStatus sets the status of the unit. 78 func (u *Unit) SetUnitStatus(unitStatus status.Status, info string, data map[string]interface{}) error { 79 if u.st.facade.BestAPIVersion() < 2 { 80 return errors.NotImplementedf("SetUnitStatus") 81 } 82 var result params.ErrorResults 83 args := params.SetStatus{ 84 Entities: []params.EntityStatusArgs{ 85 {Tag: u.tag.String(), Status: unitStatus.String(), Info: info, Data: data}, 86 }, 87 } 88 err := u.st.facade.FacadeCall("SetUnitStatus", args, &result) 89 if err != nil { 90 return errors.Trace(err) 91 } 92 return result.OneError() 93 } 94 95 // UnitStatus gets the status details of the unit. 96 func (u *Unit) UnitStatus() (params.StatusResult, error) { 97 var results params.StatusResults 98 args := params.Entities{ 99 Entities: []params.Entity{ 100 {Tag: u.tag.String()}, 101 }, 102 } 103 err := u.st.facade.FacadeCall("UnitStatus", args, &results) 104 if err != nil { 105 return params.StatusResult{}, errors.Trace(err) 106 } 107 if len(results.Results) != 1 { 108 return params.StatusResult{}, errors.Errorf("expected 1 result, got %d", len(results.Results)) 109 } 110 result := results.Results[0] 111 if result.Error != nil { 112 return params.StatusResult{}, result.Error 113 } 114 return result, nil 115 } 116 117 // SetAgentStatus sets the status of the unit agent. 118 func (u *Unit) SetAgentStatus(agentStatus status.Status, info string, data map[string]interface{}) error { 119 var result params.ErrorResults 120 args := params.SetStatus{ 121 Entities: []params.EntityStatusArgs{ 122 {Tag: u.tag.String(), Status: agentStatus.String(), Info: info, Data: data}, 123 }, 124 } 125 setStatusFacadeCall := "SetAgentStatus" 126 if u.st.facade.BestAPIVersion() < 2 { 127 setStatusFacadeCall = "SetStatus" 128 } 129 err := u.st.facade.FacadeCall(setStatusFacadeCall, args, &result) 130 if err != nil { 131 return err 132 } 133 return result.OneError() 134 } 135 136 // AddMetrics adds the metrics for the unit. 137 func (u *Unit) AddMetrics(metrics []params.Metric) error { 138 var result params.ErrorResults 139 args := params.MetricsParams{ 140 Metrics: []params.MetricsParam{{ 141 Tag: u.tag.String(), 142 Metrics: metrics, 143 }}, 144 } 145 err := u.st.facade.FacadeCall("AddMetrics", args, &result) 146 if err != nil { 147 return errors.Annotate(err, "unable to add metric") 148 } 149 return result.OneError() 150 } 151 152 // AddMetricsBatches makes an api call to the uniter requesting it to store metrics batches in state. 153 func (u *Unit) AddMetricBatches(batches []params.MetricBatch) (map[string]error, error) { 154 p := params.MetricBatchParams{ 155 Batches: make([]params.MetricBatchParam, len(batches)), 156 } 157 158 batchResults := make(map[string]error, len(batches)) 159 160 for i, batch := range batches { 161 p.Batches[i].Tag = u.tag.String() 162 p.Batches[i].Batch = batch 163 164 batchResults[batch.UUID] = nil 165 } 166 results := new(params.ErrorResults) 167 err := u.st.facade.FacadeCall("AddMetricBatches", p, results) 168 if err != nil { 169 return nil, errors.Annotate(err, "failed to send metric batches") 170 } 171 for i, result := range results.Results { 172 batchResults[batches[i].UUID] = result.Error 173 } 174 return batchResults, nil 175 } 176 177 // EnsureDead sets the unit lifecycle to Dead if it is Alive or 178 // Dying. It does nothing otherwise. 179 func (u *Unit) EnsureDead() error { 180 var result params.ErrorResults 181 args := params.Entities{ 182 Entities: []params.Entity{{Tag: u.tag.String()}}, 183 } 184 err := u.st.facade.FacadeCall("EnsureDead", args, &result) 185 if err != nil { 186 return err 187 } 188 return result.OneError() 189 } 190 191 // Watch returns a watcher for observing changes to the unit. 192 func (u *Unit) Watch() (watcher.NotifyWatcher, error) { 193 return common.Watch(u.st.facade, "Watch", u.tag) 194 } 195 196 // WatchRelations returns a StringsWatcher that notifies of changes to 197 // the lifecycles of relations involving u. 198 func (u *Unit) WatchRelations() (watcher.StringsWatcher, error) { 199 var results params.StringsWatchResults 200 args := params.Entities{ 201 Entities: []params.Entity{{Tag: u.tag.String()}}, 202 } 203 err := u.st.facade.FacadeCall("WatchUnitRelations", args, &results) 204 if err != nil { 205 return nil, err 206 } 207 if len(results.Results) != 1 { 208 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 209 } 210 result := results.Results[0] 211 if result.Error != nil { 212 return nil, result.Error 213 } 214 w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result) 215 return w, nil 216 } 217 218 // Application returns the unit's application. 219 func (u *Unit) Application() (*Application, error) { 220 application := &Application{ 221 st: u.st, 222 tag: u.ApplicationTag(), 223 } 224 // Call Refresh() immediately to get the up-to-date 225 // life and other needed locally cached fields. 226 err := application.Refresh() 227 if err != nil { 228 return nil, err 229 } 230 return application, nil 231 } 232 233 // ConfigSettings returns the complete set of application charm config settings 234 // available to the unit. Unset values will be replaced with the default 235 // value for the associated option, and may thus be nil when no default is 236 // specified. 237 func (u *Unit) ConfigSettings() (charm.Settings, error) { 238 var results params.ConfigSettingsResults 239 args := params.Entities{ 240 Entities: []params.Entity{{Tag: u.tag.String()}}, 241 } 242 err := u.st.facade.FacadeCall("ConfigSettings", args, &results) 243 if err != nil { 244 return nil, err 245 } 246 if len(results.Results) != 1 { 247 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 248 } 249 result := results.Results[0] 250 if result.Error != nil { 251 return nil, result.Error 252 } 253 return charm.Settings(result.Settings), nil 254 } 255 256 // ApplicationName returns the application name. 257 func (u *Unit) ApplicationName() string { 258 application, err := names.UnitApplication(u.Name()) 259 if err != nil { 260 panic(err) 261 } 262 return application 263 } 264 265 // ApplicationTag returns the application tag. 266 func (u *Unit) ApplicationTag() names.ApplicationTag { 267 return names.NewApplicationTag(u.ApplicationName()) 268 } 269 270 // Destroy, when called on a Alive unit, advances its lifecycle as far as 271 // possible; it otherwise has no effect. In most situations, the unit's 272 // life is just set to Dying; but if a principal unit that is not assigned 273 // to a provisioned machine is Destroyed, it will be removed from state 274 // directly. 275 func (u *Unit) Destroy() error { 276 var result params.ErrorResults 277 args := params.Entities{ 278 Entities: []params.Entity{{Tag: u.tag.String()}}, 279 } 280 err := u.st.facade.FacadeCall("Destroy", args, &result) 281 if err != nil { 282 return err 283 } 284 return result.OneError() 285 } 286 287 // DestroyAllSubordinates destroys all subordinates of the unit. 288 func (u *Unit) DestroyAllSubordinates() error { 289 var result params.ErrorResults 290 args := params.Entities{ 291 Entities: []params.Entity{{Tag: u.tag.String()}}, 292 } 293 err := u.st.facade.FacadeCall("DestroyAllSubordinates", args, &result) 294 if err != nil { 295 return err 296 } 297 return result.OneError() 298 } 299 300 // AssignedMachine returns the unit's assigned machine tag or an error 301 // satisfying params.IsCodeNotAssigned when the unit has no assigned 302 // machine.. 303 func (u *Unit) AssignedMachine() (names.MachineTag, error) { 304 if u.st.BestAPIVersion() < 1 { 305 return names.MachineTag{}, errors.NotImplementedf("unit.AssignedMachine() (need V1+)") 306 } 307 var invalidTag names.MachineTag 308 var results params.StringResults 309 args := params.Entities{ 310 Entities: []params.Entity{{Tag: u.tag.String()}}, 311 } 312 err := u.st.facade.FacadeCall("AssignedMachine", args, &results) 313 if err != nil { 314 return invalidTag, err 315 } 316 if len(results.Results) != 1 { 317 return invalidTag, errors.Errorf("expected 1 result, got %d", len(results.Results)) 318 } 319 result := results.Results[0] 320 if result.Error != nil { 321 return invalidTag, result.Error 322 } 323 return names.ParseMachineTag(result.Result) 324 } 325 326 // PrincipalName returns the principal unit name and true for subordinates. 327 // For principal units the function returns "" and false. 328 // 329 // NOTE: This differs from state.Unit.PrincipalName() by returning an 330 // error as well, because it needs to make an API call. 331 func (u *Unit) PrincipalName() (string, bool, error) { 332 var results params.StringBoolResults 333 args := params.Entities{ 334 Entities: []params.Entity{{Tag: u.tag.String()}}, 335 } 336 err := u.st.facade.FacadeCall("GetPrincipal", args, &results) 337 if err != nil { 338 return "", false, err 339 } 340 if len(results.Results) != 1 { 341 return "", false, errors.Errorf("expected 1 result, got %d", len(results.Results)) 342 } 343 result := results.Results[0] 344 if result.Error != nil { 345 return "", false, result.Error 346 } 347 var unitName string 348 if result.Ok { 349 unitTag, err := names.ParseUnitTag(result.Result) 350 if err != nil { 351 return "", false, err 352 } 353 unitName = unitTag.Id() 354 } 355 return unitName, result.Ok, nil 356 } 357 358 // HasSubordinates returns the tags of any subordinate units. 359 func (u *Unit) HasSubordinates() (bool, error) { 360 var results params.BoolResults 361 args := params.Entities{ 362 Entities: []params.Entity{{Tag: u.tag.String()}}, 363 } 364 err := u.st.facade.FacadeCall("HasSubordinates", args, &results) 365 if err != nil { 366 return false, err 367 } 368 if len(results.Results) != 1 { 369 return false, errors.Errorf("expected 1 result, got %d", len(results.Results)) 370 } 371 result := results.Results[0] 372 if result.Error != nil { 373 return false, result.Error 374 } 375 return result.Result, nil 376 } 377 378 // PublicAddress returns the public address of the unit and whether it 379 // is valid. 380 // 381 // NOTE: This differs from state.Unit.PublicAddres() by returning 382 // an error instead of a bool, because it needs to make an API call. 383 // 384 // TODO(dimitern): We might be able to drop this, once we have machine 385 // addresses implemented fully. See also LP bug 1221798. 386 func (u *Unit) PublicAddress() (string, error) { 387 var results params.StringResults 388 args := params.Entities{ 389 Entities: []params.Entity{{Tag: u.tag.String()}}, 390 } 391 err := u.st.facade.FacadeCall("PublicAddress", args, &results) 392 if err != nil { 393 return "", err 394 } 395 if len(results.Results) != 1 { 396 return "", errors.Errorf("expected 1 result, got %d", len(results.Results)) 397 } 398 result := results.Results[0] 399 if result.Error != nil { 400 return "", result.Error 401 } 402 return result.Result, nil 403 } 404 405 // PrivateAddress returns the private address of the unit and whether 406 // it is valid. 407 // 408 // NOTE: This differs from state.Unit.PrivateAddress() by returning 409 // an error instead of a bool, because it needs to make an API call. 410 // 411 // TODO(dimitern): We might be able to drop this, once we have machine 412 // addresses implemented fully. See also LP bug 1221798. 413 func (u *Unit) PrivateAddress() (string, error) { 414 var results params.StringResults 415 args := params.Entities{ 416 Entities: []params.Entity{{Tag: u.tag.String()}}, 417 } 418 err := u.st.facade.FacadeCall("PrivateAddress", args, &results) 419 if err != nil { 420 return "", err 421 } 422 if len(results.Results) != 1 { 423 return "", errors.Errorf("expected 1 result, got %d", len(results.Results)) 424 } 425 result := results.Results[0] 426 if result.Error != nil { 427 return "", result.Error 428 } 429 return result.Result, nil 430 } 431 432 // AvailabilityZone returns the availability zone of the unit. 433 func (u *Unit) AvailabilityZone() (string, error) { 434 var results params.StringResults 435 args := params.Entities{ 436 Entities: []params.Entity{{Tag: u.tag.String()}}, 437 } 438 if err := u.st.facade.FacadeCall("AvailabilityZone", args, &results); err != nil { 439 return "", errors.Trace(err) 440 } 441 if len(results.Results) != 1 { 442 return "", errors.Errorf("expected 1 result, got %d", len(results.Results)) 443 } 444 result := results.Results[0] 445 if result.Error != nil { 446 return "", errors.Trace(result.Error) 447 } 448 return result.Result, nil 449 } 450 451 // OpenPorts sets the policy of the port range with protocol to be 452 // opened. 453 func (u *Unit) OpenPorts(protocol string, fromPort, toPort int) error { 454 var result params.ErrorResults 455 args := params.EntitiesPortRanges{ 456 Entities: []params.EntityPortRange{{ 457 Tag: u.tag.String(), 458 Protocol: protocol, 459 FromPort: fromPort, 460 ToPort: toPort, 461 }}, 462 } 463 err := u.st.facade.FacadeCall("OpenPorts", args, &result) 464 if err != nil { 465 return err 466 } 467 return result.OneError() 468 } 469 470 // ClosePorts sets the policy of the port range with protocol to be 471 // closed. 472 func (u *Unit) ClosePorts(protocol string, fromPort, toPort int) error { 473 var result params.ErrorResults 474 args := params.EntitiesPortRanges{ 475 Entities: []params.EntityPortRange{{ 476 Tag: u.tag.String(), 477 Protocol: protocol, 478 FromPort: fromPort, 479 ToPort: toPort, 480 }}, 481 } 482 err := u.st.facade.FacadeCall("ClosePorts", args, &result) 483 if err != nil { 484 return err 485 } 486 return result.OneError() 487 } 488 489 var ErrNoCharmURLSet = errors.New("unit has no charm url set") 490 491 // CharmURL returns the charm URL this unit is currently using. 492 // 493 // NOTE: This differs from state.Unit.CharmURL() by returning 494 // an error instead of a bool, because it needs to make an API call. 495 func (u *Unit) CharmURL() (*charm.URL, error) { 496 var results params.StringBoolResults 497 args := params.Entities{ 498 Entities: []params.Entity{{Tag: u.tag.String()}}, 499 } 500 err := u.st.facade.FacadeCall("CharmURL", args, &results) 501 if err != nil { 502 return nil, err 503 } 504 if len(results.Results) != 1 { 505 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 506 } 507 result := results.Results[0] 508 if result.Error != nil { 509 return nil, result.Error 510 } 511 if result.Result != "" { 512 curl, err := charm.ParseURL(result.Result) 513 if err != nil { 514 return nil, err 515 } 516 return curl, nil 517 } 518 return nil, ErrNoCharmURLSet 519 } 520 521 // SetCharmURL marks the unit as currently using the supplied charm URL. 522 // An error will be returned if the unit is dead, or the charm URL not known. 523 func (u *Unit) SetCharmURL(curl *charm.URL) error { 524 if curl == nil { 525 return errors.Errorf("charm URL cannot be nil") 526 } 527 var result params.ErrorResults 528 args := params.EntitiesCharmURL{ 529 Entities: []params.EntityCharmURL{ 530 {Tag: u.tag.String(), CharmURL: curl.String()}, 531 }, 532 } 533 err := u.st.facade.FacadeCall("SetCharmURL", args, &result) 534 if err != nil { 535 return err 536 } 537 return result.OneError() 538 } 539 540 // ClearResolved removes any resolved setting on the unit. 541 func (u *Unit) ClearResolved() error { 542 var result params.ErrorResults 543 args := params.Entities{ 544 Entities: []params.Entity{{Tag: u.tag.String()}}, 545 } 546 err := u.st.facade.FacadeCall("ClearResolved", args, &result) 547 if err != nil { 548 return err 549 } 550 return result.OneError() 551 } 552 553 // WatchConfigSettingsHash returns a watcher for observing changes to 554 // the unit's charm configuration settings (with a hash of the 555 // settings content so we can determine whether it has changed since 556 // it was last seen by the uniter). The unit must have a charm URL set 557 // before this method is called, and the returned watcher will be 558 // valid only while the unit's charm URL is not changed. 559 func (u *Unit) WatchConfigSettingsHash() (watcher.StringsWatcher, error) { 560 return getHashWatcher(u, "WatchConfigSettingsHash") 561 } 562 563 // WatchTrustConfigSettingsHash returns a watcher for observing changes to 564 // the unit's application configuration settings (with a hash of the 565 // settings content so we can determine whether it has changed since 566 // it was last seen by the uniter). 567 func (u *Unit) WatchTrustConfigSettingsHash() (watcher.StringsWatcher, error) { 568 return getHashWatcher(u, "WatchTrustConfigSettingsHash") 569 } 570 571 func getHashWatcher(u *Unit, methodName string) (watcher.StringsWatcher, error) { 572 var results params.StringsWatchResults 573 args := params.Entities{ 574 Entities: []params.Entity{{Tag: u.tag.String()}}, 575 } 576 err := u.st.facade.FacadeCall(methodName, args, &results) 577 if err != nil { 578 return nil, err 579 } 580 if len(results.Results) != 1 { 581 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 582 } 583 result := results.Results[0] 584 if result.Error != nil { 585 return nil, result.Error 586 } 587 w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result) 588 return w, nil 589 } 590 591 // WatchAddressesHash returns a watcher for observing changes to the 592 // hash of the unit's addresses. 593 // For IAAS models, the unit must be assigned to a machine before 594 // this method is called, and the returned watcher will be valid 595 // only while the unit's assigned machine is not changed. 596 // For CAAS models, the watcher observes changes to the address 597 // of the pod associated with the unit. 598 func (u *Unit) WatchAddressesHash() (watcher.StringsWatcher, error) { 599 return getHashWatcher(u, "WatchUnitAddressesHash") 600 } 601 602 // WatchActionNotifications returns a StringsWatcher for observing the 603 // ids of Actions added to the Unit. The initial event will contain the 604 // ids of any Actions pending at the time the Watcher is made. 605 func (u *Unit) WatchActionNotifications() (watcher.StringsWatcher, error) { 606 var results params.StringsWatchResults 607 args := params.Entities{ 608 Entities: []params.Entity{{Tag: u.tag.String()}}, 609 } 610 err := u.st.facade.FacadeCall("WatchActionNotifications", args, &results) 611 if err != nil { 612 return nil, err 613 } 614 if len(results.Results) != 1 { 615 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 616 } 617 result := results.Results[0] 618 if result.Error != nil { 619 return nil, result.Error 620 } 621 w := apiwatcher.NewStringsWatcher(u.st.facade.RawAPICaller(), result) 622 return w, nil 623 } 624 625 // WatchUpgradeSeriesNotifications returns a NotifyWatcher for observing the 626 // state of a series upgrade. 627 func (u *Unit) WatchUpgradeSeriesNotifications() (watcher.NotifyWatcher, error) { 628 return u.st.WatchUpgradeSeriesNotifications() 629 } 630 631 // UpgradeSeriesStatus returns the upgrade series status of a unit from remote state 632 func (u *Unit) UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error) { 633 res, err := u.st.UpgradeSeriesUnitStatus() 634 if err != nil { 635 return "", errors.Trace(err) 636 } 637 if len(res) != 1 { 638 return "", errors.Errorf("expected 1 result, got %d", len(res)) 639 } 640 return res[0], nil 641 } 642 643 // SetUpgradeSeriesStatus sets the upgrade series status of the unit in the remote state 644 func (u *Unit) SetUpgradeSeriesStatus(status model.UpgradeSeriesStatus, reason string) error { 645 return u.st.SetUpgradeSeriesUnitStatus(status, reason) 646 } 647 648 // WatchLXDProfileUpgradeNotifications returns a StringsWatcher for observing the 649 // state of a lxd profile upgrade 650 func (u *Unit) WatchLXDProfileUpgradeNotifications() (watcher.StringsWatcher, error) { 651 return u.st.WatchLXDProfileUpgradeNotifications(u.ApplicationName()) 652 } 653 654 // RemoveUpgradeCharmProfileData removes the upgrade charm profile data 655 func (u *Unit) RemoveUpgradeCharmProfileData() error { 656 return u.st.RemoveUpgradeCharmProfileData() 657 } 658 659 // RequestReboot sets the reboot flag for its machine agent 660 func (u *Unit) RequestReboot() error { 661 machineId, err := u.AssignedMachine() 662 if err != nil { 663 return err 664 } 665 var result params.ErrorResults 666 args := params.Entities{ 667 Entities: []params.Entity{{Tag: machineId.String()}}, 668 } 669 err = u.st.facade.FacadeCall("RequestReboot", args, &result) 670 if err != nil { 671 return err 672 } 673 return result.OneError() 674 } 675 676 // RelationStatus holds information about a relation's scope and status. 677 type RelationStatus struct { 678 // Tag is the relation tag. 679 Tag names.RelationTag 680 681 // Suspended is true if the relation is suspended. 682 Suspended bool 683 684 // InScope is true if the relation unit is in scope. 685 InScope bool 686 } 687 688 // RelationsInScope returns the tags of the relations the unit has joined 689 // and entered scope, or the relation is suspended. 690 func (u *Unit) RelationsStatus() ([]RelationStatus, error) { 691 args := params.Entities{ 692 Entities: []params.Entity{{Tag: u.tag.String()}}, 693 } 694 var results params.RelationUnitStatusResults 695 err := u.st.facade.FacadeCall("RelationsStatus", args, &results) 696 if err != nil { 697 return nil, err 698 } 699 if len(results.Results) != 1 { 700 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 701 } 702 result := results.Results[0] 703 if result.Error != nil { 704 return nil, result.Error 705 } 706 var statusResult []RelationStatus 707 for _, result := range result.RelationResults { 708 tag, err := names.ParseRelationTag(result.RelationTag) 709 if err != nil { 710 return nil, err 711 } 712 statusResult = append(statusResult, RelationStatus{ 713 Tag: tag, 714 InScope: result.InScope, 715 Suspended: result.Suspended, 716 }) 717 } 718 return statusResult, nil 719 } 720 721 // MeterStatus returns the meter status of the unit. 722 func (u *Unit) MeterStatus() (statusCode, statusInfo string, rErr error) { 723 var results params.MeterStatusResults 724 args := params.Entities{ 725 Entities: []params.Entity{{Tag: u.tag.String()}}, 726 } 727 err := u.st.facade.FacadeCall("GetMeterStatus", args, &results) 728 if err != nil { 729 return "", "", errors.Trace(err) 730 } 731 if len(results.Results) != 1 { 732 return "", "", errors.Errorf("expected 1 result, got %d", len(results.Results)) 733 } 734 result := results.Results[0] 735 if result.Error != nil { 736 return "", "", errors.Trace(result.Error) 737 } 738 return result.Code, result.Info, nil 739 } 740 741 // WatchMeterStatus returns a watcher for observing changes to the 742 // unit's meter status. 743 func (u *Unit) WatchMeterStatus() (watcher.NotifyWatcher, error) { 744 var results params.NotifyWatchResults 745 args := params.Entities{ 746 Entities: []params.Entity{{Tag: u.tag.String()}}, 747 } 748 err := u.st.facade.FacadeCall("WatchMeterStatus", args, &results) 749 if err != nil { 750 return nil, err 751 } 752 if len(results.Results) != 1 { 753 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 754 } 755 result := results.Results[0] 756 if result.Error != nil { 757 return nil, result.Error 758 } 759 w := apiwatcher.NewNotifyWatcher(u.st.facade.RawAPICaller(), result) 760 return w, nil 761 } 762 763 // WatchStorage returns a watcher for observing changes to the 764 // unit's storage attachments. 765 func (u *Unit) WatchStorage() (watcher.StringsWatcher, error) { 766 return u.st.WatchUnitStorageAttachments(u.tag) 767 } 768 769 // AddStorage adds desired storage instances to a unit. 770 func (u *Unit) AddStorage(constraints map[string][]params.StorageConstraints) error { 771 if u.st.facade.BestAPIVersion() < 2 { 772 return errors.NotImplementedf("AddStorage() (need V2+)") 773 } 774 775 all := make([]params.StorageAddParams, 0, len(constraints)) 776 for storage, cons := range constraints { 777 for _, one := range cons { 778 all = append(all, params.StorageAddParams{ 779 UnitTag: u.Tag().String(), 780 StorageName: storage, 781 Constraints: one, 782 }) 783 } 784 } 785 786 args := params.StoragesAddParams{Storages: all} 787 var results params.ErrorResults 788 err := u.st.facade.FacadeCall("AddUnitStorage", args, &results) 789 if err != nil { 790 return err 791 } 792 793 return results.Combine() 794 } 795 796 // NetworkInfo returns network interfaces/addresses for specified bindings. 797 func (u *Unit) NetworkInfo(bindings []string, relationId *int) (map[string]params.NetworkInfoResult, error) { 798 var results params.NetworkInfoResults 799 args := params.NetworkInfoParams{ 800 Unit: u.tag.String(), 801 Bindings: bindings, 802 RelationId: relationId, 803 } 804 805 err := u.st.facade.FacadeCall("NetworkInfo", args, &results) 806 if err != nil { 807 return nil, errors.Trace(err) 808 } 809 810 return results.Results, nil 811 }