-
Notifications
You must be signed in to change notification settings - Fork 501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: drives model config into mongo from dqlite. #18042
base: main
Are you sure you want to change the base?
Conversation
cd487aa
to
d5174c7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of questions to start with.
@@ -60,6 +61,24 @@ type EnvironProvider interface { | |||
PrepareConfig(context.Context, PrepareConfigParams) (*config.Config, error) | |||
} | |||
|
|||
// ModelConfigProvider represents an interface that a [EnvironProvider] can | |||
// implement to provide opinions and defaults into a models config. | |||
type ModelConfigProvider interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have ConfigSchemaSource
which the providers all implement (nothing else implements it, just the providers).
I suspect the intent was to replace this interface? Why keep both; we should look to consolidate.
// ConfigSchemaSource instances provide information on config attributes
// and the default attribute values.
type ConfigSchemaSource interface {
// ConfigSchema returns extra config attributes specific
// to this provider only.
ConfigSchema() schema.Fields
// ConfigDefaults returns the default values for the
// provider specific config attributes.
ConfigDefaults() schema.Defaults
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the intent to replace this long term. It is used in lots of places and I don't think we are ready to pull it. I could try and do it in this PR but it will be a lot of noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we at least add a TODO or 3 to the affected places and ensure we follow up very soon (not long term) with a PR to do the fixing. This type of tech debt is not something we'd want to carry. The changes should essentially be boiler plate given were just adding a method to a new duplicate interface and all the implementors of the original interface also implement the new method.
@@ -82,6 +82,14 @@ func (p azureEnvironProvider) ConfigDefaults() schema.Defaults { | |||
return configDefaults | |||
} | |||
|
|||
// ModelConfigDefaults provides a set of default model config attributes that | |||
// should be set on a models config if they have not been specified by the user. | |||
func (prov *azureEnvironProvider) ModelConfigDefaults(_ context.Context) (map[string]any, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this here, is there a reason why the corresponding code in PrepareConfig
isn't removed?
There's probably an argument that PrepareConfig could be designed out now. It existed primarily to add in provider specific config to the model, which is essentially what this PR does. It also does validation of the cloud spec but I would argue that's a conflation of responsibilities.
// PrepareConfig prepares the configuration for a new model, based on
// the provided arguments. PrepareConfig is expected to produce a
// deterministic output. Any unique values should be based on the
// "uuid" attribute of the base configuration. This is called for the
// controller model during bootstrap, and also for new hosted models.
PrepareConfig(context.Context, PrepareConfigParams) (*config.Config, error)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct in every respect above. I want to remove it but I am trying to avoid busy work as this change is a little hard to pull out because of the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a TODO on the method declaration in the interface. At the least, we should follow up immediately with a PR to remove the unnecessary config manipulation, even if the cloud spec validation remains for now.
How does this work in multi-cloud controllers? Could the different providers be in conflict? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Model config has the storage value for lxd, however I expected the model defaults to have it from the start and it did not. What is the expectation here?
storage-default-filesystem-source
is not available from model-defaults unless set first by the user.
$ juju model-defaults storage-default-filesystem-source
ERROR there are no default model values for "storage-default-filesystem-source"
$ juju add-model six
Added 'six' model on localhost/localhost with credential 'localhost' for user 'admin'
To use "juju ssh", "juju scp" and "juju debug-hooks" ssh public keys need to be added to the model with "juju add-ssh-key"
$ juju model-config storage-default-filesystem-source
lxd
$ juju model-defaults storage-default-filesystem-source=lxd
$ juju model-defaults storage-default-filesystem-source
Attribute Default Controller
storage-default-filesystem-source - lxd
// model config provider exists for the supplied cloud type then a | ||
// [coreerrors.NotFound] error is returned. If the cloud type provider does not | ||
// support model config then a [coreerrors.NotSupported] error is returned. | ||
type ModelConfigProviderFunc func(string) (environs.ModelConfigProvider, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Why use a string here rather than an actual type for a cloud? Same for the ModelCloudType return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have a dedicated type to represent this. We use strings everywhere. I agree it would be nice but I am avoiding the side quests at the moment.
} | ||
|
||
// ProviderModelConfigGetter returns a [ModelConfigProviderFunc] for | ||
// retrieving provider based model config values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for ModelConfig or DefaultModelConfig?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both.
// that | ||
// should be set on a models config if they have not been specified by the | ||
// user. | ||
ModelConfigDefaults(context.Context) (map[string]any, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why provide a context which is never used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to future proof. Defaults could be calculated in a number of ways include http calls to public api's (possible for ec2) to gather information. Trying to get ahead of the curve now and wire it up.
No we only ever consider the provider that is set for the model. We can only have one cloud per model. Conflicts can't happen. |
I would consider this a bug. We have never considered the defaults of the provider in the model defaults command. If you run the same test on 3.6 it has the same result. Possible we could fix this in the model defaults work on the model manager soon. |
This commit adds a new interface type to the environ package to describe providers that want to have opinions about a models configuration. I am introducing this change to place together all of the disparate funcs we currently have that mix concerns. This will make it easier to inject into the model defaults domain.
Add missing model config defaults func to the azure provider.
Adds missing model config defaults func to the dummy provider.
This commit adds support get getting provider based model config defaults in the DQlite domain services. Previously this was only happening for config attributes that the provider is extending model config with and not defaults the provider wanted to apply for controller wide attributes. The most common case of a provider wanting to do this is for storage. As well in this PR we are know longer letting the mongo based model config creation process take place. Instead we are using the DQlite domain services to perform the full generation and copying this result down into Mongo while we dual write. Because of this I have been able to remove the controller/modelmanager package that is no longer needed.
Have removed some agent version test by commenting them out. This will need to be address in future updates when moving models fully over to dqlite. Removed validation checks for model config in state layer as they are not needed anymore.
06d73c0
to
f21f9ce
Compare
Yes, this is current behaviour - model defaults does not have the concept of default values for provider specific config. As seen, when a model is added, and the user doesn't specify anything for a provider config value, the provider has the option to insert a value at that point. We'll need to think carefully about how this could be implemented if we decide to do it, especially with regard to things like how the Source is reported, since now we're introducing potentially what's considered as a new source option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can land this since it's great progress, on the proviso that the 2 items of tech debt are addressed short term. Some tech debt we can live with, but here it's a fairly significant dilution of the architecture which we'd not want to carry.
} | ||
} | ||
|
||
return rval, nil | ||
} | ||
|
||
// ModelDefaults will return the default config values to be used for a model | ||
// and it's config. If no model for uuid is found then a error satisfying |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we removed the unnecessary apostrophe as a drive by
// change are layered in the form of a funnel where we apply the most granular | ||
// specific last. The current order is: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"the most granular specific last" doesn't really parse to me.
Perhaps tweak the wording a bit.
"github.com/juju/juju/environs/config" | ||
interrors "github.com/juju/juju/internal/errors" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been trying to avoid importing both juju errors and the new errors package - my personal preference is to keep things consistent in a given file. Make the changes completely or not at all. Or land new work keeping juju errors and separately in a refactoring PR make the change. As a side effect, would avoid the "interrors" horror.
err := tx.Query(ctx, stmt, modelUUIDVal).Get(&result) | ||
if errors.Is(err, sqlair.ErrNoRows) { | ||
return interrors.Errorf( | ||
"cannot get cloud type for model %q because model does not exist", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My strong preference is to not double up on the top level "what is this method doing" messaging in every single error. It's sufficient here to just surface the issue with this specific operation, ie "model doesn't exist". And below, the error would be "cannot get cloud type". Then, instead of just "return err" below when calling db.Txn(), you'd include the extra context at that point. The end result is the same but with much less verbosity and message duplication.
return nil, fmt.Errorf("getting cloud type of model %q cloud: %w", uuid, err) | ||
} | ||
|
||
provider, err := environs.Provider(cloudType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, how the fark this this ever get landed in state. Glad to see it gone. State should never import environs.
@@ -60,6 +61,24 @@ type EnvironProvider interface { | |||
PrepareConfig(context.Context, PrepareConfigParams) (*config.Config, error) | |||
} | |||
|
|||
// ModelConfigProvider represents an interface that a [EnvironProvider] can | |||
// implement to provide opinions and defaults into a models config. | |||
type ModelConfigProvider interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we at least add a TODO or 3 to the affected places and ensure we follow up very soon (not long term) with a PR to do the fixing. This type of tech debt is not something we'd want to carry. The changes should essentially be boiler plate given were just adding a method to a new duplicate interface and all the implementors of the original interface also implement the new method.
@@ -82,6 +82,14 @@ func (p azureEnvironProvider) ConfigDefaults() schema.Defaults { | |||
return configDefaults | |||
} | |||
|
|||
// ModelConfigDefaults provides a set of default model config attributes that | |||
// should be set on a models config if they have not been specified by the user. | |||
func (prov *azureEnvironProvider) ModelConfigDefaults(_ context.Context) (map[string]any, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a TODO on the method declaration in the interface. At the least, we should follow up immediately with a PR to remove the unnecessary config manipulation, even if the cloud spec validation remains for now.
This PR introduces provider based model config defaults into the model default service. Previously when calculating a model's default values for config we only considered config attributes owned by the provider and not defaults the provider wished to have opinions on for keys that are owned by the entire controller.
This has now been migrated into the ModelDefaults service through a new environs interface. The new interface was introduced so we did not have to keep using prepare config that would also require a cloud spec for validation.
Because of the above changes it is now possible to remove the Mongo code that has opinions about setting up model config on a newly created model and allow the domain services to drive the process end to end.
Checklist
[] Integration tests, with comments saying what you're testing[ ] doc.go added or updated in changed packagesQA steps
storage-default-filesystem-source
is set tolxd
.Documentation changes
N/A
Links
Jira card: JUJU-6422