github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/remoterelations/remoteapplicationworker.go (about)

     1  // Copyright 2017 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  	"gopkg.in/juju/worker.v1"
    10  	"gopkg.in/juju/worker.v1/catacomb"
    11  	"gopkg.in/macaroon.v2-unstable"
    12  
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/core/status"
    15  	"github.com/juju/juju/core/watcher"
    16  )
    17  
    18  // remoteApplicationWorker listens for localChanges to relations
    19  // involving a remote application, and publishes change to
    20  // local relation units to the remote model. It also watches for
    21  // changes originating from the offering model and consumes those
    22  // in the local model.
    23  type remoteApplicationWorker struct {
    24  	catacomb catacomb.Catacomb
    25  
    26  	// These attribute are relevant to dealing with a specific
    27  	// remote application proxy.
    28  	offerUUID             string
    29  	applicationName       string // name of the remote application proxy in the local model
    30  	localModelUUID        string // uuid of the model hosting the local application
    31  	remoteModelUUID       string // uuid of the model hosting the remote offer
    32  	isConsumerProxy       bool
    33  	localRelationChanges  chan params.RemoteRelationChangeEvent
    34  	remoteRelationChanges chan params.RemoteRelationChangeEvent
    35  
    36  	// offerMacaroon is used to confirm that permission has been granted to consume
    37  	// the remote application to which this worker pertains.
    38  	offerMacaroon *macaroon.Macaroon
    39  
    40  	// localModelFacade interacts with the local (consuming) model.
    41  	localModelFacade RemoteRelationsFacade
    42  	// remoteModelFacade interacts with the remote (offering) model.
    43  	remoteModelFacade RemoteModelRelationsFacadeCloser
    44  
    45  	newRemoteModelRelationsFacadeFunc newRemoteRelationsFacadeFunc
    46  }
    47  
    48  // relation holds attributes relevant to a particular
    49  // relation between a local app and a remote offer.
    50  type relation struct {
    51  	relationId int
    52  	suspended  bool
    53  	localRuw   *relationUnitsWorker
    54  	remoteRuw  *relationUnitsWorker
    55  	remoteRrw  *remoteRelationsWorker
    56  
    57  	applicationToken   string // token for app in local model
    58  	relationToken      string // token for relation in local model
    59  	localEndpoint      params.RemoteEndpoint
    60  	remoteEndpointName string
    61  	macaroon           *macaroon.Macaroon
    62  }
    63  
    64  // Kill is defined on worker.Worker
    65  func (w *remoteApplicationWorker) Kill() {
    66  	w.catacomb.Kill(nil)
    67  }
    68  
    69  // Wait is defined on worker.Worker
    70  func (w *remoteApplicationWorker) Wait() error {
    71  	err := w.catacomb.Wait()
    72  	if err != nil {
    73  		logger.Errorf("error in remote application worker for %v: %v", w.applicationName, err)
    74  	}
    75  	return err
    76  }
    77  
    78  func (w *remoteApplicationWorker) checkOfferPermissionDenied(err error, appToken, relationToken string) {
    79  	// If consume permission has been revoked for the offer, set the
    80  	// status of the local remote application entity.
    81  	if params.ErrCode(err) == params.CodeDischargeRequired {
    82  		if err := w.localModelFacade.SetRemoteApplicationStatus(w.applicationName, status.Error, err.Error()); err != nil {
    83  			logger.Errorf(
    84  				"updating remote application %v status from remote model %v: %v",
    85  				w.applicationName, w.remoteModelUUID, err)
    86  		}
    87  		logger.Debugf("discharge required error: app token: %v rel token: %v", appToken, relationToken)
    88  		// If we know a specific relation, update that too.
    89  		if relationToken != "" {
    90  			suspended := true
    91  			event := params.RemoteRelationChangeEvent{
    92  				RelationToken:    relationToken,
    93  				ApplicationToken: appToken,
    94  				Suspended:        &suspended,
    95  				SuspendedReason:  "offer permission revoked",
    96  			}
    97  			if err := w.localModelFacade.ConsumeRemoteRelationChange(event); err != nil {
    98  				logger.Errorf("updating relation status: %v", err)
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func (w *remoteApplicationWorker) remoteOfferRemoved() error {
   105  	logger.Debugf("remote offer for %s has been removed", w.applicationName)
   106  	if err := w.localModelFacade.SetRemoteApplicationStatus(w.applicationName, status.Terminated, "offer has been removed"); err != nil {
   107  		return errors.Annotatef(err, "updating remote application %v status from remote model %v", w.applicationName, w.remoteModelUUID)
   108  	}
   109  	return nil
   110  }
   111  
   112  func (w *remoteApplicationWorker) loop() (err error) {
   113  	// Watch for changes to any remote relations to this application.
   114  	relationsWatcher, err := w.localModelFacade.WatchRemoteApplicationRelations(w.applicationName)
   115  	if errors.IsNotFound(err) {
   116  		return nil
   117  	} else if err != nil {
   118  		return errors.Annotatef(err, "watching relations for remote application %q", w.applicationName)
   119  	}
   120  	if err := w.catacomb.Add(relationsWatcher); err != nil {
   121  		return errors.Trace(err)
   122  	}
   123  
   124  	// On the consuming side, watch for status changes to the offer.
   125  	var offerStatusChanges watcher.OfferStatusChannel
   126  	if !w.isConsumerProxy {
   127  		// Get the connection info for the remote controller.
   128  		apiInfo, err := w.localModelFacade.ControllerAPIInfoForModel(w.remoteModelUUID)
   129  		if err != nil {
   130  			return errors.Trace(err)
   131  		}
   132  		logger.Debugf("remote controller api addresses: %v", apiInfo.Addrs)
   133  
   134  		w.remoteModelFacade, err = w.newRemoteModelRelationsFacadeFunc(apiInfo)
   135  		if err != nil {
   136  			return errors.Annotate(err, "opening facade to remote model")
   137  		}
   138  
   139  		defer func() {
   140  			w.remoteModelFacade.Close()
   141  		}()
   142  
   143  		arg := params.OfferArg{
   144  			OfferUUID: w.offerUUID,
   145  		}
   146  		if w.offerMacaroon != nil {
   147  			arg.Macaroons = macaroon.Slice{w.offerMacaroon}
   148  		}
   149  
   150  		offerStatusWatcher, err := w.remoteModelFacade.WatchOfferStatus(arg)
   151  		if err != nil {
   152  			w.checkOfferPermissionDenied(err, "", "")
   153  			if params.IsCodeNotFound(err) {
   154  				return w.remoteOfferRemoved()
   155  			}
   156  			return errors.Annotate(err, "watching status for offer")
   157  		}
   158  		if err := w.catacomb.Add(offerStatusWatcher); err != nil {
   159  			return errors.Trace(err)
   160  		}
   161  		offerStatusChanges = offerStatusWatcher.Changes()
   162  	}
   163  
   164  	relations := make(map[string]*relation)
   165  	for {
   166  		select {
   167  		case <-w.catacomb.Dying():
   168  			return w.catacomb.ErrDying()
   169  		case change, ok := <-relationsWatcher.Changes():
   170  			logger.Debugf("relations changed: %#v, %v", change, ok)
   171  			if !ok {
   172  				// We are dying.
   173  				return w.catacomb.ErrDying()
   174  			}
   175  			results, err := w.localModelFacade.Relations(change)
   176  			if err != nil {
   177  				return errors.Annotate(err, "querying relations")
   178  			}
   179  			for i, result := range results {
   180  				key := change[i]
   181  				if err := w.relationChanged(key, result, relations); err != nil {
   182  					if params.IsCodeNotFound(err) {
   183  						return w.remoteOfferRemoved()
   184  					}
   185  					return errors.Annotatef(err, "handling change for relation %q", key)
   186  				}
   187  			}
   188  		case change := <-w.localRelationChanges:
   189  			logger.Debugf("local relation units changed -> publishing: %#v", change)
   190  			if err := w.remoteModelFacade.PublishRelationChange(change); err != nil {
   191  				w.checkOfferPermissionDenied(err, change.ApplicationToken, change.RelationToken)
   192  				if params.IsCodeNotFound(err) || params.IsCodeCannotEnterScope(err) {
   193  					return w.remoteOfferRemoved()
   194  				}
   195  				return errors.Annotatef(err, "publishing relation change %+v to remote model %v", change, w.remoteModelUUID)
   196  			}
   197  		case change := <-w.remoteRelationChanges:
   198  			logger.Debugf("remote relation units changed -> consuming: %#v", change)
   199  			if err := w.localModelFacade.ConsumeRemoteRelationChange(change); err != nil {
   200  				return errors.Annotatef(err, "consuming relation change %+v from remote model %v", change, w.remoteModelUUID)
   201  			}
   202  		case changes := <-offerStatusChanges:
   203  			logger.Debugf("offer status changed: %#v", changes)
   204  			for _, change := range changes {
   205  				if err := w.localModelFacade.SetRemoteApplicationStatus(w.applicationName, change.Status.Status, change.Status.Message); err != nil {
   206  					return errors.Annotatef(err, "updating remote application %v status from remote model %v", w.applicationName, w.remoteModelUUID)
   207  				}
   208  			}
   209  		}
   210  	}
   211  }
   212  
   213  func (w *remoteApplicationWorker) processRelationDying(key string, r *relation, forceCleanup bool) error {
   214  	logger.Debugf("relation %v dying (%v)", key, forceCleanup)
   215  	// On the consuming side, inform the remote side the relation is dying
   216  	// (but only if we are killing the relation due to it dying, not because
   217  	// it is suspended).
   218  	if !w.isConsumerProxy {
   219  		change := params.RemoteRelationChangeEvent{
   220  			RelationToken:    r.relationToken,
   221  			Life:             params.Dying,
   222  			ApplicationToken: r.applicationToken,
   223  			Macaroons:        macaroon.Slice{r.macaroon},
   224  		}
   225  		// forceCleanup will be true if the worker has restarted and because the relation had
   226  		// already been removed, we won't get any more unit departed events.
   227  		if forceCleanup {
   228  			change.ForceCleanup = &forceCleanup
   229  		}
   230  		if err := w.remoteModelFacade.PublishRelationChange(change); err != nil {
   231  			w.checkOfferPermissionDenied(err, r.applicationToken, r.relationToken)
   232  			if params.IsCodeNotFound(err) {
   233  				logger.Debugf("relation %v dying but offer already removed", key)
   234  				return nil
   235  			}
   236  			return errors.Annotatef(err, "publishing relation dying %+v to remote model %v", change, w.remoteModelUUID)
   237  		}
   238  	}
   239  	return nil
   240  }
   241  
   242  func (w *remoteApplicationWorker) processRelationSuspended(key string, relations map[string]*relation) error {
   243  	logger.Debugf("relation %v suspended", key)
   244  	relation, ok := relations[key]
   245  	if !ok {
   246  		return nil
   247  	}
   248  
   249  	// For suspended relations on the consuming side
   250  	// we want to keep the remote lifecycle watcher
   251  	// so we know when the relation is resumed.
   252  	if w.isConsumerProxy {
   253  		if err := worker.Stop(relation.remoteRrw); err != nil {
   254  			logger.Warningf("stopping remote relations worker for %v: %v", key, err)
   255  		}
   256  		relation.remoteRuw = nil
   257  		delete(relations, key)
   258  	}
   259  
   260  	if relation.localRuw != nil {
   261  		if err := worker.Stop(relation.localRuw); err != nil {
   262  			logger.Warningf("stopping local relation unit worker for %v: %v", key, err)
   263  		}
   264  		relation.localRuw = nil
   265  	}
   266  	return nil
   267  }
   268  
   269  func (w *remoteApplicationWorker) processRelationRemoved(key string, relations map[string]*relation) error {
   270  	logger.Debugf("relation %v removed", key)
   271  	relation, ok := relations[key]
   272  	if !ok {
   273  		return nil
   274  	}
   275  
   276  	if err := worker.Stop(relation.remoteRrw); err != nil {
   277  		logger.Warningf("stopping remote relations worker for %v: %v", key, err)
   278  	}
   279  	relation.remoteRuw = nil
   280  	delete(relations, key)
   281  
   282  	// For the unit watchers, check to see if these are nil before stopping.
   283  	// They will be nil if the relation was suspended and then we kill it for real.
   284  	if relation.localRuw != nil {
   285  		if err := worker.Stop(relation.localRuw); err != nil {
   286  			logger.Warningf("stopping local relation unit worker for %v: %v", key, err)
   287  		}
   288  		relation.localRuw = nil
   289  	}
   290  
   291  	logger.Debugf("remote relation %v removed from remote model", key)
   292  	return nil
   293  }
   294  
   295  func (w *remoteApplicationWorker) relationChanged(
   296  	key string, result params.RemoteRelationResult, relations map[string]*relation,
   297  ) error {
   298  	logger.Debugf("relation %q changed: %+v", key, result)
   299  	if result.Error != nil {
   300  		if params.IsCodeNotFound(result.Error) {
   301  			return w.processRelationRemoved(key, relations)
   302  		}
   303  		return result.Error
   304  	}
   305  	remoteRelation := result.Result
   306  
   307  	// If we have previously started the watcher and the
   308  	// relation is now suspended, stop the watcher.
   309  	if r := relations[key]; r != nil {
   310  		wasSuspended := r.suspended
   311  		r.suspended = remoteRelation.Suspended
   312  		relations[key] = r
   313  		if remoteRelation.Suspended {
   314  			return w.processRelationSuspended(key, relations)
   315  		}
   316  		if !wasSuspended && remoteRelation.Life == params.Alive {
   317  			// Nothing to do, we have previously started the watcher.
   318  			return nil
   319  		}
   320  	}
   321  
   322  	if w.isConsumerProxy {
   323  		// Nothing else to do on the offering side.
   324  		return nil
   325  	}
   326  	return w.processConsumingRelation(key, relations, remoteRelation)
   327  }
   328  
   329  // startUnitsWorkers starts 2 workers to watch for unit settings or departed changes;
   330  // one worker is for the local model, the other for the remote model.
   331  func (w *remoteApplicationWorker) startUnitsWorkers(
   332  	relationTag names.RelationTag,
   333  	applicationToken, relationToken, remoteAppToken string,
   334  	applicationName string,
   335  	mac *macaroon.Macaroon,
   336  ) (*relationUnitsWorker, *relationUnitsWorker, error) {
   337  	// Start a watcher to track changes to the units in the relation in the local model.
   338  	localRelationUnitsWatcher, err := w.localModelFacade.WatchLocalRelationUnits(relationTag.Id())
   339  	if err != nil {
   340  		return nil, nil, errors.Annotatef(err, "watching local side of relation %v", relationTag.Id())
   341  	}
   342  
   343  	// localUnitSettingsFunc converts relations units watcher results from the local model
   344  	// into settings params using an api call to the local model.
   345  	localUnitSettingsFunc := func(changedUnitNames []string) ([]params.SettingsResult, error) {
   346  		relationUnits := make([]params.RelationUnit, len(changedUnitNames))
   347  		for i, changedName := range changedUnitNames {
   348  			relationUnits[i] = params.RelationUnit{
   349  				Relation: relationTag.String(),
   350  				Unit:     names.NewUnitTag(changedName).String(),
   351  			}
   352  		}
   353  		return w.localModelFacade.RelationUnitSettings(relationUnits)
   354  	}
   355  	localUnitsWorker, err := newRelationUnitsWorker(
   356  		relationTag,
   357  		applicationToken,
   358  		mac,
   359  		relationToken,
   360  		localRelationUnitsWatcher,
   361  		w.localRelationChanges,
   362  		localUnitSettingsFunc,
   363  	)
   364  	if err != nil {
   365  		return nil, nil, errors.Trace(err)
   366  	}
   367  	if err := w.catacomb.Add(localUnitsWorker); err != nil {
   368  		return nil, nil, errors.Trace(err)
   369  	}
   370  
   371  	// Start a watcher to track changes to the units in the relation in the remote model.
   372  	remoteRelationUnitsWatcher, err := w.remoteModelFacade.WatchRelationUnits(params.RemoteEntityArg{
   373  		Token:     relationToken,
   374  		Macaroons: macaroon.Slice{mac},
   375  	})
   376  	if err != nil {
   377  		w.checkOfferPermissionDenied(err, remoteAppToken, relationToken)
   378  		return nil, nil, errors.Annotatef(
   379  			err, "watching remote side of application %v and relation %v",
   380  			applicationName, relationTag.Id())
   381  	}
   382  
   383  	// remoteUnitSettingsFunc converts relations units watcher results from the remote model
   384  	// into settings params using an api call to the remote model.
   385  	remoteUnitSettingsFunc := func(changedUnitNames []string) ([]params.SettingsResult, error) {
   386  		relationUnits := make([]params.RemoteRelationUnit, len(changedUnitNames))
   387  		for i, changedName := range changedUnitNames {
   388  			relationUnits[i] = params.RemoteRelationUnit{
   389  				RelationToken: relationToken,
   390  				Unit:          names.NewUnitTag(changedName).String(),
   391  				Macaroons:     macaroon.Slice{mac},
   392  			}
   393  		}
   394  		return w.remoteModelFacade.RelationUnitSettings(relationUnits)
   395  	}
   396  	remoteUnitsWorker, err := newRelationUnitsWorker(
   397  		relationTag,
   398  		remoteAppToken,
   399  		mac,
   400  		relationToken,
   401  		remoteRelationUnitsWatcher,
   402  		w.remoteRelationChanges,
   403  		remoteUnitSettingsFunc,
   404  	)
   405  	if err != nil {
   406  		return nil, nil, errors.Trace(err)
   407  	}
   408  	if err := w.catacomb.Add(remoteUnitsWorker); err != nil {
   409  		return nil, nil, errors.Trace(err)
   410  	}
   411  	return localUnitsWorker, remoteUnitsWorker, nil
   412  }
   413  
   414  // processConsumingRelation starts the sub-workers necessary to listen and publish
   415  // local unit settings changes, and watch and consume remote unit settings changes.
   416  // Ths will be called when a new relation is created or when a relation resumes
   417  // after being suspended.
   418  func (w *remoteApplicationWorker) processConsumingRelation(
   419  	key string,
   420  	relations map[string]*relation,
   421  	remoteRelation *params.RemoteRelation,
   422  ) error {
   423  
   424  	// We have not seen the relation before, make
   425  	// sure it is registered on the offering side.
   426  	// Or relation was suspended and is now resumed so re-register.
   427  	applicationTag := names.NewApplicationTag(remoteRelation.ApplicationName)
   428  	relationTag := names.NewRelationTag(key)
   429  	applicationToken, remoteAppToken, relationToken, mac, err := w.registerRemoteRelation(
   430  		applicationTag, relationTag, w.offerUUID,
   431  		remoteRelation.Endpoint, remoteRelation.RemoteEndpointName)
   432  	if err != nil {
   433  		w.checkOfferPermissionDenied(err, "", "")
   434  		return errors.Annotatef(err, "registering application %v and relation %v", remoteRelation.ApplicationName, relationTag.Id())
   435  	}
   436  
   437  	// Have we seen the relation before.
   438  	r, relationKnown := relations[key]
   439  	if !relationKnown {
   440  		// Totally new so start the lifecycle watcher.
   441  		remoteRelationsWatcher, err := w.remoteModelFacade.WatchRelationSuspendedStatus(params.RemoteEntityArg{
   442  			Token:     relationToken,
   443  			Macaroons: macaroon.Slice{mac},
   444  		})
   445  		if err != nil {
   446  			w.checkOfferPermissionDenied(err, remoteAppToken, relationToken)
   447  			return errors.Annotatef(err, "watching remote side of relation %v", remoteRelation.Key)
   448  		}
   449  
   450  		remoteRelationsWorker, err := newRemoteRelationsWorker(
   451  			relationTag,
   452  			remoteAppToken,
   453  			relationToken,
   454  			remoteRelationsWatcher,
   455  			w.remoteRelationChanges,
   456  		)
   457  		if err != nil {
   458  			return errors.Trace(err)
   459  		}
   460  		if err := w.catacomb.Add(remoteRelationsWorker); err != nil {
   461  			return errors.Trace(err)
   462  		}
   463  		r = &relation{
   464  			relationId:         remoteRelation.Id,
   465  			suspended:          remoteRelation.Suspended,
   466  			remoteRrw:          remoteRelationsWorker,
   467  			macaroon:           mac,
   468  			localEndpoint:      remoteRelation.Endpoint,
   469  			remoteEndpointName: remoteRelation.RemoteEndpointName,
   470  			applicationToken:   applicationToken,
   471  			relationToken:      relationToken,
   472  		}
   473  		relations[key] = r
   474  	}
   475  
   476  	if r.localRuw == nil && !remoteRelation.Suspended {
   477  		// Also start the units watchers (local and remote).
   478  		localUnitsWorker, remoteUnitsWorker, err := w.startUnitsWorkers(
   479  			relationTag, applicationToken, relationToken, remoteAppToken, remoteRelation.ApplicationName, mac)
   480  		if err != nil {
   481  			return errors.Annotate(err, "starting relation units workers")
   482  		}
   483  		r.localRuw = localUnitsWorker
   484  		r.remoteRuw = remoteUnitsWorker
   485  	}
   486  
   487  	// If the relation is dying, stop the watcher.
   488  	if remoteRelation.Life != params.Alive {
   489  		return w.processRelationDying(key, r, !relationKnown)
   490  	}
   491  
   492  	return nil
   493  }
   494  
   495  func (w *remoteApplicationWorker) registerRemoteRelation(
   496  	applicationTag, relationTag names.Tag, offerUUID string,
   497  	localEndpointInfo params.RemoteEndpoint, remoteEndpointName string,
   498  ) (applicationToken, offeringAppToken, relationToken string, _ *macaroon.Macaroon, _ error) {
   499  	logger.Debugf("register remote relation %v to local application %v", relationTag.Id(), applicationTag.Id())
   500  
   501  	fail := func(err error) (string, string, string, *macaroon.Macaroon, error) {
   502  		return "", "", "", nil, err
   503  	}
   504  
   505  	// Ensure the relation is exported first up.
   506  	results, err := w.localModelFacade.ExportEntities([]names.Tag{applicationTag, relationTag})
   507  	if err != nil {
   508  		return fail(errors.Annotatef(err, "exporting relation %v and application %v", relationTag, applicationTag))
   509  	}
   510  	if results[0].Error != nil && !params.IsCodeAlreadyExists(results[0].Error) {
   511  		return fail(errors.Annotatef(err, "exporting application %v", applicationTag))
   512  	}
   513  	applicationToken = results[0].Token
   514  	if results[1].Error != nil && !params.IsCodeAlreadyExists(results[1].Error) {
   515  		return fail(errors.Annotatef(err, "exporting relation %v", relationTag))
   516  	}
   517  	relationToken = results[1].Token
   518  
   519  	// This data goes to the remote model so we map local info
   520  	// from this model to the remote arg values and visa versa.
   521  	arg := params.RegisterRemoteRelationArg{
   522  		ApplicationToken:  applicationToken,
   523  		SourceModelTag:    names.NewModelTag(w.localModelUUID).String(),
   524  		RelationToken:     relationToken,
   525  		OfferUUID:         offerUUID,
   526  		RemoteEndpoint:    localEndpointInfo,
   527  		LocalEndpointName: remoteEndpointName,
   528  	}
   529  	if w.offerMacaroon != nil {
   530  		arg.Macaroons = macaroon.Slice{w.offerMacaroon}
   531  	}
   532  	remoteRelation, err := w.remoteModelFacade.RegisterRemoteRelations(arg)
   533  	if err != nil {
   534  		return fail(errors.Trace(err))
   535  	}
   536  	// remoteAppIds is a slice but there's only one item
   537  	// as we currently only register one remote application
   538  	if err := remoteRelation[0].Error; err != nil {
   539  		return fail(errors.Annotatef(err, "registering relation %v", relationTag))
   540  	}
   541  	// Import the application id from the offering model.
   542  	registerResult := *remoteRelation[0].Result
   543  	offeringAppToken = registerResult.Token
   544  	// We have a new macaroon attenuated to the relation.
   545  	// Save for the firewaller.
   546  	if err := w.localModelFacade.SaveMacaroon(relationTag, registerResult.Macaroon); err != nil {
   547  		return fail(errors.Annotatef(
   548  			err, "saving macaroon for %v", relationTag))
   549  	}
   550  
   551  	appTag := names.NewApplicationTag(w.applicationName)
   552  	logger.Debugf("import remote application token %v for %v", offeringAppToken, w.applicationName)
   553  	err = w.localModelFacade.ImportRemoteEntity(appTag, offeringAppToken)
   554  	if err != nil && !params.IsCodeAlreadyExists(err) {
   555  		return fail(errors.Annotatef(
   556  			err, "importing remote application %v to local model", w.applicationName))
   557  	}
   558  	return applicationToken, offeringAppToken, relationToken, registerResult.Macaroon, nil
   559  }