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