Skip to content

Conversation

@Danil-Grigorev
Copy link

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

@openshift-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Danil-Grigorev
To complete the pull request process, please assign sttts after the PR has been reviewed.
You can assign the PR to them by writing /assign @sttts in a comment when ready.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Danil-Grigorev added 2 commits February 17, 2021 13:07
ret = configobserver.Pruned(ret, c.cloudProviderConfigPath, c.cloudProviderNamePath)
}()

listers := genericListers.(InfrastructureLister)
Copy link
Contributor

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?

Copy link
Author

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{
Copy link
Contributor

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?

Copy link
Author

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.

Copy link
Contributor

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.

Copy link

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?

Comment on lines +78 to +88
// 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...)
}
Copy link
Contributor

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?

Copy link
Author

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)
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

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?

@JoelSpeed
Copy link
Contributor

@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)
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link

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?

Copy link

@elmiko elmiko left a 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 {
Copy link

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{
Copy link

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?

@Danil-Grigorev
Copy link
Author

Danil-Grigorev commented Apr 30, 2021

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 --external-cloud-volume-plugin flag, so this breakage is finite. The proposal will be updated with the procedure of simultaneous migration to CSI and out-of-tree cloud providers.

cc @jsafrane @JoelSpeed @elmiko @lobziik

Thanks for review @deads2k

/close

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants