github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/policy.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  
    11  	"github.com/juju/juju/constraints"
    12  	"github.com/juju/juju/environs/config"
    13  	"github.com/juju/juju/instance"
    14  )
    15  
    16  // Policy is an interface provided to State that may
    17  // be consulted by State to validate or modify the
    18  // behaviour of certain operations.
    19  //
    20  // If a Policy implementation does not implement one
    21  // of the methods, it must return an error that
    22  // satisfies errors.IsNotImplemented, and will thus
    23  // be ignored. Any other error will cause an error
    24  // in the use of the policy.
    25  type Policy interface {
    26  	// Prechecker takes a *config.Config and returns a Prechecker or an error.
    27  	Prechecker(*config.Config) (Prechecker, error)
    28  
    29  	// ConfigValidator takes a provider type name and returns a ConfigValidator
    30  	// or an error.
    31  	ConfigValidator(providerType string) (ConfigValidator, error)
    32  
    33  	// EnvironCapability takes a *config.Config and returns an EnvironCapability
    34  	// or an error.
    35  	EnvironCapability(*config.Config) (EnvironCapability, error)
    36  
    37  	// ConstraintsValidator takes a *config.Config and returns a
    38  	// constraints.Validator or an error.
    39  	ConstraintsValidator(*config.Config) (constraints.Validator, error)
    40  
    41  	// InstanceDistributor takes a *config.Config and returns an
    42  	// InstanceDistributor or an error.
    43  	InstanceDistributor(*config.Config) (InstanceDistributor, error)
    44  }
    45  
    46  // Prechecker is a policy interface that is provided to State
    47  // to perform pre-flight checking of instance creation.
    48  type Prechecker interface {
    49  	// PrecheckInstance performs a preflight check on the specified
    50  	// series and constraints, ensuring that they are possibly valid for
    51  	// creating an instance in this environment.
    52  	//
    53  	// PrecheckInstance is best effort, and not guaranteed to eliminate
    54  	// all invalid parameters. If PrecheckInstance returns nil, it is not
    55  	// guaranteed that the constraints are valid; if a non-nil error is
    56  	// returned, then the constraints are definitely invalid.
    57  	PrecheckInstance(series string, cons constraints.Value, placement string) error
    58  }
    59  
    60  // ConfigValidator is a policy interface that is provided to State
    61  // to check validity of new configuration attributes before applying them to state.
    62  type ConfigValidator interface {
    63  	Validate(cfg, old *config.Config) (valid *config.Config, err error)
    64  }
    65  
    66  // EnvironCapability implements access to metadata about the capabilities
    67  // of an environment.
    68  type EnvironCapability interface {
    69  	// SupportedArchitectures returns the image architectures which can
    70  	// be hosted by this environment.
    71  	SupportedArchitectures() ([]string, error)
    72  
    73  	// SupportsUnitAssignment returns an error which, if non-nil, indicates
    74  	// that the environment does not support unit placement. If the environment
    75  	// does not support unit placement, then machines may not be created
    76  	// without units, and units cannot be placed explcitly.
    77  	SupportsUnitPlacement() error
    78  }
    79  
    80  // precheckInstance calls the state's assigned policy, if non-nil, to obtain
    81  // a Prechecker, and calls PrecheckInstance if a non-nil Prechecker is returned.
    82  func (st *State) precheckInstance(series string, cons constraints.Value, placement string) error {
    83  	if st.policy == nil {
    84  		return nil
    85  	}
    86  	cfg, err := st.EnvironConfig()
    87  	if err != nil {
    88  		return err
    89  	}
    90  	prechecker, err := st.policy.Prechecker(cfg)
    91  	if errors.IsNotImplemented(err) {
    92  		return nil
    93  	} else if err != nil {
    94  		return err
    95  	}
    96  	if prechecker == nil {
    97  		return fmt.Errorf("policy returned nil prechecker without an error")
    98  	}
    99  	return prechecker.PrecheckInstance(series, cons, placement)
   100  }
   101  
   102  func (st *State) constraintsValidator() (constraints.Validator, error) {
   103  	// Default behaviour is to simply use a standard validator with
   104  	// no environment specific behaviour built in.
   105  	defaultValidator := constraints.NewValidator()
   106  	if st.policy == nil {
   107  		return defaultValidator, nil
   108  	}
   109  	cfg, err := st.EnvironConfig()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	validator, err := st.policy.ConstraintsValidator(cfg)
   114  	if errors.IsNotImplemented(err) {
   115  		return defaultValidator, nil
   116  	} else if err != nil {
   117  		return nil, err
   118  	}
   119  	if validator == nil {
   120  		return nil, fmt.Errorf("policy returned nil constraints validator without an error")
   121  	}
   122  	return validator, nil
   123  }
   124  
   125  // resolveConstraints combines the given constraints with the environ constraints to get
   126  // a constraints which will be used to create a new instance.
   127  func (st *State) resolveConstraints(cons constraints.Value) (constraints.Value, error) {
   128  	validator, err := st.constraintsValidator()
   129  	if err != nil {
   130  		return constraints.Value{}, err
   131  	}
   132  	envCons, err := st.EnvironConstraints()
   133  	if err != nil {
   134  		return constraints.Value{}, err
   135  	}
   136  	return validator.Merge(envCons, cons)
   137  }
   138  
   139  // validateConstraints returns an error if the given constraints are not valid for the
   140  // current environment, and also any unsupported attributes.
   141  func (st *State) validateConstraints(cons constraints.Value) ([]string, error) {
   142  	validator, err := st.constraintsValidator()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	return validator.Validate(cons)
   147  }
   148  
   149  // validate calls the state's assigned policy, if non-nil, to obtain
   150  // a ConfigValidator, and calls Validate if a non-nil ConfigValidator is
   151  // returned.
   152  func (st *State) validate(cfg, old *config.Config) (valid *config.Config, err error) {
   153  	if st.policy == nil {
   154  		return cfg, nil
   155  	}
   156  	configValidator, err := st.policy.ConfigValidator(cfg.Type())
   157  	if errors.IsNotImplemented(err) {
   158  		return cfg, nil
   159  	} else if err != nil {
   160  		return nil, err
   161  	}
   162  	if configValidator == nil {
   163  		return nil, fmt.Errorf("policy returned nil configValidator without an error")
   164  	}
   165  	return configValidator.Validate(cfg, old)
   166  }
   167  
   168  // supportsUnitPlacement calls the state's assigned policy, if non-nil,
   169  // to obtain an EnvironCapability, and calls SupportsUnitPlacement if a
   170  // non-nil EnvironCapability is returned.
   171  func (st *State) supportsUnitPlacement() error {
   172  	if st.policy == nil {
   173  		return nil
   174  	}
   175  	cfg, err := st.EnvironConfig()
   176  	if err != nil {
   177  		return errors.Trace(err)
   178  	}
   179  	capability, err := st.policy.EnvironCapability(cfg)
   180  	if errors.IsNotImplemented(err) {
   181  		return nil
   182  	} else if err != nil {
   183  		return errors.Trace(err)
   184  	}
   185  	if capability == nil {
   186  		return fmt.Errorf("policy returned nil EnvironCapability without an error")
   187  	}
   188  	return capability.SupportsUnitPlacement()
   189  }
   190  
   191  // InstanceDistributor is a policy interface that is provided
   192  // to State to perform distribution of units across instances
   193  // for high availability.
   194  type InstanceDistributor interface {
   195  	// DistributeInstance takes a set of clean, empty
   196  	// instances, and a distribution group, and returns
   197  	// the subset of candidates which the policy will
   198  	// allow entry into the distribution group.
   199  	//
   200  	// The AssignClean and AssignCleanEmpty unit
   201  	// assignment policies will attempt to assign a
   202  	// unit to each of the resulting instances until
   203  	// one is successful. If no instances can be assigned
   204  	// to (e.g. because of concurrent deployments), then
   205  	// a new machine will be allocated.
   206  	DistributeInstances(candidates, distributionGroup []instance.Id) ([]instance.Id, error)
   207  }