github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/singular/singular.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package singular
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/facade"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/lease"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  // NewExternalFacade is for API registration.
    21  func NewExternalFacade(context facade.Context) (*Facade, error) {
    22  	st := context.State()
    23  	auth := context.Auth()
    24  
    25  	m, err := st.Model()
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  
    30  	claimer, err := context.SingularClaimer()
    31  	if err != nil {
    32  		return nil, errors.Trace(err)
    33  	}
    34  
    35  	backend := getBackend(st, m.ModelTag())
    36  	return NewFacade(backend, claimer, auth)
    37  }
    38  
    39  var getBackend = func(st *state.State, modelTag names.ModelTag) Backend {
    40  	return &stateBackend{st, modelTag}
    41  }
    42  
    43  type stateBackend struct {
    44  	*state.State
    45  	modelTag names.ModelTag
    46  }
    47  
    48  // ModelTag is part of the Backend interface.
    49  func (b *stateBackend) ModelTag() names.ModelTag {
    50  	return b.modelTag
    51  }
    52  
    53  // Backend supplies capabilities required by a Facade.
    54  type Backend interface {
    55  	// ControllerTag tells the Facade which controller it should consider
    56  	// requests for.
    57  	ControllerTag() names.ControllerTag
    58  
    59  	// ModelTag tells the Facade what models it should consider requests for.
    60  	ModelTag() names.ModelTag
    61  }
    62  
    63  // NewFacade returns a singular-controller API facade, backed by the supplied
    64  // state, so long as the authorizer represents a controller machine.
    65  func NewFacade(backend Backend, claimer lease.Claimer, auth facade.Authorizer) (*Facade, error) {
    66  	if !auth.AuthController() {
    67  		return nil, common.ErrPerm
    68  	}
    69  	return &Facade{
    70  		auth:            auth,
    71  		modelTag:        backend.ModelTag(),
    72  		controllerTag:   backend.ControllerTag(),
    73  		singularClaimer: claimer,
    74  	}, nil
    75  }
    76  
    77  // Facade allows controller machines to request exclusive rights to administer
    78  // some specific model or controller for a limited time.
    79  type Facade struct {
    80  	auth            facade.Authorizer
    81  	controllerTag   names.ControllerTag
    82  	modelTag        names.ModelTag
    83  	singularClaimer lease.Claimer
    84  }
    85  
    86  // Wait waits for the singular-controller lease to expire for all supplied
    87  // entities. (In practice, any requests that do not refer to the connection's
    88  // model or controller will be rejected.)
    89  func (facade *Facade) Wait(ctx context.Context, args params.Entities) (result params.ErrorResults) {
    90  	result.Results = make([]params.ErrorResult, len(args.Entities))
    91  	for i, entity := range args.Entities {
    92  		leaseId, err := facade.tagLeaseId(entity.Tag)
    93  		if err != nil {
    94  			result.Results[i].Error = common.ServerError(err)
    95  			continue
    96  		}
    97  		// TODO(axw) 2017-10-30 #1728594
    98  		// We should be waiting for the leases in parallel,
    99  		// so the waits do not affect one another.
   100  		err = facade.singularClaimer.WaitUntilExpired(leaseId, ctx.Done())
   101  		result.Results[i].Error = common.ServerError(err)
   102  	}
   103  	return result
   104  }
   105  
   106  // Claim makes the supplied singular-controller lease requests. (In practice,
   107  // any requests not for the connection's model or controller, or not on behalf
   108  // of the connected ModelManager machine, will be rejected.)
   109  func (facade *Facade) Claim(args params.SingularClaims) (result params.ErrorResults) {
   110  	result.Results = make([]params.ErrorResult, len(args.Claims))
   111  	for i, claim := range args.Claims {
   112  		err := facade.claim(claim)
   113  		result.Results[i].Error = common.ServerError(err)
   114  	}
   115  	return result
   116  }
   117  
   118  func (facade *Facade) claim(claim params.SingularClaim) error {
   119  	if !allowedDuration(claim.Duration) {
   120  		return common.ErrPerm
   121  	}
   122  	leaseId, err := facade.tagLeaseId(claim.EntityTag)
   123  	if err != nil {
   124  		return errors.Trace(err)
   125  	}
   126  	holder := facade.auth.GetAuthTag().String()
   127  	if claim.ClaimantTag != holder {
   128  		return common.ErrPerm
   129  	}
   130  	return facade.singularClaimer.Claim(leaseId, holder, claim.Duration)
   131  }
   132  
   133  func (facade *Facade) tagLeaseId(tagString string) (string, error) {
   134  	tag, err := names.ParseTag(tagString)
   135  	if err != nil {
   136  		return "", errors.Trace(err)
   137  	}
   138  	switch tag {
   139  	case facade.modelTag, facade.controllerTag:
   140  		return tag.Id(), nil
   141  	}
   142  	return "", common.ErrPerm
   143  }
   144  
   145  // allowedDuration returns true if the supplied duration is at least one second,
   146  // and no more than one minute. (We expect to refine the lease-length times, but
   147  // these seem like reasonable bounds.)
   148  func allowedDuration(duration time.Duration) bool {
   149  	if duration < time.Second {
   150  		return false
   151  	}
   152  	return duration <= time.Minute
   153  }