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 }