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