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