1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
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
"github.com/juju/juju/storage"
18
// NewPolicyFunc is the type of a function that,
19
// given a *State, returns a Policy for that State.
20
type NewPolicyFunc func(*State) Policy
22
// Policy is an interface provided to State that may
23
// be consulted by State to validate or modify the
24
// behaviour of certain operations.
26
// If a Policy implementation does not implement one
27
// of the methods, it must return an error that
28
// satisfies errors.IsNotImplemented, and will thus
29
// be ignored. Any other error will cause an error
30
// in the use of the policy.
31
type Policy interface {
32
// Prechecker returns a Prechecker or an error.
33
Prechecker() (Prechecker, error)
35
// ConfigValidator returns a config.Validator or an error.
36
ConfigValidator() (config.Validator, error)
38
// ConstraintsValidator returns a constraints.Validator or an error.
39
ConstraintsValidator() (constraints.Validator, error)
41
// InstanceDistributor returns an instance.Distributor or an error.
42
InstanceDistributor() (instance.Distributor, error)
44
// StorageProviderRegistry returns a storage.ProviderRegistry or an error.
45
StorageProviderRegistry() (storage.ProviderRegistry, error)
48
// Prechecker is a policy interface that is provided to State
49
// to perform pre-flight checking of instance creation.
50
type Prechecker interface {
51
// PrecheckInstance performs a preflight check on the specified
52
// series and constraints, ensuring that they are possibly valid for
53
// creating an instance in this model.
55
// PrecheckInstance is best effort, and not guaranteed to eliminate
56
// all invalid parameters. If PrecheckInstance returns nil, it is not
57
// guaranteed that the constraints are valid; if a non-nil error is
58
// returned, then the constraints are definitely invalid.
59
PrecheckInstance(series string, cons constraints.Value, placement string) error
62
// precheckInstance calls the state's assigned policy, if non-nil, to obtain
63
// a Prechecker, and calls PrecheckInstance if a non-nil Prechecker is returned.
64
func (st *State) precheckInstance(series string, cons constraints.Value, placement string) error {
68
prechecker, err := st.policy.Prechecker()
69
if errors.IsNotImplemented(err) {
71
} else if err != nil {
74
if prechecker == nil {
75
return fmt.Errorf("policy returned nil prechecker without an error")
77
return prechecker.PrecheckInstance(series, cons, placement)
80
func (st *State) constraintsValidator() (constraints.Validator, error) {
81
// Default behaviour is to simply use a standard validator with
82
// no model specific behaviour built in.
83
var validator constraints.Validator
86
validator, err = st.policy.ConstraintsValidator()
87
if errors.IsNotImplemented(err) {
88
validator = constraints.NewValidator()
89
} else if err != nil {
91
} else if validator == nil {
92
return nil, fmt.Errorf("policy returned nil constraints validator without an error")
95
validator = constraints.NewValidator()
98
// Add supported architectures gleaned from cloud image
99
// metadata to the validator's vocabulary.
100
model, err := st.Model()
102
return nil, errors.Annotate(err, "getting model")
104
if region := model.CloudRegion(); region != "" {
105
cfg, err := st.ModelConfig()
107
return nil, errors.Trace(err)
109
arches, err := st.CloudImageMetadataStorage.SupportedArchitectures(
110
cloudimagemetadata.MetadataFilter{
111
Stream: cfg.AgentStream(),
116
return nil, errors.Annotate(err, "querying supported architectures")
118
if len(arches) != 0 {
119
validator.UpdateVocabulary(constraints.Arch, arches)
122
return validator, nil
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()
130
return constraints.Value{}, err
132
envCons, err := st.ModelConstraints()
134
return constraints.Value{}, err
136
return validator.Merge(envCons, cons)
139
// validateConstraints returns an error if the given constraints are not valid for the
140
// current model, and also any unsupported attributes.
141
func (st *State) validateConstraints(cons constraints.Value) ([]string, error) {
142
validator, err := st.constraintsValidator()
146
return validator.Validate(cons)
149
// validate calls the state's assigned policy, if non-nil, to obtain
150
// a config.Validator, and calls Validate if a non-nil config.Validator is
152
func (st *State) validate(cfg, old *config.Config) (valid *config.Config, err error) {
153
if st.policy == nil {
156
configValidator, err := st.policy.ConfigValidator()
157
if errors.IsNotImplemented(err) {
159
} else if err != nil {
162
if configValidator == nil {
163
return nil, fmt.Errorf("policy returned nil configValidator without an error")
165
return configValidator.Validate(cfg, old)
168
func (st *State) storageProviderRegistry() (storage.ProviderRegistry, error) {
169
if st.policy == nil {
170
return storage.StaticProviderRegistry{}, nil
172
return st.policy.StorageProviderRegistry()