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  }