-
Notifications
You must be signed in to change notification settings - Fork 259
[OCPCLOUD-1109] External cloud volume plugin observer for KCM #994
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
[OCPCLOUD-1109] External cloud volume plugin observer for KCM #994
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: Danil-Grigorev The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
…server This allows to have only storage in-tree functionality enabled in KCM.
| ret = configobserver.Pruned(ret, c.cloudProviderConfigPath, c.cloudProviderNamePath) | ||
| }() | ||
|
|
||
| listers := genericListers.(InfrastructureLister) |
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.
What happens if this assertion fails?
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 should not with proper use. The consumer of this method will be a part of the list here, this argument is populated internally. It is done similarly in other places.
| // NewCloudVolumePluginObserver returns a new cloudprovider observer for syncing cloud provider specific | ||
| // information to controller-manager, leaving only storage controller loops enabled. | ||
| func NewCloudVolumePluginObserver(targetNamespaceName string, cloudVolumePluginPath, cloudProviderConfigPath []string) configobserver.ObserveConfigFunc { | ||
| cloudObserver := &cloudProviderObserver{ |
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.
Why are we reusing the cloudProviderObserver, would it not be better to have a dedicated observer for this CloudVolumePluginObserver?
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'm reusing the tested part of that code. The behavior and argument types are very similar in both NewCloudVolumePluginObserver and NewCloudProviderObserver, and which method will be used is decided on this line https://github.com/openshift/library-go/pull/994/files#diff-e571c7ed0d7124cda88d781ed909db675ef38ce8c54b4cab07b1f3e0283a519bR22 This is more error-prone for something which will stay in a cluster for a couple of releases.
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.
How many repos will use this function? Also, a separate package seems proper here.
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.
so, i'm a little confused. is it better to make a new copy here or am i misinterpreting the code?
| // Set cloudProviderConfig value | ||
| existingCloudConfig, _, err := unstructured.NestedStringSlice(existingConfig, c.cloudProviderConfigPath...) | ||
| if err != nil { | ||
| errs = append(errs, err) | ||
| // keep going on read error from existing config | ||
| } | ||
|
|
||
| cloudProviderConfig, configErrs := c.getCloudProviderConfig(genericListers, recorder, infrastructure) | ||
| if configErrs != nil { | ||
| errs = append(errs, configErrs...) | ||
| } |
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.
How is the cloud provider config handled currently? Surely this is handled in MCO somehow already? How does this tie in when the cloud provider is external?
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.
Here is how it is set in KCMO. This method will co-exist on the same list of observers, and will be placed on the next line. Those two methods logic is exclusive, either NewCloudVolumePluginObserver sets the cloud-provider flag (well --external-cloud-volume-plugin) or NewCloudProviderObserver does it. MCO handles it differently, we don't use it there.
| cloudProvider := getPlatformName(infrastructure.Status.Platform, recorder) | ||
| if len(cloudProvider) > 0 { | ||
| if err := unstructured.SetNestedStringSlice(observedConfig, []string{cloudProvider}, volumePluginPath...); err != nil { | ||
| errs = append(errs, err) |
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.
if we have an error here, why don't we return the previous config, since the previous was correct at some point in time and there is a high likelihood here that we'l be returning something partial or worse?
| recorder.Eventf("ObserveCloudVolumePlugin", "Invalid featuregate.%s/cluster format: %v", err) | ||
| } else if !external { | ||
| // Running in-tree volumes - no configuration needed. Skip | ||
| return previouslyObservedConfig, errs |
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.
returning the previous config isn't a skip, it's the previous. If you want to return no args, then return an empty config
|
|
||
| external, err := cloudprovider.IsCloudProviderExternal(infrastructure.Status.Platform, featureGate) | ||
| if err != nil { | ||
| recorder.Eventf("ObserveCloudVolumePlugin", "Invalid featuregate.%s/cluster format: %v", err) |
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.
why wouldn't we return the previous config when hitting this condition?
|
|
||
| featureGate, err := listers.FeatureGateLister().Get("cluster") | ||
| if errors.IsNotFound(err) { | ||
| recorder.Eventf("ObserveCloudVolumePlugin", "Optional featuregate.%s/cluster not found", configv1.GroupName) |
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.
why wouldn't we return the previous config when hitting this condition?
|
@Danil-Grigorev Didn't we decide we can't go ahead without CSI migration? Therefore we won't be need this? |
| // Set cloudProviderConfig value | ||
| existingCloudConfig, _, err := unstructured.NestedStringSlice(existingConfig, c.cloudProviderConfigPath...) | ||
| if err != nil { | ||
| errs = append(errs, err) |
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.
looks like you should return the previous config.
|
|
||
| // NewCloudVolumePluginObserver returns a new cloudprovider observer for syncing cloud provider specific | ||
| // information to controller-manager, leaving only storage controller loops enabled. | ||
| func NewCloudVolumePluginObserver(targetNamespaceName string, cloudVolumePluginPath, cloudProviderConfigPath []string) configobserver.ObserveConfigFunc { |
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.
There may be a misunderstanding of what happens when you return an error. When you return an error, your returned configuration is still used. This is because you're one of a dozen config loops and we cannot halt all configuration on a single observation failure. This means that you must return the best possible config you can. Usually, this is the previous configuration value you have.
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.
if returning a configuration that is basically the same as the previous, is it best practice to make a copy and return the copy?
elmiko
left a comment
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.
this is mostly making sense to me, i am curious about some of the open questions though.
|
|
||
| // NewCloudVolumePluginObserver returns a new cloudprovider observer for syncing cloud provider specific | ||
| // information to controller-manager, leaving only storage controller loops enabled. | ||
| func NewCloudVolumePluginObserver(targetNamespaceName string, cloudVolumePluginPath, cloudProviderConfigPath []string) configobserver.ObserveConfigFunc { |
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.
if returning a configuration that is basically the same as the previous, is it best practice to make a copy and return the copy?
| // NewCloudVolumePluginObserver returns a new cloudprovider observer for syncing cloud provider specific | ||
| // information to controller-manager, leaving only storage controller loops enabled. | ||
| func NewCloudVolumePluginObserver(targetNamespaceName string, cloudVolumePluginPath, cloudProviderConfigPath []string) configobserver.ObserveConfigFunc { | ||
| cloudObserver := &cloudProviderObserver{ |
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.
so, i'm a little confused. is it better to make a new copy here or am i misinterpreting the code?
|
I'm closing this PR as recent CI runs confirmed that PV handling will be broken on kubelet side (volume will becomes unmountable). Kubelet is missing an equivalent of cc @jsafrane @JoelSpeed @elmiko @lobziik Thanks for review @deads2k /close |
This implementation allows to preserve in-tree storage related cloud interaction code for external cloud providers. Will be used in openshift/cluster-kube-controller-manager-operator#450
More details in https://github.com/openshift/enhancements/pull/463/files#diff-3e0e2c48e70215076dfe36c13768a823ab7080d929d80292f37db2ef5a2121e8R191-R197