github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/remoterelations/remoterelations.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package remoterelations 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 commoncrossmodel "github.com/juju/juju/apiserver/common/crossmodel" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/core/status" 15 "github.com/juju/juju/state/watcher" 16 ) 17 18 // RemoteRelationsAPI provides access to the RemoteRelations API facade. 19 type RemoteRelationsAPI struct { 20 *common.ControllerConfigAPI 21 st RemoteRelationsState 22 resources facade.Resources 23 authorizer facade.Authorizer 24 } 25 26 // NewStateRemoteRelationsAPI creates a new server-side RemoteRelationsAPI facade 27 // backed by global state. 28 func NewStateRemoteRelationsAPI(ctx facade.Context) (*RemoteRelationsAPI, error) { 29 return NewRemoteRelationsAPI( 30 stateShim{st: ctx.State(), Backend: commoncrossmodel.GetBackend(ctx.State())}, 31 common.NewStateControllerConfig(ctx.State()), 32 ctx.Resources(), ctx.Auth(), 33 ) 34 35 } 36 37 // NewRemoteRelationsAPI returns a new server-side RemoteRelationsAPI facade. 38 func NewRemoteRelationsAPI( 39 st RemoteRelationsState, 40 controllerCfgAPI *common.ControllerConfigAPI, 41 resources facade.Resources, 42 authorizer facade.Authorizer, 43 ) (*RemoteRelationsAPI, error) { 44 if !authorizer.AuthController() { 45 return nil, common.ErrPerm 46 } 47 return &RemoteRelationsAPI{ 48 st: st, 49 ControllerConfigAPI: controllerCfgAPI, 50 resources: resources, 51 authorizer: authorizer, 52 }, nil 53 } 54 55 // ImportRemoteEntities adds entities to the remote entities collection with the specified opaque tokens. 56 func (api *RemoteRelationsAPI) ImportRemoteEntities(args params.RemoteEntityTokenArgs) (params.ErrorResults, error) { 57 results := params.ErrorResults{ 58 Results: make([]params.ErrorResult, len(args.Args)), 59 } 60 for i, arg := range args.Args { 61 err := api.importRemoteEntity(arg) 62 results.Results[i].Error = common.ServerError(err) 63 } 64 return results, nil 65 } 66 67 func (api *RemoteRelationsAPI) importRemoteEntity(arg params.RemoteEntityTokenArg) error { 68 entityTag, err := names.ParseTag(arg.Tag) 69 if err != nil { 70 return errors.Trace(err) 71 } 72 return api.st.ImportRemoteEntity(entityTag, arg.Token) 73 } 74 75 // ExportEntities allocates unique, remote entity IDs for the given entities in the local model. 76 func (api *RemoteRelationsAPI) ExportEntities(entities params.Entities) (params.TokenResults, error) { 77 results := params.TokenResults{ 78 Results: make([]params.TokenResult, len(entities.Entities)), 79 } 80 for i, entity := range entities.Entities { 81 tag, err := names.ParseTag(entity.Tag) 82 if err != nil { 83 results.Results[i].Error = common.ServerError(err) 84 continue 85 } 86 token, err := api.st.ExportLocalEntity(tag) 87 if err != nil { 88 results.Results[i].Error = common.ServerError(err) 89 if !errors.IsAlreadyExists(err) { 90 continue 91 } 92 } 93 results.Results[i].Token = token 94 } 95 return results, nil 96 } 97 98 // GetTokens returns the token associated with the entities with the given tags for the given models. 99 func (api *RemoteRelationsAPI) GetTokens(args params.GetTokenArgs) (params.StringResults, error) { 100 results := params.StringResults{ 101 Results: make([]params.StringResult, len(args.Args)), 102 } 103 for i, arg := range args.Args { 104 entityTag, err := names.ParseTag(arg.Tag) 105 if err != nil { 106 results.Results[i].Error = common.ServerError(err) 107 continue 108 } 109 token, err := api.st.GetToken(entityTag) 110 if err != nil { 111 results.Results[i].Error = common.ServerError(err) 112 } 113 results.Results[i].Result = token 114 } 115 return results, nil 116 } 117 118 // SaveMacaroons saves the macaroons for the given entities. 119 func (api *RemoteRelationsAPI) SaveMacaroons(args params.EntityMacaroonArgs) (params.ErrorResults, error) { 120 results := params.ErrorResults{ 121 Results: make([]params.ErrorResult, len(args.Args)), 122 } 123 for i, arg := range args.Args { 124 entityTag, err := names.ParseTag(arg.Tag) 125 if err != nil { 126 results.Results[i].Error = common.ServerError(err) 127 continue 128 } 129 err = api.st.SaveMacaroon(entityTag, arg.Macaroon) 130 results.Results[i].Error = common.ServerError(err) 131 } 132 return results, nil 133 } 134 135 // RelationUnitSettings returns the relation unit settings for the given relation units in the local model. 136 func (api *RemoteRelationsAPI) RelationUnitSettings(relationUnits params.RelationUnits) (params.SettingsResults, error) { 137 results := params.SettingsResults{ 138 Results: make([]params.SettingsResult, len(relationUnits.RelationUnits)), 139 } 140 one := func(ru params.RelationUnit) (params.Settings, error) { 141 relationTag, err := names.ParseRelationTag(ru.Relation) 142 if err != nil { 143 return nil, errors.Trace(err) 144 } 145 rel, err := api.st.KeyRelation(relationTag.Id()) 146 if err != nil { 147 return nil, errors.Trace(err) 148 } 149 unitTag, err := names.ParseUnitTag(ru.Unit) 150 if err != nil { 151 return nil, errors.Trace(err) 152 } 153 unit, err := rel.Unit(unitTag.Id()) 154 if err != nil { 155 return nil, errors.Trace(err) 156 } 157 settings, err := unit.Settings() 158 if err != nil { 159 return nil, errors.Trace(err) 160 } 161 paramsSettings := make(params.Settings) 162 for k, v := range settings { 163 vString, ok := v.(string) 164 if !ok { 165 return nil, errors.Errorf( 166 "invalid relation setting %q: expected string, got %T", k, v, 167 ) 168 } 169 paramsSettings[k] = vString 170 } 171 return paramsSettings, nil 172 } 173 for i, ru := range relationUnits.RelationUnits { 174 settings, err := one(ru) 175 if err != nil { 176 results.Results[i].Error = common.ServerError(err) 177 continue 178 } 179 results.Results[i].Settings = settings 180 } 181 return results, nil 182 } 183 184 func (api *RemoteRelationsAPI) remoteRelation(entity params.Entity) (*params.RemoteRelation, error) { 185 tag, err := names.ParseRelationTag(entity.Tag) 186 if err != nil { 187 return nil, errors.Trace(err) 188 } 189 rel, err := api.st.KeyRelation(tag.Id()) 190 if err != nil { 191 return nil, errors.Trace(err) 192 } 193 result := ¶ms.RemoteRelation{ 194 Id: rel.Id(), 195 Life: params.Life(rel.Life().String()), 196 Suspended: rel.Suspended(), 197 Key: tag.Id(), 198 } 199 for _, ep := range rel.Endpoints() { 200 // Try looking up the info for the remote application. 201 remoteApp, err := api.st.RemoteApplication(ep.ApplicationName) 202 if err != nil && !errors.IsNotFound(err) { 203 return nil, errors.Trace(err) 204 } else if err == nil { 205 result.RemoteApplicationName = remoteApp.Name() 206 result.RemoteEndpointName = ep.Name 207 result.SourceModelUUID = remoteApp.SourceModel().Id() 208 continue 209 } 210 // Try looking up the info for the local application. 211 _, err = api.st.Application(ep.ApplicationName) 212 if err != nil && !errors.IsNotFound(err) { 213 return nil, errors.Trace(err) 214 } else if err == nil { 215 result.ApplicationName = ep.ApplicationName 216 result.Endpoint = params.RemoteEndpoint{ 217 Name: ep.Name, 218 Interface: ep.Interface, 219 Role: ep.Role, 220 } 221 continue 222 } 223 } 224 return result, nil 225 } 226 227 // Relations returns information about the cross-model relations with the specified keys 228 // in the local model. 229 func (api *RemoteRelationsAPI) Relations(entities params.Entities) (params.RemoteRelationResults, error) { 230 results := params.RemoteRelationResults{ 231 Results: make([]params.RemoteRelationResult, len(entities.Entities)), 232 } 233 for i, entity := range entities.Entities { 234 remoteRelation, err := api.remoteRelation(entity) 235 if err != nil { 236 results.Results[i].Error = common.ServerError(err) 237 continue 238 } 239 results.Results[i].Result = remoteRelation 240 } 241 return results, nil 242 } 243 244 // RemoteApplications returns the current state of the remote applications with 245 // the specified names in the local model. 246 func (api *RemoteRelationsAPI) RemoteApplications(entities params.Entities) (params.RemoteApplicationResults, error) { 247 results := params.RemoteApplicationResults{ 248 Results: make([]params.RemoteApplicationResult, len(entities.Entities)), 249 } 250 one := func(entity params.Entity) (*params.RemoteApplication, error) { 251 tag, err := names.ParseApplicationTag(entity.Tag) 252 if err != nil { 253 return nil, errors.Trace(err) 254 } 255 remoteApp, err := api.st.RemoteApplication(tag.Id()) 256 if err != nil { 257 return nil, errors.Trace(err) 258 } 259 mac, err := remoteApp.Macaroon() 260 if err != nil { 261 return nil, errors.Trace(err) 262 } 263 return ¶ms.RemoteApplication{ 264 Name: remoteApp.Name(), 265 OfferUUID: remoteApp.OfferUUID(), 266 Life: params.Life(remoteApp.Life().String()), 267 ModelUUID: remoteApp.SourceModel().Id(), 268 IsConsumerProxy: remoteApp.IsConsumerProxy(), 269 Macaroon: mac, 270 }, nil 271 } 272 for i, entity := range entities.Entities { 273 remoteApplication, err := one(entity) 274 if err != nil { 275 results.Results[i].Error = common.ServerError(err) 276 continue 277 } 278 results.Results[i].Result = remoteApplication 279 } 280 return results, nil 281 } 282 283 // WatchRemoteApplications starts a strings watcher that notifies of the addition, 284 // removal, and lifecycle changes of remote applications in the model; and 285 // returns the watcher ID and initial IDs of remote applications, or an error if 286 // watching failed. 287 func (api *RemoteRelationsAPI) WatchRemoteApplications() (params.StringsWatchResult, error) { 288 w := api.st.WatchRemoteApplications() 289 if changes, ok := <-w.Changes(); ok { 290 return params.StringsWatchResult{ 291 StringsWatcherId: api.resources.Register(w), 292 Changes: changes, 293 }, nil 294 } 295 return params.StringsWatchResult{}, watcher.EnsureErr(w) 296 } 297 298 // WatchLocalRelationUnits starts a RelationUnitsWatcher for watching the local 299 // relation units involved in each specified relation in the local model, 300 // and returns the watcher IDs and initial values, or an error if the relation 301 // units could not be watched. 302 func (api *RemoteRelationsAPI) WatchLocalRelationUnits(args params.Entities) (params.RelationUnitsWatchResults, error) { 303 results := params.RelationUnitsWatchResults{ 304 make([]params.RelationUnitsWatchResult, len(args.Entities)), 305 } 306 for i, arg := range args.Entities { 307 relationTag, err := names.ParseRelationTag(arg.Tag) 308 if err != nil { 309 results.Results[i].Error = common.ServerError(err) 310 continue 311 } 312 w, err := commoncrossmodel.WatchRelationUnits(api.st, relationTag) 313 if err != nil { 314 results.Results[i].Error = common.ServerError(err) 315 continue 316 } 317 changes, ok := <-w.Changes() 318 if !ok { 319 results.Results[i].Error = common.ServerError(watcher.EnsureErr(w)) 320 continue 321 } 322 results.Results[i].RelationUnitsWatcherId = api.resources.Register(w) 323 results.Results[i].Changes = changes 324 } 325 return results, nil 326 } 327 328 // WatchRemoteApplicationRelations starts a StringsWatcher for watching the relations of 329 // each specified application in the local model, and returns the watcher IDs 330 // and initial values, or an error if the services' relations could not be 331 // watched. 332 func (api *RemoteRelationsAPI) WatchRemoteApplicationRelations(args params.Entities) (params.StringsWatchResults, error) { 333 results := params.StringsWatchResults{ 334 make([]params.StringsWatchResult, len(args.Entities)), 335 } 336 for i, arg := range args.Entities { 337 applicationTag, err := names.ParseApplicationTag(arg.Tag) 338 if err != nil { 339 results.Results[i].Error = common.ServerError(err) 340 continue 341 } 342 appName := applicationTag.Id() 343 w, err := api.st.WatchRemoteApplicationRelations(appName) 344 if err != nil { 345 results.Results[i].Error = common.ServerError(err) 346 continue 347 } 348 changes, ok := <-w.Changes() 349 if !ok { 350 results.Results[i].Error = common.ServerError(watcher.EnsureErr(w)) 351 continue 352 } 353 results.Results[i].StringsWatcherId = api.resources.Register(w) 354 results.Results[i].Changes = changes 355 } 356 return results, nil 357 } 358 359 // WatchRemoteRelations starts a strings watcher that notifies of the addition, 360 // removal, and lifecycle changes of remote relations in the model; and 361 // returns the watcher ID and initial IDs of remote relations, or an error if 362 // watching failed. 363 func (api *RemoteRelationsAPI) WatchRemoteRelations() (params.StringsWatchResult, error) { 364 w := api.st.WatchRemoteRelations() 365 if changes, ok := <-w.Changes(); ok { 366 return params.StringsWatchResult{ 367 StringsWatcherId: api.resources.Register(w), 368 Changes: changes, 369 }, nil 370 } 371 return params.StringsWatchResult{}, watcher.EnsureErr(w) 372 } 373 374 // ConsumeRemoteRelationChanges consumes changes to settings originating 375 // from the remote/offering side of relations. 376 func (api *RemoteRelationsAPI) ConsumeRemoteRelationChanges( 377 changes params.RemoteRelationsChanges, 378 ) (params.ErrorResults, error) { 379 results := params.ErrorResults{ 380 Results: make([]params.ErrorResult, len(changes.Changes)), 381 } 382 for i, change := range changes.Changes { 383 relationTag, err := api.st.GetRemoteEntity(change.RelationToken) 384 if err != nil { 385 if errors.IsNotFound(err) { 386 continue 387 } 388 results.Results[i].Error = common.ServerError(err) 389 continue 390 } 391 if err := commoncrossmodel.PublishRelationChange(api.st, relationTag, change); err != nil { 392 results.Results[i].Error = common.ServerError(err) 393 continue 394 } 395 } 396 return results, nil 397 } 398 399 // SetRemoteApplicationsStatus sets the status for the specified remote applications. 400 func (f *RemoteRelationsAPI) SetRemoteApplicationsStatus(args params.SetStatus) (params.ErrorResults, error) { 401 var result params.ErrorResults 402 result.Results = make([]params.ErrorResult, len(args.Entities)) 403 for i, entity := range args.Entities { 404 remoteAppTag, err := names.ParseApplicationTag(entity.Tag) 405 if err != nil { 406 result.Results[i].Error = common.ServerError(err) 407 continue 408 } 409 app, err := f.st.RemoteApplication(remoteAppTag.Id()) 410 if err != nil { 411 result.Results[i].Error = common.ServerError(err) 412 continue 413 } 414 err = app.SetStatus(status.StatusInfo{ 415 Status: status.Status(entity.Status), 416 Message: entity.Info, 417 }) 418 result.Results[i].Error = common.ServerError(err) 419 } 420 return result, nil 421 }