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 }