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

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package upgradeseries
     5  
     6  import (
     7  	"github.com/juju/loggo"
     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/core/model"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.apiserver.upgradeseries")
    18  
    19  // API serves methods required by the machine agent upgrade-series worker.
    20  type API struct {
    21  	*common.UpgradeSeriesAPI
    22  	common.LeadershipPinningAPI
    23  
    24  	st        common.UpgradeSeriesBackend
    25  	auth      facade.Authorizer
    26  	resources facade.Resources
    27  }
    28  
    29  // NewAPI creates a new instance of the API with the given context
    30  func NewAPI(ctx facade.Context) (*API, error) {
    31  	leadership, err := common.NewLeadershipPinningFacade(ctx)
    32  	if err != nil {
    33  		if errors.IsNotImplemented(errors.Cause(err)) {
    34  			leadership = disabledLeadershipPinningFacade{}
    35  		} else {
    36  			return nil, errors.Trace(err)
    37  		}
    38  	}
    39  	return NewUpgradeSeriesAPI(common.UpgradeSeriesState{St: ctx.State()}, ctx.Resources(), ctx.Auth(), leadership)
    40  }
    41  
    42  // NewUpgradeSeriesAPI creates a new instance of the API server using the
    43  // dedicated state indirection.
    44  func NewUpgradeSeriesAPI(
    45  	st common.UpgradeSeriesBackend,
    46  	resources facade.Resources,
    47  	authorizer facade.Authorizer,
    48  	leadership common.LeadershipPinningAPI,
    49  ) (*API, error) {
    50  	if !authorizer.AuthMachineAgent() {
    51  		return nil, common.ErrPerm
    52  	}
    53  
    54  	accessMachine := func() (common.AuthFunc, error) {
    55  		return func(tag names.Tag) bool {
    56  			return authorizer.AuthOwner(tag)
    57  		}, nil
    58  	}
    59  	accessUnit := func() (common.AuthFunc, error) {
    60  		return func(tag names.Tag) bool {
    61  			return false
    62  		}, nil
    63  	}
    64  
    65  	return &API{
    66  		st:                   st,
    67  		resources:            resources,
    68  		auth:                 authorizer,
    69  		UpgradeSeriesAPI:     common.NewUpgradeSeriesAPI(st, resources, authorizer, accessMachine, accessUnit, logger),
    70  		LeadershipPinningAPI: leadership,
    71  	}, nil
    72  }
    73  
    74  // MachineStatus gets the current upgrade-series status of a machine.
    75  func (a *API) MachineStatus(args params.Entities) (params.UpgradeSeriesStatusResults, error) {
    76  	result := params.UpgradeSeriesStatusResults{}
    77  
    78  	canAccess, err := a.AccessMachine()
    79  	if err != nil {
    80  		return result, err
    81  	}
    82  
    83  	results := make([]params.UpgradeSeriesStatusResult, len(args.Entities))
    84  	for i, entity := range args.Entities {
    85  		machine, err := a.authAndMachine(entity, canAccess)
    86  		if err != nil {
    87  			results[i].Error = common.ServerError(err)
    88  			continue
    89  		}
    90  		status, err := machine.UpgradeSeriesStatus()
    91  		if err != nil {
    92  			results[i].Error = common.ServerError(err)
    93  			continue
    94  		}
    95  		results[i].Status = status
    96  	}
    97  
    98  	result.Results = results
    99  	return result, nil
   100  }
   101  
   102  // SetMachineStatus sets the current upgrade-series status of a machine.
   103  func (a *API) SetMachineStatus(args params.UpgradeSeriesStatusParams) (params.ErrorResults, error) {
   104  	result := params.ErrorResults{}
   105  
   106  	canAccess, err := a.AccessMachine()
   107  	if err != nil {
   108  		return result, err
   109  	}
   110  
   111  	results := make([]params.ErrorResult, len(args.Params))
   112  	for i, param := range args.Params {
   113  		machine, err := a.authAndMachine(param.Entity, canAccess)
   114  		if err != nil {
   115  			results[i].Error = common.ServerError(err)
   116  			continue
   117  		}
   118  		err = machine.SetUpgradeSeriesStatus(param.Status, param.Message)
   119  		if err != nil {
   120  			results[i].Error = common.ServerError(err)
   121  		}
   122  	}
   123  
   124  	result.Results = results
   125  	return result, nil
   126  }
   127  
   128  // TargetSeries returns the series that a machine has been locked
   129  // for upgrading to.
   130  func (a *API) TargetSeries(args params.Entities) (params.StringResults, error) {
   131  	result := params.StringResults{}
   132  
   133  	canAccess, err := a.AccessMachine()
   134  	if err != nil {
   135  		return result, err
   136  	}
   137  
   138  	results := make([]params.StringResult, len(args.Entities))
   139  	for i, entity := range args.Entities {
   140  		machine, err := a.authAndMachine(entity, canAccess)
   141  		if err != nil {
   142  			results[i].Error = common.ServerError(err)
   143  			continue
   144  		}
   145  		target, err := machine.UpgradeSeriesTarget()
   146  		if err != nil {
   147  			results[i].Error = common.ServerError(err)
   148  		}
   149  		results[i].Result = target
   150  	}
   151  
   152  	result.Results = results
   153  	return result, nil
   154  }
   155  
   156  // StartUnitCompletion starts the upgrade series completion phase for all subordinate
   157  // units of a given machine.
   158  func (a *API) StartUnitCompletion(args params.UpgradeSeriesStartUnitCompletionParam) (params.ErrorResults, error) {
   159  	result := params.ErrorResults{
   160  		Results: make([]params.ErrorResult, len(args.Entities)),
   161  	}
   162  	canAccess, err := a.AccessMachine()
   163  	if err != nil {
   164  		return params.ErrorResults{}, err
   165  	}
   166  	for i, entity := range args.Entities {
   167  		machine, err := a.authAndMachine(entity, canAccess)
   168  		if err != nil {
   169  			result.Results[i].Error = common.ServerError(err)
   170  			continue
   171  		}
   172  		err = machine.StartUpgradeSeriesUnitCompletion(args.Message)
   173  		if err != nil {
   174  			result.Results[i].Error = common.ServerError(err)
   175  			continue
   176  		}
   177  	}
   178  	return result, nil
   179  }
   180  
   181  // FinishUpgradeSeries is the last action in the upgrade workflow and is
   182  // called after all machine and unit statuses are "completed".
   183  // It updates the machine series to reflect the completed upgrade, then
   184  // removes the upgrade-series lock.
   185  func (a *API) FinishUpgradeSeries(args params.UpdateSeriesArgs) (params.ErrorResults, error) {
   186  	result := params.ErrorResults{
   187  		Results: make([]params.ErrorResult, len(args.Args)),
   188  	}
   189  	canAccess, err := a.AccessMachine()
   190  	if err != nil {
   191  		return params.ErrorResults{}, err
   192  	}
   193  	for i, arg := range args.Args {
   194  		machine, err := a.authAndMachine(arg.Entity, canAccess)
   195  		if err != nil {
   196  			result.Results[i].Error = common.ServerError(err)
   197  			continue
   198  		}
   199  
   200  		// Actually running "do-release-upgrade" is not required to complete a
   201  		// series upgrade, so we compare the incoming host OS with the machine.
   202  		// Only update if they differ, because calling UpgradeSeriesTarget
   203  		// cascades through units and subordinates to verify series support,
   204  		// which we might as well skip unless an update is required.
   205  		ms := machine.Series()
   206  		if arg.Series == ms {
   207  			logger.Debugf("%q series is unchanged from %q", arg.Entity.Tag, ms)
   208  		} else {
   209  			if err := machine.UpdateMachineSeries(arg.Series, true); err != nil {
   210  				result.Results[i].Error = common.ServerError(err)
   211  				continue
   212  			}
   213  		}
   214  
   215  		err = machine.RemoveUpgradeSeriesLock()
   216  		if err != nil {
   217  			result.Results[i].Error = common.ServerError(err)
   218  			continue
   219  		}
   220  	}
   221  	return result, nil
   222  }
   223  
   224  // UnitsPrepared returns the units running on this machine that have completed
   225  // their upgrade-series preparation, and are ready to be stopped and have their
   226  // unit agent services converted for the target series.
   227  func (a *API) UnitsPrepared(args params.Entities) (params.EntitiesResults, error) {
   228  	result, err := a.unitsInState(args, model.UpgradeSeriesPrepareCompleted)
   229  	return result, errors.Trace(err)
   230  }
   231  
   232  // UnitsCompleted returns the units running on this machine that have completed
   233  // the upgrade-series workflow and are in their normal running state.
   234  func (a *API) UnitsCompleted(args params.Entities) (params.EntitiesResults, error) {
   235  	result, err := a.unitsInState(args, model.UpgradeSeriesCompleted)
   236  	return result, errors.Trace(err)
   237  }
   238  
   239  func (a *API) unitsInState(args params.Entities, status model.UpgradeSeriesStatus) (params.EntitiesResults, error) {
   240  	result := params.EntitiesResults{}
   241  
   242  	canAccess, err := a.AccessMachine()
   243  	if err != nil {
   244  		return result, err
   245  	}
   246  
   247  	results := make([]params.EntitiesResult, len(args.Entities))
   248  	for i, entity := range args.Entities {
   249  		machine, err := a.authAndMachine(entity, canAccess)
   250  		if err != nil {
   251  			results[i].Error = common.ServerError(err)
   252  			continue
   253  		}
   254  
   255  		statuses, err := machine.UpgradeSeriesUnitStatuses()
   256  		if err != nil {
   257  			results[i].Error = common.ServerError(err)
   258  			continue
   259  		}
   260  
   261  		var entities []params.Entity
   262  		for id, s := range statuses {
   263  			if s.Status == status {
   264  				entities = append(entities, params.Entity{Tag: names.NewUnitTag(id).String()})
   265  			}
   266  		}
   267  		results[i].Entities = entities
   268  	}
   269  
   270  	result.Results = results
   271  	return result, nil
   272  }
   273  
   274  func (a *API) authAndMachine(e params.Entity, canAccess common.AuthFunc) (common.UpgradeSeriesMachine, error) {
   275  	tag, err := names.ParseMachineTag(e.Tag)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	if !canAccess(tag) {
   280  		return nil, common.ErrPerm
   281  	}
   282  	return a.GetMachine(tag)
   283  }
   284  
   285  // disabledLeadershipPinningFacade implements the LeadershipPinningAPI, but
   286  // provides a no-operation for pinning operations
   287  type disabledLeadershipPinningFacade struct{}
   288  
   289  // PinMachineApplications implements common.LeadershipPinningAPI
   290  func (disabledLeadershipPinningFacade) PinMachineApplications() (params.PinApplicationsResults, error) {
   291  	return params.PinApplicationsResults{}, errors.NotImplementedf(
   292  		"unable to get leadership pinner; pinning is not available with the legacy lease manager")
   293  }
   294  
   295  // UnpinMachineApplications implements common.LeadershipPinningAPI
   296  func (disabledLeadershipPinningFacade) UnpinMachineApplications() (params.PinApplicationsResults, error) {
   297  	return params.PinApplicationsResults{}, nil
   298  }
   299  
   300  // PinnedLeadership implements common.LeadershipPinningAPI
   301  func (disabledLeadershipPinningFacade) PinnedLeadership() (params.PinnedLeadershipResult, error) {
   302  	return params.PinnedLeadershipResult{}, nil
   303  }