github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/constraints"
    10  	"github.com/juju/juju/environs/config"
    11  	"github.com/juju/juju/instance"
    12  	"github.com/juju/juju/state/cloudimagemetadata"
    13  	"github.com/juju/juju/storage"
    14  )
    15  
    16  // NewPolicyFunc is the type of a function that,
    17  // given a *State, returns a Policy for that State.
    18  type NewPolicyFunc func(*State) Policy
    19  
    20  // Policy is an interface provided to State that may
    21  // be consulted by State to validate or modify the
    22  // behaviour of certain operations.
    23  //
    24  // If a Policy implementation does not implement one
    25  // of the methods, it must return an error that
    26  // satisfies errors.IsNotImplemented, and will thus
    27  // be ignored. Any other error will cause an error
    28  // in the use of the policy.
    29  type Policy interface {
    30  	// Prechecker returns a Prechecker or an error.
    31  	Prechecker() (Prechecker, error)
    32  
    33  	// ProviderConfigSchemaSource returns a config.ConfigSchemaSource
    34  	// for the environ provider, or an error.
    35  	ProviderConfigSchemaSource() (config.ConfigSchemaSource, error)
    36  
    37  	// ConfigValidator returns a config.Validator or an error.
    38  	ConfigValidator() (config.Validator, error)
    39  
    40  	// ConstraintsValidator returns a constraints.Validator or an error.
    41  	ConstraintsValidator() (constraints.Validator, error)
    42  
    43  	// InstanceDistributor returns an instance.Distributor or an error.
    44  	InstanceDistributor() (instance.Distributor, error)
    45  
    46  	// StorageProviderRegistry returns a storage.ProviderRegistry or an error.
    47  	StorageProviderRegistry() (storage.ProviderRegistry, error)
    48  }
    49  
    50  // Prechecker is a policy interface that is provided to State
    51  // to perform pre-flight checking of instance creation.
    52  type Prechecker interface {
    53  	// PrecheckInstance performs a preflight check on the specified
    54  	// series and constraints, ensuring that they are possibly valid for
    55  	// creating an instance in this model.
    56  	//
    57  	// PrecheckInstance is best effort, and not guaranteed to eliminate
    58  	// all invalid parameters. If PrecheckInstance returns nil, it is not
    59  	// guaranteed that the constraints are valid; if a non-nil error is
    60  	// returned, then the constraints are definitely invalid.
    61  	PrecheckInstance(series string, cons constraints.Value, placement string) error
    62  }
    63  
    64  // precheckInstance calls the state's assigned policy, if non-nil, to obtain
    65  // a Prechecker, and calls PrecheckInstance if a non-nil Prechecker is returned.
    66  func (st *State) precheckInstance(series string, cons constraints.Value, placement string) error {
    67  	if st.policy == nil {
    68  		return nil
    69  	}
    70  	prechecker, err := st.policy.Prechecker()
    71  	if errors.IsNotImplemented(err) {
    72  		return nil
    73  	} else if err != nil {
    74  		return err
    75  	}
    76  	if prechecker == nil {
    77  		return errors.New("policy returned nil prechecker without an error")
    78  	}
    79  	return prechecker.PrecheckInstance(series, cons, placement)
    80  }
    81  
    82  func (st *State) constraintsValidator() (constraints.Validator, error) {
    83  	// Default behaviour is to simply use a standard validator with
    84  	// no model specific behaviour built in.
    85  	var validator constraints.Validator
    86  	if st.policy != nil {
    87  		var err error
    88  		validator, err = st.policy.ConstraintsValidator()
    89  		if errors.IsNotImplemented(err) {
    90  			validator = constraints.NewValidator()
    91  		} else if err != nil {
    92  			return nil, err
    93  		} else if validator == nil {
    94  			return nil, errors.New("policy returned nil constraints validator without an error")
    95  		}
    96  	} else {
    97  		validator = constraints.NewValidator()
    98  	}
    99  
   100  	// Add supported architectures gleaned from cloud image
   101  	// metadata to the validator's vocabulary.
   102  	model, err := st.Model()
   103  	if err != nil {
   104  		return nil, errors.Annotate(err, "getting model")
   105  	}
   106  	if region := model.CloudRegion(); region != "" {
   107  		cfg, err := st.ModelConfig()
   108  		if err != nil {
   109  			return nil, errors.Trace(err)
   110  		}
   111  		arches, err := st.CloudImageMetadataStorage.SupportedArchitectures(
   112  			cloudimagemetadata.MetadataFilter{
   113  				Stream: cfg.AgentStream(),
   114  				Region: region,
   115  			},
   116  		)
   117  		if err != nil {
   118  			return nil, errors.Annotate(err, "querying supported architectures")
   119  		}
   120  		if len(arches) != 0 {
   121  			validator.UpdateVocabulary(constraints.Arch, arches)
   122  		}
   123  	}
   124  	return validator, nil
   125  }
   126  
   127  // resolveConstraints combines the given constraints with the environ constraints to get
   128  // a constraints which will be used to create a new instance.
   129  func (st *State) resolveConstraints(cons constraints.Value) (constraints.Value, error) {
   130  	validator, err := st.constraintsValidator()
   131  	if err != nil {
   132  		return constraints.Value{}, err
   133  	}
   134  	envCons, err := st.ModelConstraints()
   135  	if err != nil {
   136  		return constraints.Value{}, err
   137  	}
   138  	return validator.Merge(envCons, cons)
   139  }
   140  
   141  // validateConstraints returns an error if the given constraints are not valid for the
   142  // current model, and also any unsupported attributes.
   143  func (st *State) validateConstraints(cons constraints.Value) ([]string, error) {
   144  	validator, err := st.constraintsValidator()
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return validator.Validate(cons)
   149  }
   150  
   151  // validate calls the state's assigned policy, if non-nil, to obtain
   152  // a config.Validator, and calls Validate if a non-nil config.Validator is
   153  // returned.
   154  func (st *State) validate(cfg, old *config.Config) (valid *config.Config, err error) {
   155  	if st.policy == nil {
   156  		return cfg, nil
   157  	}
   158  	configValidator, err := st.policy.ConfigValidator()
   159  	if errors.IsNotImplemented(err) {
   160  		return cfg, nil
   161  	} else if err != nil {
   162  		return nil, err
   163  	}
   164  	if configValidator == nil {
   165  		return nil, errors.New("policy returned nil configValidator without an error")
   166  	}
   167  	return configValidator.Validate(cfg, old)
   168  }
   169  
   170  func (st *State) storageProviderRegistry() (storage.ProviderRegistry, error) {
   171  	if st.policy == nil {
   172  		return storage.StaticProviderRegistry{}, nil
   173  	}
   174  	return st.policy.StorageProviderRegistry()
   175  }
   176  
   177  func (st *State) environsProviderConfigSchemaSource() (config.ConfigSchemaSource, error) {
   178  	if st.policy == nil {
   179  		return nil, errors.NotImplementedf("config.ProviderConfigSchemaSource")
   180  	}
   181  	return st.policy.ProviderConfigSchemaSource()
   182  }