github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/leadership/leadership.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package leadership
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/leadership"
    15  	"github.com/juju/juju/lease"
    16  	"github.com/juju/juju/state"
    17  )
    18  
    19  const (
    20  	// FacadeName is the string-representation of this API used both
    21  	// to register the service, and for the client to resolve the
    22  	// service endpoint.
    23  	FacadeName = "LeadershipService"
    24  )
    25  
    26  var (
    27  	logger = loggo.GetLogger("juju.apiserver.leadership")
    28  	// Begin injection-chain so we can instantiate leadership
    29  	// services. Exposed as variables so we can change the
    30  	// implementation for testing purposes.
    31  	leaseMgr  = lease.Manager()
    32  	leaderMgr = leadership.NewLeadershipManager(leaseMgr)
    33  )
    34  
    35  func init() {
    36  
    37  	common.RegisterStandardFacade(
    38  		FacadeName,
    39  		1,
    40  		NewLeadershipServiceFn(leaderMgr),
    41  	)
    42  }
    43  
    44  // NewLeadershipServiceFn returns a function which can construct a
    45  // LeadershipService when passed a state, resources, and authorizer.
    46  // This function signature conforms to Juju's required API server
    47  // signature.
    48  func NewLeadershipServiceFn(
    49  	leadershipMgr leadership.LeadershipManager,
    50  ) func(*state.State, *common.Resources, common.Authorizer) (LeadershipService, error) {
    51  	return func(
    52  		state *state.State,
    53  		resources *common.Resources,
    54  		authorizer common.Authorizer,
    55  	) (LeadershipService, error) {
    56  		return NewLeadershipService(state, resources, authorizer, leadershipMgr)
    57  	}
    58  }
    59  
    60  // NewLeadershipService constructs a new LeadershipService.
    61  func NewLeadershipService(
    62  	state *state.State,
    63  	resources *common.Resources,
    64  	authorizer common.Authorizer,
    65  	leadershipMgr leadership.LeadershipManager,
    66  ) (LeadershipService, error) {
    67  
    68  	if !authorizer.AuthUnitAgent() {
    69  		return nil, common.ErrPerm
    70  	}
    71  
    72  	return &leadershipService{
    73  		state:             state,
    74  		authorizer:        authorizer,
    75  		LeadershipManager: leadershipMgr,
    76  	}, nil
    77  }
    78  
    79  // LeadershipService implements the LeadershipManager interface and
    80  // is the concrete implementation of the API endpoint.
    81  type leadershipService struct {
    82  	state      *state.State
    83  	authorizer common.Authorizer
    84  	leadership.LeadershipManager
    85  }
    86  
    87  // ClaimLeadership implements the LeadershipService interface.
    88  func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) {
    89  
    90  	var dur time.Duration
    91  	claim := callWithIds(func(sid, uid string) (err error) {
    92  		dur, err = m.LeadershipManager.ClaimLeadership(sid, uid)
    93  		return err
    94  	})
    95  
    96  	results := make([]params.ClaimLeadershipResults, len(args.Params))
    97  	for pIdx, p := range args.Params {
    98  
    99  		result := &results[pIdx]
   100  		svcTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag)
   101  		if err != nil {
   102  			result.Error = err
   103  			continue
   104  		}
   105  
   106  		// In the future, situations may arise wherein units will make
   107  		// leadership claims for other units. For now, units can only
   108  		// claim leadership for themselves.
   109  		if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) {
   110  			result.Error = common.ServerError(common.ErrPerm)
   111  			continue
   112  		} else if err := claim(svcTag, unitTag).Error; err != nil {
   113  			result.Error = err
   114  			continue
   115  		}
   116  
   117  		result.ClaimDurationInSec = dur.Seconds()
   118  		result.ServiceTag = p.ServiceTag
   119  	}
   120  
   121  	return params.ClaimLeadershipBulkResults{results}, nil
   122  }
   123  
   124  // ReleaseLeadership implements the LeadershipService interface.
   125  func (m *leadershipService) ReleaseLeadership(args params.ReleaseLeadershipBulkParams) (params.ReleaseLeadershipBulkResults, error) {
   126  
   127  	release := callWithIds(m.LeadershipManager.ReleaseLeadership)
   128  	results := make([]params.ErrorResult, len(args.Params))
   129  
   130  	for paramIdx, p := range args.Params {
   131  
   132  		result := &results[paramIdx]
   133  		svcTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag)
   134  		if err != nil {
   135  			result.Error = err
   136  			continue
   137  		}
   138  
   139  		// In the future, situations may arise wherein units will make
   140  		// leadership claims for other units. For now, units can only
   141  		// claim leadership for themselves.
   142  		if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) {
   143  			result.Error = common.ServerError(common.ErrPerm)
   144  			continue
   145  		}
   146  
   147  		result.Error = release(svcTag, unitTag).Error
   148  	}
   149  
   150  	return params.ReleaseLeadershipBulkResults{results}, nil
   151  }
   152  
   153  // BlockUntilLeadershipReleased implements the LeadershipService interface.
   154  func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) {
   155  	if !m.authorizer.AuthUnitAgent() {
   156  		return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil
   157  	}
   158  
   159  	if err := m.LeadershipManager.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil {
   160  		return params.ErrorResult{Error: common.ServerError(err)}, nil
   161  	}
   162  	return params.ErrorResult{}, nil
   163  }
   164  
   165  // callWithIds transforms a common Leadership Election function
   166  // signature into one that is conducive to use in the API Server.
   167  func callWithIds(fn func(string, string) error) func(st, ut names.Tag) params.ErrorResult {
   168  	return func(st, ut names.Tag) (result params.ErrorResult) {
   169  		if err := fn(st.Id(), ut.Id()); err != nil {
   170  			result.Error = common.ServerError(err)
   171  		}
   172  		return result
   173  	}
   174  }
   175  
   176  // parseServiceAndUnitTags takes in string representations of service
   177  // and unit tags and parses them into structs.
   178  //
   179  // NOTE: we return permissions errors when parsing fails to obfuscate
   180  // the parse-failure. This is for security purposes.
   181  func parseServiceAndUnitTags(
   182  	serviceTagString, unitTagString string,
   183  ) (serviceTag names.ServiceTag, unitTag names.UnitTag, _ *params.Error) {
   184  	serviceTag, err := names.ParseServiceTag(serviceTagString)
   185  	if err != nil {
   186  		return serviceTag, unitTag, common.ServerError(common.ErrPerm)
   187  	}
   188  
   189  	unitTag, err = names.ParseUnitTag(unitTagString)
   190  	if err != nil {
   191  		return serviceTag, unitTag, common.ServerError(common.ErrPerm)
   192  	}
   193  
   194  	return serviceTag, unitTag, nil
   195  }