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

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package meterstatus provides the meter status API facade.
     5  package meterstatus
     6  
     7  import (
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/facade"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/state/watcher"
    16  )
    17  
    18  // MeterStatus defines the methods exported by the meter status API facade.
    19  type MeterStatus interface {
    20  	GetMeterStatus(args params.Entities) (params.MeterStatusResults, error)
    21  	WatchMeterStatus(args params.Entities) (params.NotifyWatchResults, error)
    22  }
    23  
    24  // MeterStatusState represents the state of an model required by the MeterStatus.
    25  //go:generate mockgen -package mocks -destination mocks/meterstatus_mock.go github.com/juju/juju/apiserver/facades/agent/meterstatus MeterStatusState
    26  type MeterStatusState interface {
    27  
    28  	// Application returns a application state by name.
    29  	Application(name string) (*state.Application, error)
    30  
    31  	// Unit returns a unit by name.
    32  	Unit(id string) (*state.Unit, error)
    33  }
    34  
    35  // MeterStatusAPI implements the MeterStatus interface and is the concrete implementation
    36  // of the API endpoint.
    37  type MeterStatusAPI struct {
    38  	state      MeterStatusState
    39  	accessUnit common.GetAuthFunc
    40  	resources  facade.Resources
    41  }
    42  
    43  // NewMeterStatusFacade provides the signature required for facade registration.
    44  func NewMeterStatusFacade(ctx facade.Context) (*MeterStatusAPI, error) {
    45  	authorizer := ctx.Auth()
    46  	resources := ctx.Resources()
    47  	return NewMeterStatusAPI(ctx.State(), resources, authorizer)
    48  }
    49  
    50  // NewMeterStatusAPI creates a new API endpoint for dealing with unit meter status.
    51  func NewMeterStatusAPI(
    52  	st MeterStatusState,
    53  	resources facade.Resources,
    54  	authorizer facade.Authorizer,
    55  ) (*MeterStatusAPI, error) {
    56  	if !authorizer.AuthUnitAgent() && !authorizer.AuthApplicationAgent() {
    57  		return nil, common.ErrPerm
    58  	}
    59  	return &MeterStatusAPI{
    60  		state: st,
    61  		accessUnit: func() (common.AuthFunc, error) {
    62  			switch tag := authorizer.GetAuthTag().(type) {
    63  			case names.ApplicationTag:
    64  				// If called by an application agent, any of the units
    65  				// belonging to that application can be accessed.
    66  				app, err := st.Application(tag.Name)
    67  				if err != nil {
    68  					return nil, errors.Trace(err)
    69  				}
    70  				allUnits, err := app.AllUnits()
    71  				if err != nil {
    72  					return nil, errors.Trace(err)
    73  				}
    74  				return func(tag names.Tag) bool {
    75  					for _, u := range allUnits {
    76  						if u.Tag() == tag {
    77  							return true
    78  						}
    79  					}
    80  					return false
    81  				}, nil
    82  			case names.UnitTag:
    83  				return func(tag names.Tag) bool {
    84  					return authorizer.AuthOwner(tag)
    85  				}, nil
    86  			default:
    87  				return nil, errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)
    88  			}
    89  		},
    90  		resources: resources,
    91  	}, nil
    92  }
    93  
    94  // WatchMeterStatus returns a NotifyWatcher for observing changes
    95  // to each unit's meter status.
    96  func (m *MeterStatusAPI) WatchMeterStatus(args params.Entities) (params.NotifyWatchResults, error) {
    97  	result := params.NotifyWatchResults{
    98  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
    99  	}
   100  	canAccess, err := m.accessUnit()
   101  	if err != nil {
   102  		return params.NotifyWatchResults{}, err
   103  	}
   104  	for i, entity := range args.Entities {
   105  		tag, err := names.ParseUnitTag(entity.Tag)
   106  		if err != nil {
   107  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   108  			continue
   109  		}
   110  		err = common.ErrPerm
   111  		var watcherId string
   112  		if canAccess(tag) {
   113  			watcherId, err = m.watchOneUnitMeterStatus(tag)
   114  		}
   115  		result.Results[i].NotifyWatcherId = watcherId
   116  		result.Results[i].Error = common.ServerError(err)
   117  	}
   118  	return result, nil
   119  }
   120  
   121  func (m *MeterStatusAPI) watchOneUnitMeterStatus(tag names.UnitTag) (string, error) {
   122  	unit, err := m.state.Unit(tag.Id())
   123  	if err != nil {
   124  		return "", err
   125  	}
   126  	watch := unit.WatchMeterStatus()
   127  	if _, ok := <-watch.Changes(); ok {
   128  		return m.resources.Register(watch), nil
   129  	}
   130  	return "", watcher.EnsureErr(watch)
   131  }
   132  
   133  // GetMeterStatus returns meter status information for each unit.
   134  func (m *MeterStatusAPI) GetMeterStatus(args params.Entities) (params.MeterStatusResults, error) {
   135  	result := params.MeterStatusResults{
   136  		Results: make([]params.MeterStatusResult, len(args.Entities)),
   137  	}
   138  	canAccess, err := m.accessUnit()
   139  	if err != nil {
   140  		return params.MeterStatusResults{}, common.ErrPerm
   141  	}
   142  	for i, entity := range args.Entities {
   143  		unitTag, err := names.ParseUnitTag(entity.Tag)
   144  		if err != nil {
   145  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   146  			continue
   147  		}
   148  		err = common.ErrPerm
   149  		var status state.MeterStatus
   150  		if canAccess(unitTag) {
   151  			var unit *state.Unit
   152  			unit, err = m.state.Unit(unitTag.Id())
   153  			if err == nil {
   154  				status, err = MeterStatusWrapper(unit.GetMeterStatus)
   155  			}
   156  			result.Results[i].Code = status.Code.String()
   157  			result.Results[i].Info = status.Info
   158  		}
   159  		result.Results[i].Error = common.ServerError(err)
   160  	}
   161  	return result, nil
   162  }