github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/leadership.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  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/facade"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/core/leadership"
    13  	"github.com/juju/juju/permission"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  //go:generate mockgen -package mocks -destination mocks/leadership.go github.com/juju/juju/apiserver/common LeadershipPinningBackend,LeadershipMachine
    18  
    19  // LeadershipMachine is an indirection for state.machine.
    20  type LeadershipMachine interface {
    21  	ApplicationNames() ([]string, error)
    22  }
    23  
    24  type leadershipMachine struct {
    25  	*state.Machine
    26  }
    27  
    28  // LeadershipPinningBacked describes state method wrappers used by this API.
    29  type LeadershipPinningBackend interface {
    30  	Machine(string) (LeadershipMachine, error)
    31  }
    32  
    33  type leadershipPinningBackend struct {
    34  	*state.State
    35  }
    36  
    37  // Machine wraps state.Machine to return an implementation
    38  // of the LeadershipMachine indirection.
    39  func (s leadershipPinningBackend) Machine(name string) (LeadershipMachine, error) {
    40  	m, err := s.State.Machine(name)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	return leadershipMachine{m}, nil
    45  }
    46  
    47  // API exposes leadership pinning and unpinning functionality for remote use.
    48  type LeadershipPinningAPI interface {
    49  	PinMachineApplications() (params.PinApplicationsResults, error)
    50  	UnpinMachineApplications() (params.PinApplicationsResults, error)
    51  	PinnedLeadership() (params.PinnedLeadershipResult, error)
    52  }
    53  
    54  // NewLeadershipPinningFacade creates and returns a new leadership API.
    55  // This signature is suitable for facade registration.
    56  func NewLeadershipPinningFacade(ctx facade.Context) (LeadershipPinningAPI, error) {
    57  	st := ctx.State()
    58  	model, err := st.Model()
    59  	if err != nil {
    60  		return nil, errors.Trace(err)
    61  	}
    62  	pinner, err := ctx.LeadershipPinner(model.UUID())
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	return NewLeadershipPinningAPI(leadershipPinningBackend{st}, model.ModelTag(), pinner, ctx.Auth())
    67  }
    68  
    69  // NewLeadershipPinningAPI creates and returns a new leadership API from the
    70  // input tag, Pinner implementation and facade Authorizer.
    71  func NewLeadershipPinningAPI(
    72  	st LeadershipPinningBackend, modelTag names.ModelTag, pinner leadership.Pinner, authorizer facade.Authorizer,
    73  ) (LeadershipPinningAPI, error) {
    74  	return &leadershipPinningAPI{
    75  		st:         st,
    76  		modelTag:   modelTag,
    77  		pinner:     pinner,
    78  		authorizer: authorizer,
    79  	}, nil
    80  }
    81  
    82  type leadershipPinningAPI struct {
    83  	st         LeadershipPinningBackend
    84  	modelTag   names.ModelTag
    85  	pinner     leadership.Pinner
    86  	authorizer facade.Authorizer
    87  }
    88  
    89  // PinnedLeadership returns all pinned applications and the entities that
    90  // require their pinned behaviour, for leadership in the current model.
    91  func (a *leadershipPinningAPI) PinnedLeadership() (params.PinnedLeadershipResult, error) {
    92  	result := params.PinnedLeadershipResult{}
    93  
    94  	canAccess, err := a.authorizer.HasPermission(permission.ReadAccess, a.modelTag)
    95  	if err != nil {
    96  		return result, errors.Trace(err)
    97  	}
    98  	if !canAccess {
    99  		return result, ErrPerm
   100  	}
   101  
   102  	result.Result = a.pinner.PinnedLeadership()
   103  	return result, nil
   104  }
   105  
   106  // TODO (manadart 2018-10-29): Rename the two methods below (and on the client
   107  // side) to be [Un]PinApplicationLeaders, and derive the list of applications
   108  // based on the authenticating entity.
   109  
   110  // PinMachineApplications pins leadership for applications represented by units
   111  // running on the auth'd machine.
   112  func (a *leadershipPinningAPI) PinMachineApplications() (params.PinApplicationsResults, error) {
   113  	if !a.authorizer.AuthMachineAgent() {
   114  		return params.PinApplicationsResults{}, ErrPerm
   115  	}
   116  	return a.pinMachineAppsOps(a.pinner.PinLeadership)
   117  }
   118  
   119  // UnpinMachineApplications unpins leadership for applications represented by
   120  // units running on the auth'd machine.
   121  func (a *leadershipPinningAPI) UnpinMachineApplications() (params.PinApplicationsResults, error) {
   122  	if !a.authorizer.AuthMachineAgent() {
   123  		return params.PinApplicationsResults{}, ErrPerm
   124  	}
   125  	return a.pinMachineAppsOps(a.pinner.UnpinLeadership)
   126  }
   127  
   128  // pinMachineAppsOps runs the input pin/unpin operation against all
   129  // applications represented by units on the authorised machine.
   130  // An assumption is made that the validity of the auth tag has been verified
   131  // by the caller.
   132  func (a *leadershipPinningAPI) pinMachineAppsOps(op func(string, string) error) (params.PinApplicationsResults, error) {
   133  	result := params.PinApplicationsResults{}
   134  
   135  	tag := a.authorizer.GetAuthTag()
   136  	m, err := a.st.Machine(tag.Id())
   137  	if err != nil {
   138  		return result, errors.Trace(err)
   139  	}
   140  	apps, err := m.ApplicationNames()
   141  	if err != nil {
   142  		return result, errors.Trace(err)
   143  	}
   144  
   145  	results := make([]params.PinApplicationResult, len(apps))
   146  	for i, app := range apps {
   147  		results[i] = params.PinApplicationResult{
   148  			ApplicationName: app,
   149  		}
   150  		if err := op(app, tag.String()); err != nil {
   151  			results[i].Error = ServerError(err)
   152  		}
   153  	}
   154  	result.Results = results
   155  	return result, nil
   156  }