Skip to content
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

webhook rules for sandbox shutdown #971

Merged
merged 46 commits into from
Nov 8, 2024
Merged

webhook rules for sandbox shutdown #971

merged 46 commits into from
Nov 8, 2024

Conversation

LiboYu2
Copy link
Collaborator

@LiboYu2 LiboYu2 commented Oct 28, 2024

Here are the rules that are implemented by this change.

  • When creating a vdb, all subclusters and sandboxes(if any) must not have shutdown set to true.
  • If a sandbox has shutdown(spec) true and any of its subclusters has the annotation "vertica.com/shutdown-driven-by-sandbox" set to true in the old vdb, we should prevent the user from updating that subcluster's shutdown field.
  • You cannot unsandbox a subcluster with shutdown true(spec and/or status) or part of a sandbox with shutdown true
  • You cannot change spec.sandboxes[].image if the sandbox has shutdown true or has any subcluster with shutdown true(spec and/or status)
  • You cannot terminate a sandbox if it has has shutdown true. terminate means removing the sandbox in spec.sandboxes[] and its subclusters in spec.subclusters[] at the same time.

Comment on lines 1897 to 1917
func (v *VerticaDB) checkSboxForShutdownInSpecAndStatus(sandbox *Sandbox, subclusterMap map[string]*Subcluster, subclusterIndexMap map[string]int, errMsgs []string) []string {
errMsgs = v.checkSboxForShutdownInSpec(sandbox, subclusterMap, errMsgs)
for _, subcluster := range sandbox.Subclusters {
if v.Status.Subclusters[subclusterIndexMap[subcluster.Name]].Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown status of subcluster %q is true", subcluster.Name))
}
}
return errMsgs
}

func (v *VerticaDB) checkSboxForShutdownInSpec(sandbox *Sandbox, subclusterMap map[string]*Subcluster, errMsgs []string) []string {
if sandbox.Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown field of sandbox %q is set to true", sandbox.Name))
}
for _, subcluster := range sandbox.Subclusters {
if subclusterMap[subcluster.Name].Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown field of subcluster %q is set to true", subcluster.Name))
}
}
return errMsgs
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will create too many msgs. Let's have a single loop over sandbox.Subclusters and for each subcluster find the shutdown value in both spec and status(make sure the subcluster exists in status). If any of them is true return a generate a generic msg like subcluster %q is marked for shut down or has already been shut down

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"make sure the subcluster exists in status"
I got your point. When a user tries to change the sandbox image, the subcluster is already shutdown at this time. This is different from my original understanding. I will make a change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the JIRA:
"You cannot change spec.sandboxes[].image if the sandbox has shutdown true or has any subcluster with shutdown true(spec and/or status)"

Could you remove the "or" from within the brackets? Thanks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I mean what I said. If you prefer here is what I mean You cannot change spec.sandboxes[].image if the sandbox has shutdown true or has any subcluster with shutdown true(spec or status)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. It is different from your original comment: "for each subcluster find the shutdown value in both spec and status(make sure the subcluster exists in status)."

@@ -1701,6 +1760,162 @@ func (v *VerticaDB) checkSandboxPrimary(allErrs field.ErrorList, oldObj *Vertica
return allErrs
}

// validateUnsandboxShutdownConditions ensures we will not unsandbox a subcluster that has shutdown set to true, or a subcluster
// in a sandbox where the shutdown field for the sandbox is set to true
func (v *VerticaDB) validateUnsandboxShutdownConditions(old runtime.Object, allErrs field.ErrorList) field.ErrorList {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split the content of this function into smaller functions to make it more readable. Unsandboxing means 2 things: 1. we removed one or multiple subclusters from sandbox or we remove one or multiple sandboxes from spec.sandboxes.
In both cases we need to identify subclusters to be unsandboxed and for each for them, check if the subcluster shutdown(spec/status) is true or if it is/was part of a sandbox that has shutdown true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"check if the subcluster shutdown(spec/status)"
for the condition shutdown(spec/status), is it an OR or AND? Currently I implemented it as an OR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an OR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add one more limitation: we cannot add one or multiple subclusters to a sandbox if the sandbox is shutdown or partially shutdown. I didn't test it, but I think we cannot restart a sandboxed subcluster if that sandbox changed its group members.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposed new rule can be found in another webhook JIRA VER-97254

Comment on lines 109 to 110
allErrs = v.hasNoShutdownSubclusters(allErrs)
allErrs = v.hasNoShutdownSandboxes(allErrs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the 1st rule to this: when a subcluster or sandbox is added to the vdb for the 1st time (not in status yet) they all must have shutdown field set to false.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the original 1st rule is still a valid one. The two cover different scenarios. Should we add what you proposed as a new rule?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we allow shutdown in db creation?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this new rule covers the original first rule. When you create a vdb, the subclusters and/or sandboxes in the spec will not be in the status.

if newSandbox.Shutdown {
for _, subclusterName := range oldSandbox.Subclusters {
oldSubcluster := oldSubclusterMap[subclusterName.Name]
if drivenby, ok := oldSubcluster.Annotations["vertica.com/shutdown-driven-by-sandbox"]; ok && drivenby == trueString {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a constant in annotations.go. A function to get the annotation's value too.

Ω(newVdb.validateAnnotatedSubclustersInShutdownSandbox(oldVdb, field.ErrorList{})).Should(HaveLen(1))
newVdb.Spec.Subclusters[1].Shutdown = true
Ω(newVdb.validateAnnotatedSubclustersInShutdownSandbox(oldVdb, field.ErrorList{})).Should(HaveLen(0))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

newVdb.Spec.Subclusters[1].Shutdown = false
Ω(newVdb.validateAnnotatedSubclustersInShutdownSandbox(oldVdb, field.ErrorList{})).Should(HaveLen(1))
newVdb.Spec.Subclusters[1].Shutdown = true
Ω(newVdb.validateAnnotatedSubclustersInShutdownSandbox(oldVdb, field.ErrorList{})).Should(HaveLen(0))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need the following cases:

  • change sc1 shutdown and check there is no error as it is not part of the sandbox
  • set sandbox.shutdown to false, rerun the above cases and check there is no error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new cases added

@@ -1701,6 +1760,162 @@ func (v *VerticaDB) checkSandboxPrimary(allErrs field.ErrorList, oldObj *Vertica
return allErrs
}

// validateUnsandboxShutdownConditions ensures we will not unsandbox a subcluster that has shutdown set to true, or a subcluster
// in a sandbox where the shutdown field for the sandbox is set to true
func (v *VerticaDB) validateUnsandboxShutdownConditions(old runtime.Object, allErrs field.ErrorList) field.ErrorList {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an OR.

Comment on lines 1897 to 1917
func (v *VerticaDB) checkSboxForShutdownInSpecAndStatus(sandbox *Sandbox, subclusterMap map[string]*Subcluster, subclusterIndexMap map[string]int, errMsgs []string) []string {
errMsgs = v.checkSboxForShutdownInSpec(sandbox, subclusterMap, errMsgs)
for _, subcluster := range sandbox.Subclusters {
if v.Status.Subclusters[subclusterIndexMap[subcluster.Name]].Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown status of subcluster %q is true", subcluster.Name))
}
}
return errMsgs
}

func (v *VerticaDB) checkSboxForShutdownInSpec(sandbox *Sandbox, subclusterMap map[string]*Subcluster, errMsgs []string) []string {
if sandbox.Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown field of sandbox %q is set to true", sandbox.Name))
}
for _, subcluster := range sandbox.Subclusters {
if subclusterMap[subcluster.Name].Shutdown {
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown field of subcluster %q is set to true", subcluster.Name))
}
}
return errMsgs
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I mean what I said. If you prefer here is what I mean You cannot change spec.sandboxes[].image if the sandbox has shutdown true or has any subcluster with shutdown true(spec or status)

Comment on lines 109 to 110
allErrs = v.hasNoShutdownSubclusters(allErrs)
allErrs = v.hasNoShutdownSandboxes(allErrs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this new rule covers the original first rule. When you create a vdb, the subclusters and/or sandboxes in the spec will not be in the status.

Copy link
Collaborator

@roypaulin roypaulin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are getting close.

api/v1/verticadb_webhook.go Outdated Show resolved Hide resolved
// checkSboxForShutdown checks if sbox or its scluster has shutdown field set to true in spec/status
func (v *VerticaDB) checkSboxForShutdown(newSandbox *Sandbox, newSClusterMap map[string]*Subcluster, statusSClusterIndexMap map[string]int) string {
if newSandbox.Shutdown {
return fmt.Sprintf("Shutdown field of sandbox %q is set to true", newSandbox.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Sprintf("Shutdown field of sandbox %q is set to true", newSandbox.Name)
errMsgs = append(errMsgs, fmt.Sprintf("Shutdown field of sandbox %q is set to true", newSandbox.Name))

Copy link
Collaborator Author

@LiboYu2 LiboYu2 Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will make the change.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it can happen that the sandbox has shutdown true and also all its subclusters have shutdown true. In that case we want to report all the error msgs so the user is aware.

for _, subcluster := range newSandbox.Subclusters {
i, foundInStatus := statusSClusterIndexMap[subcluster.Name]
if foundInStatus && (newSClusterMap[subcluster.Name].Shutdown || v.Status.Subclusters[i].Shutdown) {
return fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name)
errMsgs = append(errMsgs, fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as the above one.

return fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name)
}
}
return ""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return ""
return errMsgs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as the above one. When it returns empty string, it means no error.

}

// checkSboxForShutdown checks if sbox or its scluster has shutdown field set to true in spec/status
func (v *VerticaDB) checkSboxForShutdown(newSandbox *Sandbox, newSClusterMap map[string]*Subcluster, statusSClusterIndexMap map[string]int) string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (v *VerticaDB) checkSboxForShutdown(newSandbox *Sandbox, newSClusterMap map[string]*Subcluster, statusSClusterIndexMap map[string]int) string {
func (v *VerticaDB) checkSboxForShutdown(newSandbox *Sandbox, newSClusterMap map[string]*Subcluster, statusSClusterIndexMap map[string]int) []string {
errMsgs := []string{}

We still want to return all the error msgs. We just did not want to return one batch for spec and another for status.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I know what you want to achieve. I withdraw my previous comments and will make the change.

Comment on lines 1796 to 1807
allErrs = append(allErrs, err)
continue
}
if oldSandbox.Shutdown {
p := field.NewPath("spec").Child("sandboxes")
err := field.Invalid(p,
oldSubcluster.Name,
fmt.Sprintf("cannot unsandbox subcluster %q in sandbox %q that has Shutdown field set to true",
oldSubcluster.Name, oldSandbox.Name))
allErrs = append(allErrs, err)
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
allErrs = append(allErrs, err)
continue
}
if oldSandbox.Shutdown {
p := field.NewPath("spec").Child("sandboxes")
err := field.Invalid(p,
oldSubcluster.Name,
fmt.Sprintf("cannot unsandbox subcluster %q in sandbox %q that has Shutdown field set to true",
oldSubcluster.Name, oldSandbox.Name))
allErrs = append(allErrs, err)
continue
}
allErrs = append(allErrs, err)
}
if oldSandbox.Shutdown {
p := field.NewPath("spec").Child("sandboxes").Index(...)
err := field.Invalid(p.Child("shutdown"),
oldSandbox.Name,
fmt.Sprintf("cannot unsandbox subcluster %q in sandbox %q that has shutdown field set to true",
oldSubcluster.Name, oldSandbox.Name))
allErrs = append(allErrs, err)
}

Comment on lines 1833 to 1834
err := field.Invalid(p.Index(index),
subclusterName.Name,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err := field.Invalid(p.Index(index),
subclusterName.Name,
err := field.Invalid(p.Index(index).Child("shutdown"),
newSubcluster.Shutdown,

p := field.NewPath("spec").Child("subclusters")
err := field.Invalid(p.Index(index),
subclusterName.Name,
fmt.Sprintf("cannot change Shutdown field for subcluster %q that has annotation \"vertica.com/shutdown-driven-by-sandbox\" set to true",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fmt.Sprintf("cannot change Shutdown field for subcluster %q that has annotation \"vertica.com/shutdown-driven-by-sandbox\" set to true",
fmt.Sprintf("cannot change shutdown field for subcluster %q whose shutdown is driven by sandbox %q",

newSBox := v.Spec.Sandboxes[i]
if _, foundInStatus := statusSBoxMap[newSBox.Name]; !foundInStatus && newSBox.Shutdown {
p := field.NewPath("spec").Child("sandboxes").Index(i)
err := field.Invalid(p,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err := field.Invalid(p,
err := field.Invalid(p.Child("shutdown"),

newSCluster := &v.Spec.Subclusters[i]
if _, foundInStatus := statusSClusterMap[newSCluster.Name]; !foundInStatus && newSCluster.Shutdown {
p := field.NewPath("spec").Child("subclusters").Index(i)
err := field.Invalid(p,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err := field.Invalid(p,
err := field.Invalid(p.child("shutdown"),

Copy link
Collaborator

@roypaulin roypaulin 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 the last round. After my comments are addressed, I will approve

if newSandbox.Shutdown {
return fmt.Sprintf("Shutdown field of sandbox %q is set to true", newSandbox.Name)
return append(errMsgs, fmt.Sprintf("Shutdown field of sandbox %q is set to true", newSandbox.Name))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not return.

}

for _, subcluster := range newSandbox.Subclusters {
i, foundInStatus := statusSClusterIndexMap[subcluster.Name]
if foundInStatus && (newSClusterMap[subcluster.Name].Shutdown || v.Status.Subclusters[i].Shutdown) {
return fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name)
return append(errMsgs, fmt.Sprintf("Subcluster %q is marked for shut down or has already been shut down", subcluster.Name))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not return.
Also to make the error msg shorter, we can first append the subcluster names and then add the msg. The err msg will look like: ......Subclusters sc1, sc2, sc3 are marked for shut down or have already been shut down.

}
// sandboxToBeRemoved and all its subclusters are not found in new spec
if sclustersToTerminate == numOfSclustersInSbox {
if oldSandboxMap[sandboxToBeRemoved].Shutdown {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be at the beginning of the loop so you can skip to the next sandbox if the current sandbox has shutdown true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement compares the number of subclusters to be removed with the number of subclusters in the sandbox.
If they equal, that means the sandbox will be terminated.
There is another for loop on the top of this if statement. It is used to calculate "sclustersToTerminate" used in the if statement.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, if oldSandboxMap[sandboxToBeRemoved].Shutdown is false then there is no point in running all this code. Check oldSandboxMap[sandboxToBeRemoved].Shutdown at the beginning of the loop and if false skip and go to next sandbox. Something like:

for _, sandboxToBeRemoved := range sandboxesToBeRemoved {
       if !oldSandboxMap[sandboxToBeRemoved].Shutdown {
              continue
       }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point. I will check oldSandboxMap[sandboxToBeRemoved].Shutdown earlier. If it is false, let it skip the rest of the current loop.

Comment on lines 153 to 157
allErrs = v.validateShutdownSandboxImage(old, allErrs)
allErrs = v.validateTerminatingSandboxes(old, allErrs)
allErrs = v.validateUnsandboxShutdownConditions(old, allErrs)
allErrs = v.validateAnnotatedSubclustersInShutdownSandbox(old, allErrs)
allErrs = v.validateNewSBoxOrSClusterShutdownUnset(allErrs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the same function structure and call these check... like checkShutdownSandboxImage

@@ -1701,6 +1728,199 @@ func (v *VerticaDB) checkSandboxPrimary(allErrs field.ErrorList, oldObj *Vertica
return allErrs
}

// ensures a new subcluster or sandbox to be added to a vdb has Shutdown field set to false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ensures a new subcluster or sandbox to be added to a vdb has Shutdown field set to false
// validateNewSBoxOrSClusterShutdownUnset ensures a new subcluster or sandbox to be added to a vdb has Shutdown field set to false

Follow the same forma for other functions

return statusSboxMap
}

// GenStatusSandboxMap() returns a map from status. The key is subcluster name and value is the subcluster pointer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: update GenStatusSandboxMap to GenStatusSubclusterMap

func (v *VerticaDB) findPersistSandboxes(oldObj *VerticaDB) []string {
oldSandboxes := oldObj.Spec.Sandboxes
newSandboxes := v.Spec.Sandboxes
oldSandboxMap := make(map[string]int)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: this map should be a map[string]any or map[string]bool.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be addressed.

return allErrs
}

// findSclustersToUnsandboxreturn a map whose key is the pointer of a subcluster that is to be unsandboxed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add space in front of "return"

newSubclusterMap := v.GenSubclusterMap()
sclusterSboxMap := make(map[*Subcluster]*Sandbox)
for oldSubclusterName, oldSandboxName := range oldSubclusterInSandbox {
_, oldSubclusterInNewSandboxes := newSuclusterInSandbox[oldSubclusterName]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to check if the old sandbox name and new sandbox name are the same. If we move a subcluster from a sandbox to another in new vdb, that subcluster is still in new vdb, but it will be unsandboxed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make code change to cover that scenario and set up a new test case.

sandboxIndexMap := v.GenSandboxIndexMap()
for oldSubcluster, oldSandbox := range subclustersToUnsandbox {
oldSubclusterIndex, found := statusSClusterIndexMap[oldSubcluster.Name]
if oldSubcluster.Shutdown || found && v.Status.Subclusters[oldSubclusterIndex].Shutdown {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parenthesis for readability.

allErrs = append(allErrs, err)
continue
}
if oldSandbox.Shutdown {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For one sandbox, we probably reach here multiple times so we probably return duplicate errors.

newSandbox := newSandboxMap[sandboxName]
oldSandbox := oldSandboxMap[sandboxName]
if newSandbox.Shutdown {
for _, subclusterName := range oldSandbox.Subclusters {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we checking old sandbox's subclusters? Doesn't this rule apply on the subclusters in new sandbox?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the JIRA: " If a sandbox has shutdown(spec) true and any of its subclusters has the annotation "vertica.com/shutdown-driven-by-sandbox" set to true in the old vdb, we should prevent the user from updating that subcluster's shutdown field. "
Here we check the subclusters that exist in both new and old vdbs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then in line 1836, we should check oldSandbox.Shutdown.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we should check newSandbox. Here is the scenario this covers: sandbox sand1 has a subcluster sc1. you set sand1.shutdown to true, the operator will set sc1.shutdown to true and add the annotation to sc1. So now sand.shutdown = true, sc1.shutdown. true and sc1 has "vertica.com/shutdown-driven-by-sandbox". We do not want a user to come and change sc1.shutdown because that annotation is a proof that the sandbox drives the shutdown.

This is relevant only if sand1.shutdown is still true(newvdb)

return allErrs
}

// checkSboxForShutdown checks if sbox or its scluster has shutdown field set to true in spec/status
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enrich the comment to explain the returned value.

func (v *VerticaDB) findPersistSandboxes(oldObj *VerticaDB) []string {
oldSandboxes := oldObj.Spec.Sandboxes
newSandboxes := v.Spec.Sandboxes
oldSandboxMap := make(map[string]int)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be addressed.

return sclusterSboxMap
}

// checkUnsandboxShutdownConditions ensures we will not unsandbox a subcluster that has shutdown set to true, or a subcluster
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix golint error of the comments here.

}
if oldSandbox.Shutdown {
i := sandboxIndexMap[oldSandbox.Name]
if i != unsandboxIndex { // this is to avoid duplicate error messages
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this fix the duplicate error messages bug? In the for loop, we have this sequence: sc1, sand1; sc2, sand2; sc3, sand1. unsandboxIndex will be 1(assume sand1's index is 1), 2, 1. We still come here twice for sand1.

You should check the sandbox outside this loop or use a map to store checked sandboxes.

_, oldSubclusterInPersist := newSubclusterMap[oldSubclusterName]
oldSandbox := oldSandboxMap[oldSandboxName]
// for unsandboxing, check shutdown field of the subcluster and sandbox
if oldSubclusterInPersist {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition doesn't help. Even the subcluster does not persist, the subcluster could be unsandboxed.

newSandbox := newSandboxMap[sandboxName]
oldSandbox := oldSandboxMap[sandboxName]
if newSandbox.Shutdown {
for _, subclusterName := range oldSandbox.Subclusters {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then in line 1836, we should check oldSandbox.Shutdown.

return allErrs
}

// checkTerminatingSandboxes ensure the sandbox to terminate is not shut down
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't match the function implementation. I don't think we need to check if all subclusters inside the sandbox are terminating. If a "shutdown" sandbox is removed, we should error out.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I overthought this. @LiboYu2 when a sandbox is removed check its shutdown and if it is true, block the change. We should also do the same for remove_subcluster, a subcluster must not be removed from vdb if shutdown is true, but that will be part of his other ticket. This has already taken too long.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will change the code to adopt the new logic. Could you update the rule in the JIRA as well?

Comment on lines 153 to 157
allErrs = v.checkShutdownSandboxImage(old, allErrs)
allErrs = v.checkTerminatingSandboxes(old, allErrs)
allErrs = v.checkUnsandboxShutdownConditions(old, allErrs)
allErrs = v.checkAnnotatedSubclustersInShutdownSandbox(old, allErrs)
allErrs = v.checkNewSBoxOrSClusterShutdownUnset(allErrs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly pass oldObj to these functions to stay consistent.

@@ -1828,20 +1828,18 @@ func (v *VerticaDB) checkAnnotatedSubclustersInShutdownSandbox(oldObj *VerticaDB
for sandboxName := range persistSandboxes {
newSandbox := newSandboxMap[sandboxName]
oldSandbox := oldSandboxMap[sandboxName]
if newSandbox.Shutdown {
if newSandbox.Shutdown { // !oldSandbox.Shutdown &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this comment? I do not think oldSandbox.shutdown value matters.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is useless. I will remove it.

allErrs = append(allErrs, err)
}
newSubcluster, oldSclusterPersist := newSubclusterMap[subclusterName.Name]
if oldSclusterPersist && oldSubcluster.Shutdown != newSubcluster.Shutdown && !newSubcluster.Shutdown {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need the last part of this condition? We want to prevent any change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this one "!newSubcluster.Shutdown"? It is used to check if a user sets the shutdown to false while the sandbox is marked for shutdown. It is needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to prevent any kind of change so "oldSubcluster.Shutdown != newSubcluster.Shutdown" is enough

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we only have "oldSubcluster.Shutdown != newSubcluster.Shutdown", that will stop the operator from the setting the shutdown field to true from false.

@@ -1833,20 +1828,18 @@ func (v *VerticaDB) checkAnnotatedSubclustersInShutdownSandbox(old runtime.Objec
for sandboxName := range persistSandboxes {
newSandbox := newSandboxMap[sandboxName]
oldSandbox := oldSandboxMap[sandboxName]
if newSandbox.Shutdown {
if newSandbox.Shutdown { // !oldSandbox.Shutdown &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the comment in this line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@@ -1822,8 +1818,7 @@ func (v *VerticaDB) checkUnsandboxShutdownConditions(old runtime.Object, allErrs

// checkAnnotatedSubclustersInShutdownSandbox ensures: when a sandbox is to be shutdown, if a subcluster in it has annotation
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the comment here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

@@ -1833,20 +1828,18 @@ func (v *VerticaDB) checkAnnotatedSubclustersInShutdownSandbox(old runtime.Objec
for sandboxName := range persistSandboxes {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the sandbox to be persistent?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this condition "oldSubcluster.Shutdown != newSubcluster.Shutdown"
The rule applies when a user tries to mess with an existing subcluster. If it is a new subcluster, it will be covered by other rules.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That condition is for persistent subclusters, not for persistent sandboxes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the sandbox is not persistent, it will be a new sandbox, For new sandboxes to be created, its shutdown field will not be true. We are only concerned about subclusters in persistent sandboxes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a subcluster that isn't in a sandbox of old vdb, but we put it in the sandbox of new vdb, your code cannot cover that subcluster, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a rule to prevent a "shutdown" subcluster to add to a sandbox in your PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct myself. Rule 1 of https://jira.verticacorp.com/jira/browse/VER-97368 covers that scenario.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a subcluster that isn't in a sandbox of old vdb, but we put it in the sandbox of new vdb, your code cannot cover that subcluster, right?

Right. That is the sandboxing scenario. It will be covered in https://jira.verticacorp.com/jira/browse/VER-97254

}
// sandboxToBeRemoved and all its subclusters are not found in new spec
if sclustersToTerminate == numOfSclustersInSbox {
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the else here since you already used a "continue" in line 1884

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the "else", it is easier to read and understand the logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then line 1884 doesn't make sense. You can just use if oldSandboxMap[sandboxToBeRemoved].Shutdown, what's the point to use an else block.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove it.

@LiboYu2 LiboYu2 merged commit 265f01a into main Nov 8, 2024
39 checks passed
@LiboYu2 LiboYu2 deleted the VER-97368-webhook-rules branch November 8, 2024 20:15
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.

3 participants