github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/watcher.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/kr/pretty" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/common/crossmodel" 12 "github.com/juju/juju/apiserver/common/storagecommon" 13 apiservererrors "github.com/juju/juju/apiserver/errors" 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/apiserver/facades/controller/crossmodelrelations" 16 "github.com/juju/juju/controller" 17 "github.com/juju/juju/core/cache" 18 "github.com/juju/juju/core/migration" 19 "github.com/juju/juju/core/multiwatcher" 20 "github.com/juju/juju/core/network" 21 coresecrets "github.com/juju/juju/core/secrets" 22 "github.com/juju/juju/core/status" 23 corewatcher "github.com/juju/juju/core/watcher" 24 "github.com/juju/juju/environs/config" 25 "github.com/juju/juju/rpc/params" 26 "github.com/juju/juju/state" 27 ) 28 29 type watcherCommon struct { 30 id string 31 resources facade.Resources 32 dispose func() 33 controller *cache.Controller 34 } 35 36 func newWatcherCommon(context facade.Context) watcherCommon { 37 return watcherCommon{ 38 context.ID(), 39 context.Resources(), 40 context.Dispose, 41 context.Controller(), 42 } 43 } 44 45 // Stop stops the watcher. 46 func (w *watcherCommon) Stop() error { 47 w.dispose() 48 return w.resources.Stop(w.id) 49 } 50 51 // SrvAllWatcher defines the API methods on a state.Multiwatcher. 52 // which watches any changes to the state. Each client has its own 53 // current set of watchers, stored in resources. It is used by both 54 // the AllWatcher and AllModelWatcher facades. 55 type SrvAllWatcher struct { 56 watcherCommon 57 watcher multiwatcher.Watcher 58 59 deltaTranslater DeltaTranslater 60 } 61 62 func newAllWatcher(context facade.Context, deltaTranslater DeltaTranslater) (*SrvAllWatcher, error) { 63 id := context.ID() 64 auth := context.Auth() 65 resources := context.Resources() 66 67 if !auth.AuthClient() { 68 // Note that we don't need to check specific permissions 69 // here, as the AllWatcher can only do anything if the 70 // watcher resource has already been created, so we can 71 // rely on the permission check there to ensure that 72 // this facade can't do anything it shouldn't be allowed 73 // to. 74 // 75 // This is useful because the AllWatcher is reused for 76 // both the WatchAll (requires model access rights) and 77 // the WatchAllModels (requring controller superuser 78 // rights) API calls. 79 return nil, apiservererrors.ErrPerm 80 } 81 watcher, ok := resources.Get(id).(multiwatcher.Watcher) 82 if !ok { 83 return nil, apiservererrors.ErrUnknownWatcher 84 } 85 return &SrvAllWatcher{ 86 watcherCommon: newWatcherCommon(context), 87 watcher: watcher, 88 deltaTranslater: deltaTranslater, 89 }, nil 90 } 91 92 // NewAllWatcher returns a new API server endpoint for interacting 93 // with a watcher created by the WatchAll and WatchAllModels API calls. 94 func NewAllWatcher(context facade.Context) (facade.Facade, error) { 95 return newAllWatcher(context, newAllWatcherDeltaTranslater()) 96 } 97 98 // Next will return the current state of everything on the first call 99 // and subsequent calls will 100 func (aw *SrvAllWatcher) Next() (params.AllWatcherNextResults, error) { 101 deltas, err := aw.watcher.Next() 102 return params.AllWatcherNextResults{ 103 Deltas: translate(aw.deltaTranslater, deltas), 104 }, err 105 } 106 107 type allWatcherDeltaTranslater struct { 108 DeltaTranslater 109 } 110 111 func newAllWatcherDeltaTranslater() DeltaTranslater { 112 return &allWatcherDeltaTranslater{} 113 } 114 115 // DeltaTranslater defines methods for translating multiwatcher.EntityInfo to params.EntityInfo. 116 type DeltaTranslater interface { 117 TranslateModel(multiwatcher.EntityInfo) params.EntityInfo 118 TranslateApplication(multiwatcher.EntityInfo) params.EntityInfo 119 TranslateRemoteApplication(multiwatcher.EntityInfo) params.EntityInfo 120 TranslateMachine(multiwatcher.EntityInfo) params.EntityInfo 121 TranslateUnit(multiwatcher.EntityInfo) params.EntityInfo 122 TranslateCharm(multiwatcher.EntityInfo) params.EntityInfo 123 TranslateRelation(multiwatcher.EntityInfo) params.EntityInfo 124 TranslateBranch(multiwatcher.EntityInfo) params.EntityInfo 125 TranslateAnnotation(multiwatcher.EntityInfo) params.EntityInfo 126 TranslateBlock(multiwatcher.EntityInfo) params.EntityInfo 127 TranslateAction(multiwatcher.EntityInfo) params.EntityInfo 128 TranslateApplicationOffer(multiwatcher.EntityInfo) params.EntityInfo 129 } 130 131 func translate(dt DeltaTranslater, deltas []multiwatcher.Delta) []params.Delta { 132 response := make([]params.Delta, 0, len(deltas)) 133 for _, delta := range deltas { 134 id := delta.Entity.EntityID() 135 var converted params.EntityInfo 136 switch id.Kind { 137 case multiwatcher.ModelKind: 138 converted = dt.TranslateModel(delta.Entity) 139 case multiwatcher.ApplicationKind: 140 converted = dt.TranslateApplication(delta.Entity) 141 case multiwatcher.RemoteApplicationKind: 142 converted = dt.TranslateRemoteApplication(delta.Entity) 143 case multiwatcher.MachineKind: 144 converted = dt.TranslateMachine(delta.Entity) 145 case multiwatcher.UnitKind: 146 converted = dt.TranslateUnit(delta.Entity) 147 case multiwatcher.CharmKind: 148 converted = dt.TranslateCharm(delta.Entity) 149 case multiwatcher.RelationKind: 150 converted = dt.TranslateRelation(delta.Entity) 151 case multiwatcher.BranchKind: 152 converted = dt.TranslateBranch(delta.Entity) 153 case multiwatcher.AnnotationKind: // THIS SEEMS WEIRD 154 // FIXME: annotations should be part of the underlying entity. 155 converted = dt.TranslateAnnotation(delta.Entity) 156 case multiwatcher.BlockKind: 157 converted = dt.TranslateBlock(delta.Entity) 158 case multiwatcher.ActionKind: 159 converted = dt.TranslateAction(delta.Entity) 160 case multiwatcher.ApplicationOfferKind: 161 converted = dt.TranslateApplicationOffer(delta.Entity) 162 default: 163 // converted stays nil 164 } 165 // It is possible that there are some multiwatcher elements that are 166 // internal, and not exposed outside the controller. 167 // Also this is a key place to start versioning the all watchers. 168 if converted != nil { 169 response = append(response, params.Delta{ 170 Removed: delta.Removed, 171 Entity: converted}) 172 } 173 } 174 return response 175 } 176 177 func (aw allWatcherDeltaTranslater) TranslateModel(info multiwatcher.EntityInfo) params.EntityInfo { 178 orig, ok := info.(*multiwatcher.ModelInfo) 179 if !ok { 180 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 181 return nil 182 } 183 184 var version string 185 if cfg, err := config.New(config.NoDefaults, orig.Config); err == nil { 186 versionNumber, _ := cfg.AgentVersion() 187 version = versionNumber.String() 188 } 189 190 return ¶ms.ModelUpdate{ 191 ModelUUID: orig.ModelUUID, 192 Name: orig.Name, 193 Life: orig.Life, 194 Owner: orig.Owner, 195 ControllerUUID: orig.ControllerUUID, 196 IsController: orig.IsController, 197 Config: orig.Config, 198 Status: aw.translateStatus(orig.Status), 199 Constraints: orig.Constraints, 200 SLA: params.ModelSLAInfo{ 201 Level: orig.SLA.Level, 202 Owner: orig.SLA.Owner, 203 }, 204 Type: orig.Type.String(), 205 Cloud: orig.Cloud, 206 CloudRegion: orig.CloudRegion, 207 Version: version, 208 } 209 } 210 211 func (aw allWatcherDeltaTranslater) translateStatus(info multiwatcher.StatusInfo) params.StatusInfo { 212 return params.StatusInfo{ 213 Err: info.Err, // CHECK THIS 214 Current: info.Current, 215 Message: info.Message, 216 Since: info.Since, 217 Version: info.Version, 218 Data: info.Data, 219 } 220 } 221 222 func (aw allWatcherDeltaTranslater) TranslateApplication(info multiwatcher.EntityInfo) params.EntityInfo { 223 orig, ok := info.(*multiwatcher.ApplicationInfo) 224 if !ok { 225 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 226 return nil 227 } 228 229 // If the application status is unset, then set it to unknown. It is 230 // expected that downstream clients (model cache, pylibjuju, jslibjuju) 231 // correctly interpret the unknown status from the unit status. If the unit 232 // status is not found, then fall back to unknown. 233 // If a charm author has set the application status, then show that instead. 234 applicationStatus := multiwatcher.StatusInfo{Current: status.Unset} 235 if orig.Status.Current != status.Unset { 236 applicationStatus = orig.Status 237 } 238 239 return ¶ms.ApplicationInfo{ 240 ModelUUID: orig.ModelUUID, 241 Name: orig.Name, 242 Exposed: orig.Exposed, 243 CharmURL: orig.CharmURL, 244 OwnerTag: orig.OwnerTag, 245 Life: orig.Life, 246 MinUnits: orig.MinUnits, 247 Constraints: orig.Constraints, 248 Config: orig.Config, 249 Subordinate: orig.Subordinate, 250 Status: aw.translateStatus(applicationStatus), 251 WorkloadVersion: orig.WorkloadVersion, 252 } 253 } 254 255 func (aw allWatcherDeltaTranslater) TranslateMachine(info multiwatcher.EntityInfo) params.EntityInfo { 256 orig, ok := info.(*multiwatcher.MachineInfo) 257 if !ok { 258 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 259 return nil 260 } 261 return ¶ms.MachineInfo{ 262 ModelUUID: orig.ModelUUID, 263 Id: orig.ID, 264 InstanceId: orig.InstanceID, 265 AgentStatus: aw.translateStatus(orig.AgentStatus), 266 InstanceStatus: aw.translateStatus(orig.InstanceStatus), 267 Life: orig.Life, 268 Config: orig.Config, 269 Base: orig.Base, 270 ContainerType: orig.ContainerType, 271 IsManual: orig.IsManual, 272 SupportedContainers: orig.SupportedContainers, 273 SupportedContainersKnown: orig.SupportedContainersKnown, 274 HardwareCharacteristics: orig.HardwareCharacteristics, 275 CharmProfiles: orig.CharmProfiles, 276 Jobs: orig.Jobs, 277 Addresses: aw.translateAddresses(orig.Addresses), 278 HasVote: orig.HasVote, 279 WantsVote: orig.WantsVote, 280 Hostname: orig.Hostname, 281 } 282 } 283 284 func (aw allWatcherDeltaTranslater) translateAddresses(addresses []network.ProviderAddress) []params.Address { 285 if addresses == nil { 286 return nil 287 } 288 result := make([]params.Address, 0, len(addresses)) 289 for _, address := range addresses { 290 result = append(result, params.Address{ 291 Value: address.Value, 292 Type: string(address.Type), 293 Scope: string(address.Scope), 294 SpaceName: string(address.SpaceName), 295 ProviderSpaceID: string(address.ProviderSpaceID), 296 }) 297 } 298 return result 299 } 300 301 func (aw allWatcherDeltaTranslater) TranslateCharm(info multiwatcher.EntityInfo) params.EntityInfo { 302 orig, ok := info.(*multiwatcher.CharmInfo) 303 if !ok { 304 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 305 return nil 306 } 307 return ¶ms.CharmInfo{ 308 ModelUUID: orig.ModelUUID, 309 CharmURL: orig.CharmURL, 310 CharmVersion: orig.CharmVersion, 311 Life: orig.Life, 312 LXDProfile: aw.translateProfile(orig.LXDProfile), 313 DefaultConfig: orig.DefaultConfig, 314 } 315 } 316 317 func (aw allWatcherDeltaTranslater) translateProfile(profile *multiwatcher.Profile) *params.Profile { 318 if profile == nil { 319 return nil 320 } 321 return ¶ms.Profile{ 322 Config: profile.Config, 323 Description: profile.Description, 324 Devices: profile.Devices, 325 } 326 } 327 328 func (aw allWatcherDeltaTranslater) TranslateRemoteApplication(info multiwatcher.EntityInfo) params.EntityInfo { 329 orig, ok := info.(*multiwatcher.RemoteApplicationUpdate) 330 if !ok { 331 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 332 return nil 333 } 334 return ¶ms.RemoteApplicationUpdate{ 335 ModelUUID: orig.ModelUUID, 336 Name: orig.Name, 337 OfferURL: orig.OfferURL, 338 Life: orig.Life, 339 Status: aw.translateStatus(orig.Status), 340 } 341 } 342 343 func (aw allWatcherDeltaTranslater) TranslateApplicationOffer(info multiwatcher.EntityInfo) params.EntityInfo { 344 orig, ok := info.(*multiwatcher.ApplicationOfferInfo) 345 if !ok { 346 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 347 return nil 348 } 349 return ¶ms.ApplicationOfferInfo{ 350 ModelUUID: orig.ModelUUID, 351 OfferName: orig.OfferName, 352 OfferUUID: orig.OfferUUID, 353 ApplicationName: orig.ApplicationName, 354 CharmName: orig.CharmName, 355 TotalConnectedCount: orig.TotalConnectedCount, 356 ActiveConnectedCount: orig.ActiveConnectedCount, 357 } 358 } 359 360 func (aw allWatcherDeltaTranslater) TranslateUnit(info multiwatcher.EntityInfo) params.EntityInfo { 361 orig, ok := info.(*multiwatcher.UnitInfo) 362 if !ok { 363 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 364 return nil 365 } 366 367 translatedPortRanges := aw.translatePortRanges(orig.OpenPortRangesByEndpoint) 368 369 return ¶ms.UnitInfo{ 370 ModelUUID: orig.ModelUUID, 371 Name: orig.Name, 372 Application: orig.Application, 373 Base: orig.Base, 374 CharmURL: orig.CharmURL, 375 Life: orig.Life, 376 PublicAddress: orig.PublicAddress, 377 PrivateAddress: orig.PrivateAddress, 378 MachineId: orig.MachineID, 379 Ports: aw.mapRangesIntoPorts(translatedPortRanges), 380 PortRanges: translatedPortRanges, 381 Principal: orig.Principal, 382 Subordinate: orig.Subordinate, 383 WorkloadStatus: aw.translateStatus(orig.WorkloadStatus), 384 AgentStatus: aw.translateStatus(orig.AgentStatus), 385 } 386 } 387 388 func (aw allWatcherDeltaTranslater) mapRangesIntoPorts(portRanges []params.PortRange) []params.Port { 389 if portRanges == nil { 390 return nil 391 } 392 result := make([]params.Port, 0, len(portRanges)) 393 for _, pr := range portRanges { 394 for portNum := pr.FromPort; portNum <= pr.ToPort; portNum++ { 395 result = append(result, params.Port{ 396 Protocol: pr.Protocol, 397 Number: portNum, 398 }) 399 } 400 } 401 return result 402 } 403 404 // translatePortRanges flattens a set of port ranges grouped by endpoint into 405 // a list of unique port ranges. This method ignores subnet IDs and is provided 406 // for backwards compatibility with pre 2.9 clients that assume that open-ports 407 // applies to all subnets. 408 func (aw allWatcherDeltaTranslater) translatePortRanges(portsByEndpoint network.GroupedPortRanges) []params.PortRange { 409 if portsByEndpoint == nil { 410 return nil 411 } 412 413 uniquePortRanges := portsByEndpoint.UniquePortRanges() 414 network.SortPortRanges(uniquePortRanges) 415 416 result := make([]params.PortRange, len(uniquePortRanges)) 417 for i, pr := range uniquePortRanges { 418 result[i] = params.FromNetworkPortRange(pr) 419 } 420 421 return result 422 } 423 424 func (aw allWatcherDeltaTranslater) TranslateAction(info multiwatcher.EntityInfo) params.EntityInfo { 425 orig, ok := info.(*multiwatcher.ActionInfo) 426 if !ok { 427 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 428 return nil 429 } 430 return ¶ms.ActionInfo{ 431 ModelUUID: orig.ModelUUID, 432 Id: orig.ID, 433 Receiver: orig.Receiver, 434 Name: orig.Name, 435 Status: orig.Status, 436 Message: orig.Message, 437 Enqueued: orig.Enqueued, 438 Started: orig.Started, 439 Completed: orig.Completed, 440 } 441 } 442 443 func (aw allWatcherDeltaTranslater) TranslateRelation(info multiwatcher.EntityInfo) params.EntityInfo { 444 orig, ok := info.(*multiwatcher.RelationInfo) 445 if !ok { 446 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 447 return nil 448 } 449 return ¶ms.RelationInfo{ 450 ModelUUID: orig.ModelUUID, 451 Key: orig.Key, 452 Id: orig.ID, 453 Endpoints: aw.translateEndpoints(orig.Endpoints), 454 } 455 } 456 457 func (aw allWatcherDeltaTranslater) translateEndpoints(eps []multiwatcher.Endpoint) []params.Endpoint { 458 if eps == nil { 459 return nil 460 } 461 result := make([]params.Endpoint, 0, len(eps)) 462 for _, ep := range eps { 463 result = append(result, params.Endpoint{ 464 ApplicationName: ep.ApplicationName, 465 Relation: params.CharmRelation{ 466 Name: ep.Relation.Name, 467 Role: ep.Relation.Role, 468 Interface: ep.Relation.Interface, 469 Optional: ep.Relation.Optional, 470 Limit: ep.Relation.Limit, 471 Scope: ep.Relation.Scope, 472 }, 473 }) 474 } 475 return result 476 } 477 478 func (aw allWatcherDeltaTranslater) TranslateAnnotation(info multiwatcher.EntityInfo) params.EntityInfo { 479 orig, ok := info.(*multiwatcher.AnnotationInfo) 480 if !ok { 481 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 482 return nil 483 } 484 return ¶ms.AnnotationInfo{ 485 ModelUUID: orig.ModelUUID, 486 Tag: orig.Tag, 487 Annotations: orig.Annotations, 488 } 489 } 490 491 func (aw allWatcherDeltaTranslater) TranslateBlock(info multiwatcher.EntityInfo) params.EntityInfo { 492 orig, ok := info.(*multiwatcher.BlockInfo) 493 if !ok { 494 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 495 return nil 496 } 497 return ¶ms.BlockInfo{ 498 ModelUUID: orig.ModelUUID, 499 Id: orig.ID, 500 Type: orig.Type, 501 Message: orig.Message, 502 Tag: orig.Tag, 503 } 504 } 505 506 func (aw allWatcherDeltaTranslater) TranslateBranch(info multiwatcher.EntityInfo) params.EntityInfo { 507 orig, ok := info.(*multiwatcher.BranchInfo) 508 if !ok { 509 logger.Criticalf("consistency error: %s", pretty.Sprint(info)) 510 return nil 511 } 512 return ¶ms.BranchInfo{ 513 ModelUUID: orig.ModelUUID, 514 Id: orig.ID, 515 Name: orig.Name, 516 AssignedUnits: orig.AssignedUnits, 517 Config: aw.translateBranchConfig(orig.Config), 518 Created: orig.Created, 519 CreatedBy: orig.CreatedBy, 520 Completed: orig.Completed, 521 CompletedBy: orig.CompletedBy, 522 GenerationId: orig.GenerationID, 523 } 524 } 525 526 func (aw allWatcherDeltaTranslater) translateBranchConfig(config map[string][]multiwatcher.ItemChange) map[string][]params.ItemChange { 527 if config == nil { 528 return nil 529 } 530 result := make(map[string][]params.ItemChange) 531 for key, value := range config { 532 if value == nil { 533 result[key] = nil 534 } else { 535 changes := make([]params.ItemChange, 0, len(value)) 536 for _, change := range value { 537 changes = append(changes, params.ItemChange{ 538 Type: change.Type, 539 Key: change.Key, 540 OldValue: change.OldValue, 541 NewValue: change.NewValue, 542 }) 543 } 544 result[key] = changes 545 } 546 } 547 return result 548 } 549 550 func isAgent(auth facade.Authorizer) bool { 551 return auth.AuthMachineAgent() || auth.AuthUnitAgent() || auth.AuthApplicationAgent() || auth.AuthModelAgent() 552 } 553 554 func isAgentOrUser(auth facade.Authorizer) bool { 555 return isAgent(auth) || auth.AuthClient() 556 } 557 558 func newNotifyWatcher(context facade.Context) (facade.Facade, error) { 559 id := context.ID() 560 auth := context.Auth() 561 resources := context.Resources() 562 563 // TODO(wallyworld) - enhance this watcher to support 564 // anonymous api calls with macaroons. 565 if auth.GetAuthTag() != nil && !isAgentOrUser(auth) { 566 return nil, apiservererrors.ErrPerm 567 } 568 569 watcher, ok := resources.Get(id).(cache.NotifyWatcher) 570 if !ok { 571 return nil, apiservererrors.ErrUnknownWatcher 572 } 573 574 return &srvNotifyWatcher{ 575 watcherCommon: newWatcherCommon(context), 576 watcher: watcher, 577 }, nil 578 } 579 580 // srvNotifyWatcher defines the API access to methods on a NotifyWatcher. 581 // Each client has its own current set of watchers, stored in resources. 582 type srvNotifyWatcher struct { 583 watcherCommon 584 watcher cache.NotifyWatcher 585 } 586 587 // state watchers have an Err method, but cache watchers do not. 588 type hasErr interface { 589 Err() error 590 } 591 592 // Next returns when a change has occurred to the 593 // entity being watched since the most recent call to Next 594 // or the Watch call that created the NotifyWatcher. 595 func (w *srvNotifyWatcher) Next() error { 596 if _, ok := <-w.watcher.Changes(); ok { 597 return nil 598 } 599 600 var err error 601 if e, ok := w.watcher.(hasErr); ok { 602 err = e.Err() 603 } 604 if err == nil { 605 err = apiservererrors.ErrStoppedWatcher 606 } 607 return err 608 } 609 610 // srvStringsWatcher defines the API for methods on a state.StringsWatcher. 611 // Each client has its own current set of watchers, stored in resources. 612 // srvStringsWatcher notifies about changes for all entities of a given kind, 613 // sending the changes as a list of strings. 614 type srvStringsWatcher struct { 615 watcherCommon 616 watcher cache.StringsWatcher 617 } 618 619 func newStringsWatcher(context facade.Context) (facade.Facade, error) { 620 id := context.ID() 621 auth := context.Auth() 622 resources := context.Resources() 623 624 // TODO(wallyworld) - enhance this watcher to support 625 // anonymous api calls with macaroons. 626 if auth.GetAuthTag() != nil && !isAgentOrUser(auth) { 627 return nil, apiservererrors.ErrPerm 628 } 629 watcher, ok := resources.Get(id).(cache.StringsWatcher) 630 if !ok { 631 return nil, apiservererrors.ErrUnknownWatcher 632 } 633 return &srvStringsWatcher{ 634 watcherCommon: newWatcherCommon(context), 635 watcher: watcher, 636 }, nil 637 } 638 639 // Next returns when a change has occurred to an entity of the 640 // collection being watched since the most recent call to Next 641 // or the Watch call that created the srvStringsWatcher. 642 func (w *srvStringsWatcher) Next() (params.StringsWatchResult, error) { 643 if changes, ok := <-w.watcher.Changes(); ok { 644 return params.StringsWatchResult{ 645 Changes: changes, 646 }, nil 647 } 648 var err error 649 if e, ok := w.watcher.(hasErr); ok { 650 err = e.Err() 651 } 652 if err == nil { 653 err = apiservererrors.ErrStoppedWatcher 654 } 655 return params.StringsWatchResult{}, err 656 } 657 658 // srvRelationUnitsWatcher defines the API wrapping a state.RelationUnitsWatcher. 659 // It notifies about units entering and leaving the scope of a RelationUnit, 660 // and changes to the settings of those units known to have entered. 661 type srvRelationUnitsWatcher struct { 662 watcherCommon 663 watcher common.RelationUnitsWatcher 664 } 665 666 func newRelationUnitsWatcher(context facade.Context) (facade.Facade, error) { 667 id := context.ID() 668 auth := context.Auth() 669 resources := context.Resources() 670 671 // TODO(wallyworld) - enhance this watcher to support 672 // anonymous api calls with macaroons. 673 if auth.GetAuthTag() != nil && !isAgent(auth) { 674 return nil, apiservererrors.ErrPerm 675 } 676 watcher, ok := resources.Get(id).(common.RelationUnitsWatcher) 677 if !ok { 678 return nil, apiservererrors.ErrUnknownWatcher 679 } 680 return &srvRelationUnitsWatcher{ 681 watcherCommon: newWatcherCommon(context), 682 watcher: watcher, 683 }, nil 684 } 685 686 // Next returns when a change has occurred to an entity of the 687 // collection being watched since the most recent call to Next 688 // or the Watch call that created the srvRelationUnitsWatcher. 689 func (w *srvRelationUnitsWatcher) Next() (params.RelationUnitsWatchResult, error) { 690 if changes, ok := <-w.watcher.Changes(); ok { 691 return params.RelationUnitsWatchResult{ 692 Changes: changes, 693 }, nil 694 } 695 err := w.watcher.Err() 696 if err == nil { 697 err = apiservererrors.ErrStoppedWatcher 698 } 699 return params.RelationUnitsWatchResult{}, err 700 } 701 702 // srvRemoteRelationWatcher defines the API wrapping a 703 // state.RelationUnitsWatcher but serving the events it emits as 704 // fully-expanded params.RemoteRelationChangeEvents so they can be 705 // used across model/controller boundaries. 706 type srvRemoteRelationWatcher struct { 707 watcherCommon 708 backend crossmodel.Backend 709 watcher *crossmodel.WrappedUnitsWatcher 710 } 711 712 func newRemoteRelationWatcher(context facade.Context) (facade.Facade, error) { 713 id := context.ID() 714 auth := context.Auth() 715 resources := context.Resources() 716 717 // TODO(wallyworld) - enhance this watcher to support 718 // anonymous api calls with macaroons. 719 if auth.GetAuthTag() != nil && !isAgent(auth) { 720 return nil, apiservererrors.ErrPerm 721 } 722 watcher, ok := resources.Get(id).(*crossmodel.WrappedUnitsWatcher) 723 if !ok { 724 return nil, apiservererrors.ErrUnknownWatcher 725 } 726 return &srvRemoteRelationWatcher{ 727 watcherCommon: newWatcherCommon(context), 728 backend: crossmodel.GetBackend(context.State()), 729 watcher: watcher, 730 }, nil 731 } 732 733 func (w *srvRemoteRelationWatcher) Next() (params.RemoteRelationWatchResult, error) { 734 if change, ok := <-w.watcher.Changes(); ok { 735 // Expand the change into a cross-model event. 736 expanded, err := crossmodel.ExpandChange( 737 w.backend, 738 w.watcher.RelationToken, 739 w.watcher.ApplicationToken, 740 change, 741 ) 742 if err != nil { 743 return params.RemoteRelationWatchResult{ 744 Error: apiservererrors.ServerError(err), 745 }, nil 746 } 747 return params.RemoteRelationWatchResult{ 748 Changes: expanded, 749 }, nil 750 } 751 err := w.watcher.Err() 752 if err == nil { 753 err = apiservererrors.ErrStoppedWatcher 754 } 755 return params.RemoteRelationWatchResult{}, err 756 } 757 758 // srvRelationStatusWatcher defines the API wrapping a state.RelationStatusWatcher. 759 type srvRelationStatusWatcher struct { 760 watcherCommon 761 st *state.State 762 watcher state.StringsWatcher 763 } 764 765 func newRelationStatusWatcher(context facade.Context) (facade.Facade, error) { 766 id := context.ID() 767 auth := context.Auth() 768 resources := context.Resources() 769 770 // TODO(wallyworld) - enhance this watcher to support 771 // anonymous api calls with macaroons. 772 if auth.GetAuthTag() != nil && !isAgent(auth) { 773 return nil, apiservererrors.ErrPerm 774 } 775 watcher, ok := resources.Get(id).(state.StringsWatcher) 776 if !ok { 777 return nil, apiservererrors.ErrUnknownWatcher 778 } 779 return &srvRelationStatusWatcher{ 780 watcherCommon: newWatcherCommon(context), 781 st: context.State(), 782 watcher: watcher, 783 }, nil 784 } 785 786 // Next returns when a change has occurred to an entity of the 787 // collection being watched since the most recent call to Next 788 // or the Watch call that created the srvRelationStatusWatcher. 789 func (w *srvRelationStatusWatcher) Next() (params.RelationLifeSuspendedStatusWatchResult, error) { 790 if changes, ok := <-w.watcher.Changes(); ok { 791 changesParams := make([]params.RelationLifeSuspendedStatusChange, len(changes)) 792 for i, key := range changes { 793 change, err := crossmodel.GetRelationLifeSuspendedStatusChange(crossmodel.GetBackend(w.st), key) 794 if err != nil { 795 return params.RelationLifeSuspendedStatusWatchResult{ 796 Error: apiservererrors.ServerError(err), 797 }, nil 798 } 799 changesParams[i] = *change 800 } 801 return params.RelationLifeSuspendedStatusWatchResult{ 802 Changes: changesParams, 803 }, nil 804 } 805 err := w.watcher.Err() 806 if err == nil { 807 err = apiservererrors.ErrStoppedWatcher 808 } 809 return params.RelationLifeSuspendedStatusWatchResult{}, err 810 } 811 812 // srvOfferStatusWatcher defines the API wrapping a crossmodelrelations.OfferStatusWatcher. 813 type srvOfferStatusWatcher struct { 814 watcherCommon 815 st *state.State 816 watcher crossmodelrelations.OfferWatcher 817 } 818 819 func newOfferStatusWatcher(context facade.Context) (facade.Facade, error) { 820 id := context.ID() 821 auth := context.Auth() 822 resources := context.Resources() 823 824 st := context.State() 825 826 // TODO(wallyworld) - enhance this watcher to support 827 // anonymous api calls with macaroons. 828 if auth.GetAuthTag() != nil && !isAgent(auth) { 829 return nil, apiservererrors.ErrPerm 830 } 831 watcher, ok := resources.Get(id).(crossmodelrelations.OfferWatcher) 832 if !ok { 833 return nil, apiservererrors.ErrUnknownWatcher 834 } 835 return &srvOfferStatusWatcher{ 836 watcherCommon: newWatcherCommon(context), 837 st: st, 838 watcher: watcher, 839 }, nil 840 } 841 842 // Next returns when a change has occurred to an entity of the 843 // collection being watched since the most recent call to Next 844 // or the Watch call that created the srvOfferStatusWatcher. 845 func (w *srvOfferStatusWatcher) Next() (params.OfferStatusWatchResult, error) { 846 if _, ok := <-w.watcher.Changes(); ok { 847 change, err := crossmodel.GetOfferStatusChange( 848 crossmodel.GetBackend(w.st), 849 w.watcher.OfferUUID(), w.watcher.OfferName()) 850 if err != nil { 851 // For the specific case where we are informed that a migration is 852 // in progress, we want to return an error that causes the client 853 // to stop watching, rather than in the payload. 854 if errors.Is(err, migration.ErrMigrating) { 855 return params.OfferStatusWatchResult{}, err 856 } 857 858 return params.OfferStatusWatchResult{Error: apiservererrors.ServerError(err)}, nil 859 } 860 return params.OfferStatusWatchResult{ 861 Changes: []params.OfferStatusChange{*change}, 862 }, nil 863 } 864 err := w.watcher.Err() 865 if err == nil { 866 err = apiservererrors.ErrStoppedWatcher 867 } 868 return params.OfferStatusWatchResult{}, err 869 } 870 871 // srvMachineStorageIdsWatcher defines the API wrapping a state.StringsWatcher 872 // watching machine/storage attachments. This watcher notifies about storage 873 // entities (volumes/filesystems) being attached to and detached from machines. 874 // 875 // TODO(axw) state needs a new watcher, this is a bt of a hack. State watchers 876 // could do with some deduplication of logic, and I don't want to add to that 877 // spaghetti right now. 878 type srvMachineStorageIdsWatcher struct { 879 watcherCommon 880 watcher state.StringsWatcher 881 parser func([]string) ([]params.MachineStorageId, error) 882 } 883 884 func newVolumeAttachmentsWatcher(context facade.Context) (facade.Facade, error) { 885 return newMachineStorageIdsWatcher( 886 context, 887 storagecommon.ParseVolumeAttachmentIds, 888 ) 889 } 890 891 func newVolumeAttachmentPlansWatcher(context facade.Context) (facade.Facade, error) { 892 return newMachineStorageIdsWatcher( 893 context, 894 storagecommon.ParseVolumeAttachmentIds, 895 ) 896 } 897 898 func newFilesystemAttachmentsWatcher(context facade.Context) (facade.Facade, error) { 899 return newMachineStorageIdsWatcher( 900 context, 901 storagecommon.ParseFilesystemAttachmentIds, 902 ) 903 } 904 905 func newMachineStorageIdsWatcher( 906 context facade.Context, 907 parser func([]string) ([]params.MachineStorageId, error), 908 ) (facade.Facade, error) { 909 id := context.ID() 910 auth := context.Auth() 911 resources := context.Resources() 912 if !isAgent(auth) { 913 return nil, apiservererrors.ErrPerm 914 } 915 watcher, ok := resources.Get(id).(state.StringsWatcher) 916 if !ok { 917 return nil, apiservererrors.ErrUnknownWatcher 918 } 919 return &srvMachineStorageIdsWatcher{ 920 watcherCommon: newWatcherCommon(context), 921 watcher: watcher, 922 parser: parser, 923 }, nil 924 } 925 926 // Next returns when a change has occurred to an entity of the 927 // collection being watched since the most recent call to Next 928 // or the Watch call that created the srvMachineStorageIdsWatcher. 929 func (w *srvMachineStorageIdsWatcher) Next() (params.MachineStorageIdsWatchResult, error) { 930 if stringChanges, ok := <-w.watcher.Changes(); ok { 931 changes, err := w.parser(stringChanges) 932 if err != nil { 933 return params.MachineStorageIdsWatchResult{}, err 934 } 935 return params.MachineStorageIdsWatchResult{ 936 Changes: changes, 937 }, nil 938 } 939 err := w.watcher.Err() 940 if err == nil { 941 err = apiservererrors.ErrStoppedWatcher 942 } 943 return params.MachineStorageIdsWatchResult{}, err 944 } 945 946 // EntitiesWatcher defines an interface based on the StringsWatcher 947 // but also providing a method for the mapping of the received 948 // strings to the tags of the according entities. 949 type EntitiesWatcher interface { 950 state.StringsWatcher 951 952 // MapChanges maps the received strings to their according tag strings. 953 // The EntityFinder interface representing state or a mock has to be 954 // upcasted into the needed sub-interface of state for the real mapping. 955 MapChanges(in []string) ([]string, error) 956 } 957 958 // srvEntitiesWatcher defines the API for methods on a state.StringsWatcher. 959 // Each client has its own current set of watchers, stored in resources. 960 // srvEntitiesWatcher notifies about changes for all entities of a given kind, 961 // sending the changes as a list of strings, which could be transformed 962 // from state entity ids to their corresponding entity tags. 963 type srvEntitiesWatcher struct { 964 watcherCommon 965 watcher EntitiesWatcher 966 } 967 968 func newEntitiesWatcher(context facade.Context) (facade.Facade, error) { 969 id := context.ID() 970 auth := context.Auth() 971 resources := context.Resources() 972 973 if !isAgent(auth) { 974 return nil, apiservererrors.ErrPerm 975 } 976 watcher, ok := resources.Get(id).(EntitiesWatcher) 977 if !ok { 978 return nil, apiservererrors.ErrUnknownWatcher 979 } 980 return &srvEntitiesWatcher{ 981 watcherCommon: newWatcherCommon(context), 982 watcher: watcher, 983 }, nil 984 } 985 986 // Next returns when a change has occurred to an entity of the 987 // collection being watched since the most recent call to Next 988 // or the Watch call that created the srvEntitiesWatcher. 989 func (w *srvEntitiesWatcher) Next() (params.EntitiesWatchResult, error) { 990 if changes, ok := <-w.watcher.Changes(); ok { 991 mapped, err := w.watcher.MapChanges(changes) 992 if err != nil { 993 return params.EntitiesWatchResult{}, errors.Annotate(err, "cannot map changes") 994 } 995 return params.EntitiesWatchResult{ 996 Changes: mapped, 997 }, nil 998 } 999 err := w.watcher.Err() 1000 if err == nil { 1001 err = apiservererrors.ErrStoppedWatcher 1002 } 1003 return params.EntitiesWatchResult{}, err 1004 } 1005 1006 var getMigrationBackend = func(st *state.State) migrationBackend { 1007 return st 1008 } 1009 1010 var getControllerBackend = func(pool *state.StatePool) (controllerBackend, error) { 1011 return pool.SystemState() 1012 } 1013 1014 // migrationBackend defines model State functionality required by the 1015 // migration watchers. 1016 type migrationBackend interface { 1017 LatestMigration() (state.ModelMigration, error) 1018 } 1019 1020 // migrationBackend defines controller State functionality required by the 1021 // migration watchers. 1022 type controllerBackend interface { 1023 APIHostPortsForClients() ([]network.SpaceHostPorts, error) 1024 ControllerConfig() (controller.Config, error) 1025 } 1026 1027 func newMigrationStatusWatcher(context facade.Context) (facade.Facade, error) { 1028 id := context.ID() 1029 auth := context.Auth() 1030 resources := context.Resources() 1031 st := context.State() 1032 pool := context.StatePool() 1033 1034 if !isAgent(auth) { 1035 return nil, apiservererrors.ErrPerm 1036 } 1037 w, ok := resources.Get(id).(state.NotifyWatcher) 1038 if !ok { 1039 return nil, apiservererrors.ErrUnknownWatcher 1040 } 1041 controllerBackend, err := getControllerBackend(pool) 1042 if err != nil { 1043 return nil, errors.Trace(err) 1044 } 1045 return &srvMigrationStatusWatcher{ 1046 watcherCommon: newWatcherCommon(context), 1047 watcher: w, 1048 st: getMigrationBackend(st), 1049 ctrlSt: controllerBackend, 1050 }, nil 1051 } 1052 1053 type srvMigrationStatusWatcher struct { 1054 watcherCommon 1055 watcher state.NotifyWatcher 1056 st migrationBackend 1057 ctrlSt controllerBackend 1058 } 1059 1060 // Next returns when the status for a model migration for the 1061 // associated model changes. The current details for the active 1062 // migration are returned. 1063 func (w *srvMigrationStatusWatcher) Next() (params.MigrationStatus, error) { 1064 empty := params.MigrationStatus{} 1065 1066 if _, ok := <-w.watcher.Changes(); !ok { 1067 err := w.watcher.Err() 1068 if err == nil { 1069 err = apiservererrors.ErrStoppedWatcher 1070 } 1071 return empty, err 1072 } 1073 1074 mig, err := w.st.LatestMigration() 1075 if errors.IsNotFound(err) { 1076 return params.MigrationStatus{ 1077 Phase: migration.NONE.String(), 1078 }, nil 1079 } else if err != nil { 1080 return empty, errors.Annotate(err, "migration lookup") 1081 } 1082 1083 phase, err := mig.Phase() 1084 if err != nil { 1085 return empty, errors.Annotate(err, "retrieving migration phase") 1086 } 1087 1088 sourceAddrs, err := w.getLocalHostPorts() 1089 if err != nil { 1090 return empty, errors.Annotate(err, "retrieving source addresses") 1091 } 1092 1093 sourceCACert, err := getControllerCACert(w.ctrlSt) 1094 if err != nil { 1095 return empty, errors.Annotate(err, "retrieving source CA cert") 1096 } 1097 1098 target, err := mig.TargetInfo() 1099 if err != nil { 1100 return empty, errors.Annotate(err, "retrieving target info") 1101 } 1102 1103 return params.MigrationStatus{ 1104 MigrationId: mig.Id(), 1105 Attempt: mig.Attempt(), 1106 Phase: phase.String(), 1107 SourceAPIAddrs: sourceAddrs, 1108 SourceCACert: sourceCACert, 1109 TargetAPIAddrs: target.Addrs, 1110 TargetCACert: target.CACert, 1111 }, nil 1112 } 1113 1114 func (w *srvMigrationStatusWatcher) getLocalHostPorts() ([]string, error) { 1115 hostports, err := w.ctrlSt.APIHostPortsForClients() 1116 if err != nil { 1117 return nil, errors.Trace(err) 1118 } 1119 var out []string 1120 for _, section := range hostports { 1121 for _, hostport := range section { 1122 out = append(out, hostport.String()) 1123 } 1124 } 1125 return out, nil 1126 } 1127 1128 // This is a shim to avoid the need to use a working State into the 1129 // unit tests. It is tested as part of the client side API tests. 1130 var getControllerCACert = func(st controllerBackend) (string, error) { 1131 cfg, err := st.ControllerConfig() 1132 if err != nil { 1133 return "", errors.Trace(err) 1134 } 1135 1136 cacert, ok := cfg.CACert() 1137 if !ok { 1138 return "", errors.New("missing CA cert for controller model") 1139 } 1140 return cacert, nil 1141 } 1142 1143 // newModelSummaryWatcher exists solely to be registered with regRaw. 1144 // Standard registration doesn't handle watcher types (it checks for 1145 // and empty ID in the context). 1146 func newModelSummaryWatcher(context facade.Context) (facade.Facade, error) { 1147 return NewModelSummaryWatcher(context) 1148 } 1149 1150 // NewModelSummaryWatcher returns a new API server endpoint for interacting with 1151 // a watcher created by the WatchModelSummaries and WatchAllModelSummaries API 1152 // calls. 1153 func NewModelSummaryWatcher(context facade.Context) (*SrvModelSummaryWatcher, error) { 1154 id := context.ID() 1155 auth := context.Auth() 1156 resources := context.Resources() 1157 1158 if !auth.AuthClient() { 1159 // Note that we don't need to check specific permissions 1160 // here, as the AllWatcher can only do anything if the 1161 // watcher resource has already been created, so we can 1162 // rely on the permission check there to ensure that 1163 // this facade can't do anything it shouldn't be allowed 1164 // to. 1165 // 1166 // This is useful because the AllWatcher is reused for 1167 // both the WatchAll (requires model access rights) and 1168 // the WatchAllModels (requring controller superuser 1169 // rights) API calls. 1170 return nil, apiservererrors.ErrPerm 1171 } 1172 watcher, ok := resources.Get(id).(cache.ModelSummaryWatcher) 1173 if !ok { 1174 return nil, errors.Annotatef(apiservererrors.ErrUnknownWatcher, "watcher id: %s", id) 1175 } 1176 return &SrvModelSummaryWatcher{ 1177 watcherCommon: newWatcherCommon(context), 1178 watcher: watcher, 1179 }, nil 1180 } 1181 1182 // SrvModelSummaryWatcher defines the API methods on a ModelSummaryWatcher. 1183 type SrvModelSummaryWatcher struct { 1184 watcherCommon 1185 watcher cache.ModelSummaryWatcher 1186 } 1187 1188 // Next will return the current state of everything on the first call 1189 // and subsequent calls will return just those model summaries that have 1190 // changed. 1191 func (w *SrvModelSummaryWatcher) Next() (params.SummaryWatcherNextResults, error) { 1192 if summaries, ok := <-w.watcher.Changes(); ok { 1193 return params.SummaryWatcherNextResults{ 1194 Models: w.translate(summaries), 1195 }, nil 1196 } 1197 return params.SummaryWatcherNextResults{}, apiservererrors.ErrStoppedWatcher 1198 } 1199 1200 func (w *SrvModelSummaryWatcher) translate(summaries []cache.ModelSummary) []params.ModelAbstract { 1201 response := make([]params.ModelAbstract, 0, len(summaries)) 1202 for _, summary := range summaries { 1203 if summary.Removed { 1204 response = append(response, params.ModelAbstract{ 1205 UUID: summary.UUID, 1206 Removed: true, 1207 }) 1208 continue 1209 } 1210 1211 result := params.ModelAbstract{ 1212 UUID: summary.UUID, 1213 Controller: summary.Controller, 1214 Name: summary.Name, 1215 Admins: summary.Admins, 1216 Cloud: summary.Cloud, 1217 Region: summary.Region, 1218 Credential: summary.Credential, 1219 Size: params.ModelSummarySize{ 1220 Machines: summary.MachineCount, 1221 Containers: summary.ContainerCount, 1222 Applications: summary.ApplicationCount, 1223 Units: summary.UnitCount, 1224 Relations: summary.RelationCount, 1225 }, 1226 Status: summary.Status, 1227 Messages: w.translateMessages(summary.Messages), 1228 Annotations: summary.Annotations, 1229 } 1230 response = append(response, result) 1231 } 1232 return response 1233 } 1234 1235 func (w *SrvModelSummaryWatcher) translateMessages(messages []cache.ModelSummaryMessage) []params.ModelSummaryMessage { 1236 if messages == nil { 1237 return nil 1238 } 1239 result := make([]params.ModelSummaryMessage, len(messages)) 1240 for i, m := range messages { 1241 result[i] = params.ModelSummaryMessage{ 1242 Agent: m.Agent, 1243 Message: m.Message, 1244 } 1245 } 1246 return result 1247 } 1248 1249 // srvSecretTriggerWatcher defines the API wrapping a SecretsTriggerWatcher. 1250 type srvSecretTriggerWatcher struct { 1251 watcherCommon 1252 st *state.State 1253 watcher state.SecretsTriggerWatcher 1254 } 1255 1256 func newSecretsTriggerWatcher(context facade.Context) (facade.Facade, error) { 1257 id := context.ID() 1258 auth := context.Auth() 1259 resources := context.Resources() 1260 1261 st := context.State() 1262 1263 if !isAgent(auth) { 1264 return nil, apiservererrors.ErrPerm 1265 } 1266 watcher, ok := resources.Get(id).(state.SecretsTriggerWatcher) 1267 if !ok { 1268 return nil, apiservererrors.ErrUnknownWatcher 1269 } 1270 return &srvSecretTriggerWatcher{ 1271 watcherCommon: newWatcherCommon(context), 1272 st: st, 1273 watcher: watcher, 1274 }, nil 1275 } 1276 1277 // Next returns when a change has occurred to an entity of the 1278 // collection being watched since the most recent call to Next 1279 // or the Watch call that created the srvSecretRotationWatcher. 1280 func (w *srvSecretTriggerWatcher) Next() (params.SecretTriggerWatchResult, error) { 1281 if changes, ok := <-w.watcher.Changes(); ok { 1282 return params.SecretTriggerWatchResult{ 1283 Changes: w.translateChanges(changes), 1284 }, nil 1285 } 1286 err := w.watcher.Err() 1287 if err == nil { 1288 err = apiservererrors.ErrStoppedWatcher 1289 } 1290 return params.SecretTriggerWatchResult{}, err 1291 } 1292 1293 func (w *srvSecretTriggerWatcher) translateChanges(changes []corewatcher.SecretTriggerChange) []params.SecretTriggerChange { 1294 if changes == nil { 1295 return nil 1296 } 1297 result := make([]params.SecretTriggerChange, len(changes)) 1298 for i, c := range changes { 1299 result[i] = params.SecretTriggerChange{ 1300 URI: c.URI.String(), 1301 Revision: c.Revision, 1302 NextTriggerTime: c.NextTriggerTime, 1303 } 1304 } 1305 return result 1306 } 1307 1308 // srvSecretBackendsRotateWatcher defines the API wrapping a SecretBackendsRotateWatcher. 1309 type srvSecretBackendsRotateWatcher struct { 1310 watcherCommon 1311 st *state.State 1312 watcher state.SecretBackendRotateWatcher 1313 } 1314 1315 func newSecretBackendsRotateWatcher(context facade.Context) (facade.Facade, error) { 1316 id := context.ID() 1317 auth := context.Auth() 1318 resources := context.Resources() 1319 1320 st := context.State() 1321 1322 if !isAgent(auth) { 1323 return nil, apiservererrors.ErrPerm 1324 } 1325 watcher, ok := resources.Get(id).(state.SecretBackendRotateWatcher) 1326 if !ok { 1327 return nil, apiservererrors.ErrUnknownWatcher 1328 } 1329 return &srvSecretBackendsRotateWatcher{ 1330 watcherCommon: newWatcherCommon(context), 1331 st: st, 1332 watcher: watcher, 1333 }, nil 1334 } 1335 1336 // Next returns when a change has occurred to an entity of the 1337 // collection being watched since the most recent call to Next 1338 // or the Watch call that created the srvSecretRotationWatcher. 1339 func (w *srvSecretBackendsRotateWatcher) Next() (params.SecretBackendRotateWatchResult, error) { 1340 if changes, ok := <-w.watcher.Changes(); ok { 1341 return params.SecretBackendRotateWatchResult{ 1342 Changes: w.translateChanges(changes), 1343 }, nil 1344 } 1345 err := w.watcher.Err() 1346 if err == nil { 1347 err = apiservererrors.ErrStoppedWatcher 1348 } 1349 return params.SecretBackendRotateWatchResult{}, err 1350 } 1351 1352 func (w *srvSecretBackendsRotateWatcher) translateChanges(changes []corewatcher.SecretBackendRotateChange) []params.SecretBackendRotateChange { 1353 if changes == nil { 1354 return nil 1355 } 1356 result := make([]params.SecretBackendRotateChange, len(changes)) 1357 for i, c := range changes { 1358 result[i] = params.SecretBackendRotateChange{ 1359 ID: c.ID, 1360 Name: c.Name, 1361 NextTriggerTime: c.NextTriggerTime, 1362 } 1363 } 1364 return result 1365 } 1366 1367 // srvSecretsRevisionWatcher defines the API wrapping a SecretsRevisionWatcher. 1368 type srvSecretsRevisionWatcher struct { 1369 watcherCommon 1370 st *state.State 1371 watcher state.StringsWatcher 1372 } 1373 1374 func newSecretsRevisionWatcher(context facade.Context) (facade.Facade, error) { 1375 id := context.ID() 1376 auth := context.Auth() 1377 resources := context.Resources() 1378 1379 st := context.State() 1380 1381 // TODO(wallyworld) - enhance this watcher to support 1382 // anonymous api calls with macaroons. 1383 if auth.GetAuthTag() != nil && !isAgent(auth) { 1384 return nil, apiservererrors.ErrPerm 1385 } 1386 watcher, ok := resources.Get(id).(state.StringsWatcher) 1387 if !ok { 1388 return nil, apiservererrors.ErrUnknownWatcher 1389 } 1390 return &srvSecretsRevisionWatcher{ 1391 watcherCommon: newWatcherCommon(context), 1392 st: st, 1393 watcher: watcher, 1394 }, nil 1395 } 1396 1397 // Next returns when a change has occurred to an entity of the 1398 // collection being watched since the most recent call to Next 1399 // or the Watch call that created the srvSecretRotationWatcher. 1400 func (w *srvSecretsRevisionWatcher) Next() (params.SecretRevisionWatchResult, error) { 1401 if changes, ok := <-w.watcher.Changes(); ok { 1402 ch, err := w.translateChanges(changes) 1403 if err != nil { 1404 return params.SecretRevisionWatchResult{}, errors.Trace(err) 1405 } 1406 return params.SecretRevisionWatchResult{ 1407 Changes: ch, 1408 }, nil 1409 } 1410 err := w.watcher.Err() 1411 if err == nil { 1412 err = apiservererrors.ErrStoppedWatcher 1413 } 1414 return params.SecretRevisionWatchResult{}, err 1415 } 1416 1417 func (w *srvSecretsRevisionWatcher) translateChanges(changes []string) ([]params.SecretRevisionChange, error) { 1418 if changes == nil { 1419 return nil, nil 1420 } 1421 secrets := state.NewSecrets(w.st) 1422 result := make([]params.SecretRevisionChange, len(changes)) 1423 for i, uriStr := range changes { 1424 uri, err := coresecrets.ParseURI(uriStr) 1425 if err != nil { 1426 return nil, errors.Trace(err) 1427 } 1428 md, err := secrets.GetSecret(uri) 1429 if err != nil { 1430 return nil, errors.Trace(err) 1431 } 1432 result[i] = params.SecretRevisionChange{ 1433 URI: uri.String(), 1434 Revision: md.LatestRevision, 1435 } 1436 } 1437 return result, nil 1438 }