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