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