github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/upgradeseries.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/juju/apiserver/facade"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/core/model"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  //go:generate mockgen -package mocks -destination mocks/upgradeseries.go github.com/juju/juju/apiserver/common UpgradeSeriesBackend,UpgradeSeriesMachine,UpgradeSeriesUnit
    18  
    19  type UpgradeSeriesBackend interface {
    20  	Machine(string) (UpgradeSeriesMachine, error)
    21  	Unit(string) (UpgradeSeriesUnit, error)
    22  }
    23  
    24  // UpgradeSeriesMachine describes machine-receiver state methods
    25  // for executing a series upgrade.
    26  type UpgradeSeriesMachine interface {
    27  	WatchUpgradeSeriesNotifications() (state.NotifyWatcher, error)
    28  	Units() ([]UpgradeSeriesUnit, error)
    29  	UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error)
    30  	SetUpgradeSeriesStatus(model.UpgradeSeriesStatus, string) error
    31  	StartUpgradeSeriesUnitCompletion(string) error
    32  	UpgradeSeriesUnitStatuses() (map[string]state.UpgradeSeriesUnitStatus, error)
    33  	RemoveUpgradeSeriesLock() error
    34  	UpgradeSeriesTarget() (string, error)
    35  	Series() string
    36  	UpdateMachineSeries(series string, force bool) error
    37  }
    38  
    39  // UpgradeSeriesUnit describes unit-receiver state methods
    40  // for executing a series upgrade.
    41  type UpgradeSeriesUnit interface {
    42  	Tag() names.Tag
    43  	AssignedMachineId() (string, error)
    44  	UpgradeSeriesStatus() (model.UpgradeSeriesStatus, error)
    45  	SetUpgradeSeriesStatus(model.UpgradeSeriesStatus, string) error
    46  }
    47  
    48  // UpgradeSeriesState implements the UpgradeSeriesBackend indirection
    49  // over state.State.
    50  type UpgradeSeriesState struct {
    51  	St *state.State
    52  }
    53  
    54  func (s UpgradeSeriesState) Machine(id string) (UpgradeSeriesMachine, error) {
    55  	m, err := s.St.Machine(id)
    56  	return &upgradeSeriesMachine{m}, err
    57  }
    58  
    59  func (s UpgradeSeriesState) Unit(id string) (UpgradeSeriesUnit, error) {
    60  	return s.St.Unit(id)
    61  }
    62  
    63  type upgradeSeriesMachine struct {
    64  	*state.Machine
    65  }
    66  
    67  // Units maintains the UpgradeSeriesMachine indirection by wrapping the call to
    68  // state.Machine.Units().
    69  func (m *upgradeSeriesMachine) Units() ([]UpgradeSeriesUnit, error) {
    70  	units, err := m.Machine.Units()
    71  	if err != nil {
    72  		return nil, errors.Trace(err)
    73  	}
    74  
    75  	wrapped := make([]UpgradeSeriesUnit, len(units))
    76  	for i, u := range units {
    77  		wrapped[i] = u
    78  	}
    79  	return wrapped, nil
    80  }
    81  
    82  type UpgradeSeriesAPI struct {
    83  	backend   UpgradeSeriesBackend
    84  	resources facade.Resources
    85  
    86  	logger loggo.Logger
    87  
    88  	accessUnitOrMachine GetAuthFunc
    89  	AccessMachine       GetAuthFunc
    90  	accessUnit          GetAuthFunc
    91  }
    92  
    93  // NewUpgradeSeriesAPI returns a new UpgradeSeriesAPI. Currently both
    94  // GetAuthFuncs can used to determine current permissions.
    95  func NewUpgradeSeriesAPI(
    96  	backend UpgradeSeriesBackend,
    97  	resources facade.Resources,
    98  	authorizer facade.Authorizer,
    99  	accessMachine GetAuthFunc,
   100  	accessUnit GetAuthFunc,
   101  	logger loggo.Logger,
   102  ) *UpgradeSeriesAPI {
   103  	logger.Tracef("NewUpgradeSeriesAPI called with %s", authorizer.GetAuthTag())
   104  	return &UpgradeSeriesAPI{
   105  		backend:             backend,
   106  		resources:           resources,
   107  		accessUnitOrMachine: AuthAny(accessUnit, accessMachine),
   108  		AccessMachine:       accessMachine,
   109  		accessUnit:          accessUnit,
   110  		logger:              logger,
   111  	}
   112  }
   113  
   114  // WatchUpgradeSeriesNotifications returns a NotifyWatcher for observing changes to upgrade series locks.
   115  func (u *UpgradeSeriesAPI) WatchUpgradeSeriesNotifications(args params.Entities) (params.NotifyWatchResults, error) {
   116  	u.logger.Tracef("Starting WatchUpgradeSeriesNotifications with %+v", args)
   117  	result := params.NotifyWatchResults{
   118  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   119  	}
   120  	canAccess, err := u.accessUnitOrMachine()
   121  	if err != nil {
   122  		return params.NotifyWatchResults{}, err
   123  	}
   124  	for i, entity := range args.Entities {
   125  		tag, err := names.ParseTag(entity.Tag)
   126  		if err != nil {
   127  			result.Results[i].Error = ServerError(ErrPerm)
   128  			continue
   129  		}
   130  
   131  		if !canAccess(tag) {
   132  			result.Results[i].Error = ServerError(ErrPerm)
   133  			continue
   134  		}
   135  		machine, err := u.GetMachine(tag)
   136  		if err != nil {
   137  			result.Results[i].Error = ServerError(err)
   138  			continue
   139  		}
   140  		w, err := machine.WatchUpgradeSeriesNotifications()
   141  		if err != nil {
   142  			result.Results[i].Error = ServerError(err)
   143  			continue
   144  		}
   145  		watcherId := u.resources.Register(w)
   146  		result.Results[i].NotifyWatcherId = watcherId
   147  	}
   148  	return result, nil
   149  }
   150  
   151  // UpgradeSeriesUnitStatus returns the current preparation status of an
   152  // upgrading unit.
   153  // If no series upgrade is in progress an error is returned instead.
   154  func (u *UpgradeSeriesAPI) UpgradeSeriesUnitStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) {
   155  	u.logger.Tracef("Starting UpgradeSeriesUnitStatus with %+v", args)
   156  	return u.unitStatus(args)
   157  }
   158  
   159  // SetUpgradeSeriesUnitStatus sets the upgrade series status of the unit.
   160  // If no upgrade is in progress an error is returned instead.
   161  func (u *UpgradeSeriesAPI) SetUpgradeSeriesUnitStatus(
   162  	args params.UpgradeSeriesStatusParams,
   163  ) (params.ErrorResults, error) {
   164  	u.logger.Tracef("Starting SetUpgradeSeriesUnitStatus with %+v", args)
   165  	return u.setUnitStatus(args)
   166  }
   167  
   168  func (u *UpgradeSeriesAPI) GetMachine(tag names.Tag) (UpgradeSeriesMachine, error) {
   169  	var id string
   170  	switch tag.Kind() {
   171  	case names.MachineTagKind:
   172  		id = tag.Id()
   173  	case names.UnitTagKind:
   174  		unit, err := u.backend.Unit(tag.Id())
   175  		if err != nil {
   176  
   177  		}
   178  		id, err = unit.AssignedMachineId()
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  	default:
   183  	}
   184  	return u.backend.Machine(id)
   185  }
   186  
   187  func (u *UpgradeSeriesAPI) getUnit(tag names.Tag) (UpgradeSeriesUnit, error) {
   188  	return u.backend.Unit(tag.Id())
   189  }
   190  
   191  // NewExternalUpgradeSeriesAPI can be used for API registration.
   192  func NewExternalUpgradeSeriesAPI(
   193  	st *state.State,
   194  	resources facade.Resources,
   195  	authorizer facade.Authorizer,
   196  	accessMachine GetAuthFunc,
   197  	accessUnit GetAuthFunc,
   198  	logger loggo.Logger,
   199  ) *UpgradeSeriesAPI {
   200  	return NewUpgradeSeriesAPI(UpgradeSeriesState{st}, resources, authorizer, accessMachine, accessUnit, logger)
   201  }
   202  
   203  func (u *UpgradeSeriesAPI) setUnitStatus(args params.UpgradeSeriesStatusParams) (params.ErrorResults, error) {
   204  	result := params.ErrorResults{
   205  		Results: make([]params.ErrorResult, len(args.Params)),
   206  	}
   207  	canAccess, err := u.accessUnit()
   208  	if err != nil {
   209  		return params.ErrorResults{}, err
   210  	}
   211  	for i, p := range args.Params {
   212  		//TODO[externalreality] refactor all of this, its being copied often.
   213  		tag, err := names.ParseUnitTag(p.Entity.Tag)
   214  		if err != nil {
   215  			result.Results[i].Error = ServerError(ErrPerm)
   216  			continue
   217  		}
   218  		if !canAccess(tag) {
   219  			result.Results[i].Error = ServerError(ErrPerm)
   220  			continue
   221  		}
   222  		unit, err := u.getUnit(tag)
   223  		if err != nil {
   224  			result.Results[i].Error = ServerError(err)
   225  			continue
   226  		}
   227  		status, err := model.ValidateUpgradeSeriesStatus(p.Status)
   228  		if err != nil {
   229  			result.Results[i].Error = ServerError(err)
   230  			continue
   231  		}
   232  		err = unit.SetUpgradeSeriesStatus(status, p.Message)
   233  		if err != nil {
   234  			result.Results[i].Error = ServerError(err)
   235  			continue
   236  		}
   237  	}
   238  	return result, nil
   239  }
   240  
   241  func (u *UpgradeSeriesAPI) unitStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) {
   242  	canAccess, err := u.accessUnit()
   243  	if err != nil {
   244  		return params.UpgradeSeriesStatusResults{}, err
   245  	}
   246  
   247  	results := make([]params.UpgradeSeriesStatusResult, len(args.Entities))
   248  	for i, entity := range args.Entities {
   249  		tag, err := names.ParseUnitTag(entity.Tag)
   250  		if err != nil {
   251  			results[i].Error = ServerError(ErrPerm)
   252  			continue
   253  		}
   254  		if !canAccess(tag) {
   255  			results[i].Error = ServerError(ErrPerm)
   256  			continue
   257  		}
   258  		unit, err := u.getUnit(tag)
   259  		if err != nil {
   260  			results[i].Error = ServerError(err)
   261  			continue
   262  		}
   263  		status, err := unit.UpgradeSeriesStatus()
   264  		if err != nil {
   265  			results[i].Error = ServerError(err)
   266  			continue
   267  		}
   268  		results[i].Status = status
   269  	}
   270  	return params.UpgradeSeriesStatusResults{Results: results}, nil
   271  }