github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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/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  	// MinLeaseRequest is the shortest duration for which we will accept
    26  	// a leadership claim.
    27  	MinLeaseRequest = 5 * time.Second
    28  
    29  	// MaxLeaseRequest is the longest duration for which we will accept
    30  	// a leadership claim.
    31  	MaxLeaseRequest = 5 * time.Minute
    32  )
    33  
    34  var (
    35  	logger = loggo.GetLogger("juju.apiserver.leadership")
    36  )
    37  
    38  func init() {
    39  	common.RegisterStandardFacade(
    40  		FacadeName,
    41  		1,
    42  		NewLeadershipServiceFacade,
    43  	)
    44  }
    45  
    46  // NewLeadershipServiceFacade constructs a new LeadershipService and presents
    47  // a signature that can be used with RegisterStandardFacade.
    48  func NewLeadershipServiceFacade(
    49  	state *state.State, resources *common.Resources, authorizer common.Authorizer,
    50  ) (LeadershipService, error) {
    51  	return NewLeadershipService(state.LeadershipClaimer(), authorizer)
    52  }
    53  
    54  // NewLeadershipService constructs a new LeadershipService.
    55  func NewLeadershipService(
    56  	claimer leadership.Claimer, authorizer common.Authorizer,
    57  ) (LeadershipService, error) {
    58  
    59  	if !authorizer.AuthUnitAgent() {
    60  		return nil, errors.Unauthorizedf("permission denied")
    61  	}
    62  
    63  	return &leadershipService{
    64  		claimer:    claimer,
    65  		authorizer: authorizer,
    66  	}, nil
    67  }
    68  
    69  // leadershipService implements the LeadershipService interface and
    70  // is the concrete implementation of the API endpoint.
    71  type leadershipService struct {
    72  	claimer    leadership.Claimer
    73  	authorizer common.Authorizer
    74  }
    75  
    76  // ClaimLeadership is part of the LeadershipService interface.
    77  func (m *leadershipService) ClaimLeadership(args params.ClaimLeadershipBulkParams) (params.ClaimLeadershipBulkResults, error) {
    78  
    79  	results := make([]params.ErrorResult, len(args.Params))
    80  	for pIdx, p := range args.Params {
    81  
    82  		result := &results[pIdx]
    83  		serviceTag, unitTag, err := parseServiceAndUnitTags(p.ServiceTag, p.UnitTag)
    84  		if err != nil {
    85  			result.Error = common.ServerError(err)
    86  			continue
    87  		}
    88  		duration := time.Duration(p.DurationSeconds * float64(time.Second))
    89  		if duration > MaxLeaseRequest || duration < MinLeaseRequest {
    90  			result.Error = common.ServerError(errors.New("invalid duration"))
    91  			continue
    92  		}
    93  
    94  		// In the future, situations may arise wherein units will make
    95  		// leadership claims for other units. For now, units can only
    96  		// claim leadership for themselves, for their own service.
    97  		if !m.authorizer.AuthOwner(unitTag) || !m.authMember(serviceTag) {
    98  			result.Error = common.ServerError(common.ErrPerm)
    99  			continue
   100  		}
   101  
   102  		err = m.claimer.ClaimLeadership(serviceTag.Id(), unitTag.Id(), duration)
   103  		if err != nil {
   104  			result.Error = common.ServerError(err)
   105  		}
   106  	}
   107  
   108  	return params.ClaimLeadershipBulkResults{results}, nil
   109  }
   110  
   111  // BlockUntilLeadershipReleased implements the LeadershipService interface.
   112  func (m *leadershipService) BlockUntilLeadershipReleased(serviceTag names.ServiceTag) (params.ErrorResult, error) {
   113  	if !m.authMember(serviceTag) {
   114  		return params.ErrorResult{Error: common.ServerError(common.ErrPerm)}, nil
   115  	}
   116  
   117  	if err := m.claimer.BlockUntilLeadershipReleased(serviceTag.Id()); err != nil {
   118  		return params.ErrorResult{Error: common.ServerError(err)}, nil
   119  	}
   120  	return params.ErrorResult{}, nil
   121  }
   122  
   123  func (m *leadershipService) authMember(serviceTag names.ServiceTag) bool {
   124  	ownerTag := m.authorizer.GetAuthTag()
   125  	unitTag, ok := ownerTag.(names.UnitTag)
   126  	if !ok {
   127  		return false
   128  	}
   129  	unitId := unitTag.Id()
   130  	requireServiceId, err := names.UnitService(unitId)
   131  	if err != nil {
   132  		return false
   133  	}
   134  	return serviceTag.Id() == requireServiceId
   135  }
   136  
   137  // parseServiceAndUnitTags takes in string representations of service
   138  // and unit tags and returns their corresponding tags.
   139  func parseServiceAndUnitTags(
   140  	serviceTagString, unitTagString string,
   141  ) (
   142  	names.ServiceTag, names.UnitTag, error,
   143  ) {
   144  	// TODO(fwereade) 2015-02-25 bug #1425506
   145  	// These permissions errors are not appropriate -- there's no permission or
   146  	// security issue in play here, because our tag format is public, and the
   147  	// error only triggers when the strings fail to match that format.
   148  	serviceTag, err := names.ParseServiceTag(serviceTagString)
   149  	if err != nil {
   150  		return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm
   151  	}
   152  
   153  	unitTag, err := names.ParseUnitTag(unitTagString)
   154  	if err != nil {
   155  		return names.ServiceTag{}, names.UnitTag{}, common.ErrPerm
   156  	}
   157  
   158  	return serviceTag, unitTag, nil
   159  }