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 := &params.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 &params.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  }