github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/caasoperator/operator.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperator
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/facade"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/caas"
    14  	"github.com/juju/juju/core/status"
    15  	"github.com/juju/juju/environs"
    16  	"github.com/juju/juju/state/watcher"
    17  )
    18  
    19  type Facade struct {
    20  	auth      facade.Authorizer
    21  	resources facade.Resources
    22  	state     CAASOperatorState
    23  	*common.LifeGetter
    24  	*common.AgentEntityWatcher
    25  	*common.Remover
    26  	*common.ToolsSetter
    27  	*common.APIAddresser
    28  
    29  	model Model
    30  }
    31  
    32  // NewStateFacade provides the signature required for facade registration.
    33  func NewStateFacade(ctx facade.Context) (*Facade, error) {
    34  	authorizer := ctx.Auth()
    35  	resources := ctx.Resources()
    36  	return NewFacade(resources, authorizer, stateShim{ctx.State()})
    37  }
    38  
    39  // NewFacade returns a new CAASOperator facade.
    40  func NewFacade(
    41  	resources facade.Resources,
    42  	authorizer facade.Authorizer,
    43  	st CAASOperatorState,
    44  ) (*Facade, error) {
    45  	if !authorizer.AuthApplicationAgent() {
    46  		return nil, common.ErrPerm
    47  	}
    48  	model, err := st.Model()
    49  	if err != nil {
    50  		return nil, errors.Trace(err)
    51  	}
    52  	canRead := common.AuthAny(
    53  		common.AuthFuncForTagKind(names.ApplicationTagKind),
    54  		common.AuthFuncForTagKind(names.UnitTagKind),
    55  	)
    56  	accessUnit := func() (common.AuthFunc, error) {
    57  		switch tag := authorizer.GetAuthTag().(type) {
    58  		case names.ApplicationTag:
    59  			// Any of the units belonging to
    60  			// the application can be accessed.
    61  			app, err := st.Application(tag.Name)
    62  			if err != nil {
    63  				return nil, errors.Trace(err)
    64  			}
    65  			allUnits, err := app.AllUnits()
    66  			if err != nil {
    67  				return nil, errors.Trace(err)
    68  			}
    69  			return func(tag names.Tag) bool {
    70  				for _, u := range allUnits {
    71  					if u.Tag() == tag {
    72  						return true
    73  					}
    74  				}
    75  				return false
    76  			}, nil
    77  		default:
    78  			return nil, errors.Errorf("expected names.ApplicationTag, got %T", tag)
    79  		}
    80  	}
    81  	return &Facade{
    82  		LifeGetter:         common.NewLifeGetter(st, canRead),
    83  		APIAddresser:       common.NewAPIAddresser(st, resources),
    84  		AgentEntityWatcher: common.NewAgentEntityWatcher(st, resources, canRead),
    85  		Remover:            common.NewRemover(st, true, accessUnit),
    86  		ToolsSetter:        common.NewToolsSetter(st, common.AuthFuncForTag(authorizer.GetAuthTag())),
    87  		auth:               authorizer,
    88  		resources:          resources,
    89  		state:              st,
    90  		model:              model,
    91  	}, nil
    92  }
    93  
    94  // CurrentModel returns the name and UUID for the current juju model.
    95  func (f *Facade) CurrentModel() (params.ModelResult, error) {
    96  	return params.ModelResult{
    97  		Name: f.model.Name(),
    98  		UUID: f.model.UUID(),
    99  		Type: string(f.model.Type()),
   100  	}, nil
   101  }
   102  
   103  // SetStatus sets the status of each given entity.
   104  func (f *Facade) SetStatus(args params.SetStatus) (params.ErrorResults, error) {
   105  	results := params.ErrorResults{
   106  		Results: make([]params.ErrorResult, len(args.Entities)),
   107  	}
   108  	authTag := f.auth.GetAuthTag()
   109  	for i, arg := range args.Entities {
   110  		tag, err := names.ParseApplicationTag(arg.Tag)
   111  		if err != nil {
   112  			results.Results[i].Error = common.ServerError(err)
   113  			continue
   114  		}
   115  		if tag != authTag {
   116  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   117  			continue
   118  		}
   119  		info := status.StatusInfo{
   120  			Status:  status.Status(arg.Status),
   121  			Message: arg.Info,
   122  			Data:    arg.Data,
   123  		}
   124  		results.Results[i].Error = common.ServerError(f.setStatus(tag, info))
   125  	}
   126  	return results, nil
   127  }
   128  
   129  func (f *Facade) setStatus(tag names.ApplicationTag, info status.StatusInfo) error {
   130  	app, err := f.state.Application(tag.Id())
   131  	if err != nil {
   132  		return errors.Trace(err)
   133  	}
   134  	return app.SetOperatorStatus(info)
   135  }
   136  
   137  // Charm returns the charm info for all given applications.
   138  func (f *Facade) Charm(args params.Entities) (params.ApplicationCharmResults, error) {
   139  	results := params.ApplicationCharmResults{
   140  		Results: make([]params.ApplicationCharmResult, len(args.Entities)),
   141  	}
   142  	authTag := f.auth.GetAuthTag()
   143  	for i, entity := range args.Entities {
   144  		tag, err := names.ParseApplicationTag(entity.Tag)
   145  		if err != nil {
   146  			results.Results[i].Error = common.ServerError(err)
   147  			continue
   148  		}
   149  		if tag != authTag {
   150  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   151  			continue
   152  		}
   153  		application, err := f.state.Application(tag.Id())
   154  		if err != nil {
   155  			results.Results[i].Error = common.ServerError(err)
   156  			continue
   157  		}
   158  		charm, force, err := application.Charm()
   159  		if err != nil {
   160  			results.Results[i].Error = common.ServerError(err)
   161  			continue
   162  		}
   163  		results.Results[i].Result = &params.ApplicationCharm{
   164  			URL:                  charm.URL().String(),
   165  			ForceUpgrade:         force,
   166  			SHA256:               charm.BundleSha256(),
   167  			CharmModifiedVersion: application.CharmModifiedVersion(),
   168  		}
   169  	}
   170  	return results, nil
   171  }
   172  
   173  // SetPodSpec sets the container specs for a set of applications.
   174  func (f *Facade) SetPodSpec(args params.SetPodSpecParams) (params.ErrorResults, error) {
   175  	results := params.ErrorResults{
   176  		Results: make([]params.ErrorResult, len(args.Specs)),
   177  	}
   178  
   179  	cfg, err := f.model.ModelConfig()
   180  	if err != nil {
   181  		return params.ErrorResults{}, errors.Trace(err)
   182  	}
   183  	provider, err := environs.Provider(cfg.Type())
   184  	if err != nil {
   185  		return params.ErrorResults{}, errors.Trace(err)
   186  	}
   187  	caasProvider, ok := provider.(caas.ContainerEnvironProvider)
   188  	if !ok {
   189  		return params.ErrorResults{}, errors.NotValidf("container environ provider %T", provider)
   190  	}
   191  
   192  	for i, arg := range args.Specs {
   193  		tag, err := names.ParseApplicationTag(arg.Tag)
   194  		if err != nil {
   195  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   196  			continue
   197  		}
   198  		if !f.auth.AuthOwner(tag) {
   199  			results.Results[i].Error = common.ServerError(common.ErrPerm)
   200  			continue
   201  		}
   202  		if _, err := caasProvider.ParsePodSpec(arg.Value); err != nil {
   203  			results.Results[i].Error = common.ServerError(errors.New("invalid pod spec"))
   204  			continue
   205  		}
   206  		results.Results[i].Error = common.ServerError(
   207  			f.model.SetPodSpec(tag, arg.Value),
   208  		)
   209  	}
   210  	return results, nil
   211  }
   212  
   213  // WatchUnits starts a StringsWatcher to watch changes to the
   214  // lifecycle states of units for the specified applications in
   215  // this model.
   216  func (f *Facade) WatchUnits(args params.Entities) (params.StringsWatchResults, error) {
   217  	results := params.StringsWatchResults{
   218  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   219  	}
   220  	for i, arg := range args.Entities {
   221  		id, changes, err := f.watchUnits(arg.Tag)
   222  		if err != nil {
   223  			results.Results[i].Error = common.ServerError(err)
   224  			continue
   225  		}
   226  		results.Results[i].StringsWatcherId = id
   227  		results.Results[i].Changes = changes
   228  	}
   229  	return results, nil
   230  }
   231  
   232  func (f *Facade) watchUnits(tagString string) (string, []string, error) {
   233  	tag, err := names.ParseApplicationTag(tagString)
   234  	if err != nil {
   235  		return "", nil, errors.Trace(err)
   236  	}
   237  	app, err := f.state.Application(tag.Id())
   238  	if err != nil {
   239  		return "", nil, errors.Trace(err)
   240  	}
   241  	w := app.WatchUnits()
   242  	if changes, ok := <-w.Changes(); ok {
   243  		return f.resources.Register(w), changes, nil
   244  	}
   245  	return "", nil, watcher.EnsureErr(w)
   246  }