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