github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/errors"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/names"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/leadership"
    16  	"github.com/juju/juju/lease"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  const (
    21  	// FacadeName is the string-representation of this API used both
    22  	// to register the service, and for the client to resolve the
    23  	// service endpoint.
    24  	FacadeName = "LeadershipService"
    25  
    26  	// MinLeaseRequest is the shortest duration for which we will accept
    27  	// a leadership claim.
    28  	MinLeaseRequest = 5 * time.Second
    29  
    30  	// MaxLeaseRequest is the longest duration for which we will accept
    31  	// a leadership claim.
    32  	MaxLeaseRequest = 5 * time.Minute
    33  )
    34  
    35  var (
    36  	logger = loggo.GetLogger("juju.apiserver.leadership")
    37  	// Begin injection-chain so we can instantiate leadership
    38  	// services. Exposed as variables so we can change the
    39  	// implementation for testing purposes.
    40  	leaseMgr  = lease.Manager()
    41  	leaderMgr = leadership.NewLeadershipManager(leaseMgr)
    42  )
    43  
    44  func init() {
    45  
    46  	common.RegisterStandardFacade(
    47  		FacadeName,
    48  		1,
    49  		NewLeadershipServiceFn(leaderMgr),
    50  	)
    51  }
    52  
    53  // NewLeadershipServiceFn returns a function which can construct a
    54  // LeadershipService when passed a state, resources, and authorizer.
    55  // This function signature conforms to Juju's required API server
    56  // signature.
    57  func NewLeadershipServiceFn(
    58  	leadershipMgr leadership.LeadershipManager,
    59  ) func(*state.State, *common.Resources, common.Authorizer) (LeadershipService, error) {
    60  	return func(
    61  		state *state.State,
    62  		resources *common.Resources,
    63  		authorizer common.Authorizer,
    64  	) (LeadershipService, error) {
    65  		return NewLeadershipService(state, resources, authorizer, leadershipMgr)
    66  	}
    67  }
    68  
    69  // NewLeadershipService constructs a new LeadershipService.
    70  func NewLeadershipService(
    71  	state *state.State,
    72  	resources *common.Resources,
    73  	authorizer common.Authorizer,
    74  	leadershipMgr leadership.LeadershipManager,
    75  ) (LeadershipService, error) {
    76  
    77  	if !authorizer.AuthUnitAgent() {
    78  		return nil, common.ErrPerm
    79  	}
    80  
    81  	return &leadershipService{
    82  		state:             state,
    83  		authorizer:        authorizer,
    84  		LeadershipManager: leadershipMgr,
    85  	}, nil
    86  }
    87  
    88  // LeadershipService implements the LeadershipManager interface and
    89  // is the concrete implementation of the API endpoint.
    90  type leadershipService struct {
    91  	state      *state.State
    92  	authorizer common.Authorizer
    93  	leadership.LeadershipManager
    94  }
    95  
    96  // ClaimLeadership implements the LeadershipService interface.
    97  func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) {
    98  
    99  	results := make([]params.ErrorResult, len(args.Params))
   100  	for pIdx, p := range args.Params {
   101  
   102  		result := &results[pIdx]
   103  		serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag)
   104  		if err != nil {
   105  			result.Error = common.ServerError(err)
   106  			continue
   107  		}
   108  		duration := time.Duration(p.DurationSeconds * float64(time.Second))
   109  		if duration > MaxLeaseRequest || duration < MinLeaseRequest {
   110  			result.Error = common.ServerError(errors.New("invalid duration"))
   111  			continue
   112  		}
   113  
   114  		// In the future, situations may arise wherein units will make
   115  		// leadership claims for other units. For now, units can only
   116  		// claim leadership for themselves.
   117  		if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) {
   118  			result.Error = common.ServerError(common.ErrPerm)
   119  			continue
   120  		}
   121  
   122  		err = m.LeadershipManager.ClaimLeadership(serviceTag.Id(), unitTag.Id(), duration)
   123  		if err != nil {
   124  			result.Error = common.ServerError(err)
   125  		}
   126  	}
   127  
   128  	return params.ClaimLeadershipBulkResults{results}, nil
   129  }
   130  
   131  // ReleaseLeadership implements the LeadershipService interface.
   132  func (m *leadershipService) ReleaseLeadership(args params.ReleaseLeadershipBulkParams) (params.ReleaseLeadershipBulkResults, error) {
   133  
   134  	results := make([]params.ErrorResult, len(args.Params))
   135  
   136  	for paramIdx, p := range args.Params {
   137  
   138  		result := &results[paramIdx]
   139  		serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag)
   140  		if err != nil {
   141  			result.Error = common.ServerError(err)
   142  			continue
   143  		}
   144  
   145  		// In the future, situations may arise wherein units will make
   146  		// leadership claims for other units. For now, units can only
   147  		// claim leadership for themselves.
   148  		if !m.authorizer.AuthUnitAgent() || !m.authorizer.AuthOwner(unitTag) {
   149  			result.Error = common.ServerError(common.ErrPerm)
   150  			continue
   151  		}
   152  
   153  		err = m.LeadershipManager.ReleaseLeadership(serviceTag.Id(), unitTag.Id())
   154  		if err != nil {
   155  			result.Error = common.ServerError(err)
   156  		}
   157  	}
   158  
   159  	return params.ReleaseLeadershipBulkResults{results}, nil
   160  }
   161  
   162  // BlockUntilLeadershipReleased implements the LeadershipService interface.
   163  func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) {
   164  	if !m.authorizer.AuthUnitAgent() {
   165  		return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil
   166  	}
   167  
   168  	if err := m.LeadershipManager.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil {
   169  		return params.ErrorResult{Error: common.ServerError(err)}, nil
   170  	}
   171  	return params.ErrorResult{}, nil
   172  }
   173  
   174  // parseServiceAndUnitTags takes in string representations of service
   175  // and unit tags and returns their corresponding tags.
   176  func parseServiceAndUnitTags(
   177  	serviceTagString, unitTagString string,
   178  ) (
   179  	names.ServiceTag, names.UnitTag, error,
   180  ) {
   181  	// TODO(fwereade) 2015-02-25 bug #1425506
   182  	// These permissions errors are not appropriate -- there's no permission or
   183  	// security issue in play here, because our tag format is public, and the
   184  	// error only triggers when the strings fail to match that format.
   185  	serviceTag, err := names.ParseServiceTag(serviceTagString)
   186  	if err != nil {
   187  		return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm
   188  	}
   189  
   190  	unitTag, err := names.ParseUnitTag(unitTagString)
   191  	if err != nil {
   192  		return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm
   193  	}
   194  
   195  	return serviceTag, unitTag, nil
   196  }