-
Notifications
You must be signed in to change notification settings - Fork 47
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
feat: Add VM delete protection #1199
Conversation
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 did you create a separate controllers instead of adding these new resources into an operand? We usually add new resources in operands. That way, they can be configured in the SSP resource.
Can you rename this PR and the commit? This is definitely not a chore
.
Maybe something like:
feat: Add VM delete protection
}, | ||
Validations: []admissionregistrationv1.Validation{ | ||
{ | ||
Expression: `(!has(oldObject.metadata.labels) || !(variables.label in oldObject.metadata.labels) || !oldObject.metadata.labels[variables.label].matches('^(true|True)$'))`, |
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.
Would it be enough to test that the label exists, instead of checking its 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.
I've considered clearer from the user point of view. My idea is: by forcing the user to add the "True" value, we make sure that user really wants the protection enabled, and I think it will avoid confusions. That being said, I'm not against it if we find it out better.
/hold |
It is my understanding that operands are better fit when you are using CRD than controllers. In this case, we are just using a build-in feature such as VAP and VAPB. Given that, I'm not particularly against adding it though an operand. In fact, I question this myself multiple times. As we consider best.
You are absolutely right. Thanks. |
57c3cc2
to
e4f96d2
Compare
I'm not sure what do you mean by "operands are better fit when you are using CRD than controllers". Can you explain it more? The SSP controller deploys various resources based on what is configured in the SSP CR. VAP and VAPB are one of these resources. Currently we don't configure them, but maybe we can. Operands are an abstraction used to group related resources together in the code, so it is easier to understand. For example we deploy multiple ClusterRole objects, and it would be harder to understand if they were all in one package. I would say that this is the exact use case to add a new operand. |
All right! You convinced me! Thanks. Let's create an operand. |
e4f96d2
to
03ff41f
Compare
v2:
|
Please also add the new operand to the |
03ff41f
to
e4d02a8
Compare
Added. |
e4d02a8
to
2c8aa38
Compare
/unhold |
2c8aa38
to
4ff65b3
Compare
Can you also remove unneeded text from the PR's description? |
4ff65b3
to
886d79d
Compare
Adjusted. |
e7a1d76
to
c2d3f89
Compare
c2d3f89
to
69bbfa5
Compare
69bbfa5
to
5c60ead
Compare
c9dcddc
to
1c2b9e7
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.
/approve
Thanks
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: 0xFelix The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
}). | ||
StatusFunc(func(resource client.Object) common.ResourceStatus { | ||
vap := resource.(*admissionregistrationv1.ValidatingAdmissionPolicy) | ||
if vap.Status.TypeChecking != nil && len(vap.Status.TypeChecking.ExpressionWarnings) != 0 { |
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 vap.Status.TypeChecking == nil
, it means that it is still in progress, so we should not consider it a success.
This is written as a comment in the api:
ssp-operator/vendor/k8s.io/api/admissionregistration/v1/types.go
Lines 162 to 165 in d03ad50
// The results of type checking for each expression. | |
// Presence of this field indicates the completion of the type checking. | |
// +optional | |
TypeChecking *TypeChecking `json:"typeChecking,omitempty" protobuf:"bytes,2,opt,name=typeChecking"` |
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.
Really nice catch! Added. PTAL.
It("should create a valid CEL expression", func() { | ||
celEnv, err := cel.NewEnv() | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
_, issues := celEnv.Parse(vmDeleteProtectionCELExpression) | ||
Expect(issues.Err()).ToNot(HaveOccurred()) | ||
}) |
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.
Please change this to a call to operand.Reconcile(&request)
, and then check the syntax on the VAP object that was created. That way we are sure it tests the correct thing even if the private constant changes.
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.
Adjusted.
tests/vm_delete_protection_test.go
Outdated
|
||
AfterEach(func() { | ||
err := apiClient.Get(ctx, client.ObjectKeyFromObject(vm), vm) | ||
Expect(err).To(Or(Not(HaveOccurred()), MatchError(errors.IsNotFound, "VM not found"))) |
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.
Please use MatchError(errors.IsNotFound, "errors.IsNotFound")
, so that the error message will be:
Expected ... to match error function errors.IsNotFound
Instead of
Expected ... to match error function VM not found
This is where the second parameter is used:
return format.Message(actual, fmt.Sprintf("to match error function %s", matcher.FuncErrDescription[0])) |
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.
Ah, I see. Thanks. Adjusted.
tests/vm_delete_protection_test.go
Outdated
vm = createVMWithDeleteProtection(labelValue) | ||
|
||
Expect(apiClient.Delete(ctx, vm)).To(Succeed()) | ||
waitForDeletion(client.ObjectKeyFromObject(vm), &kubevirtv1.VirtualMachine{}) |
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.
No need to waste time waiting for deletion, because the above function would fail, if VAP blocked the deletion.
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.
Dropped
tests/vm_delete_protection_test.go
Outdated
vm = createVMWithLabels(nil) | ||
|
||
Expect(apiClient.Delete(ctx, vm)).To(Succeed()) | ||
waitForDeletion(client.ObjectKeyFromObject(vm), &kubevirtv1.VirtualMachine{}) |
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.
same as above
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.
Dropped
tests/vm_delete_protection_test.go
Outdated
}) | ||
|
||
AfterEach(func() { | ||
err := apiClient.Get(ctx, client.ObjectKeyFromObject(vm), vm) |
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 vm == nil
, this whole logic should be skipped
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.
Added.
tests/vm_delete_protection_test.go
Outdated
}, env.ShortTimeout(), time.Second).Should(Succeed()) | ||
|
||
Expect(apiClient.Delete(ctx, vm)).To(Succeed()) | ||
waitForDeletion(client.ObjectKeyFromObject(vm), &kubevirtv1.VirtualMachine{}) |
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.
set the vm = nil
after the VM is deleted, so that we don't potentially reuse it in a test, if the test forgets to create a new VM.
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.
Added.
1c2b9e7
to
7271722
Compare
vap := resource.(*admissionregistrationv1.ValidatingAdmissionPolicy) | ||
if vap.Status.TypeChecking == nil { | ||
return common.ResourceStatus{ | ||
Progressing: ptr.To("Delete protection VAP type checking in progress"), |
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.
Also the other fields need to be set. If the object is progressing, it means that it is not yet available and it is degraded.
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.
Done.
return common.ResourceStatus{ | ||
Progressing: ptr.To("Delete protection VAP type checking in progress"), | ||
} | ||
} else if vap.Status.TypeChecking != nil && len(vap.Status.TypeChecking.ExpressionWarnings) != 0 { |
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 above block returns, so else
and vap.Status.TypeChecking != nil
are not needed.
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.
Done.
It adds the ability to protect VirtualMachine objects from being deleted. If the label `kubevirt.io/vm-delete-protection` is set to `True`, any attempt to delete the VM will be rejected by a VAP policy. Signed-off-by: Javier Cano Cano <[email protected]>
7271722
to
0ab684c
Compare
|
/lgtm |
/retest-required |
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 late as a reviewer due to sick leave. I think it looks good and can be merged, just a few nits about naming.
/lgtm
} | ||
} | ||
|
||
type VMDeleteProtection struct{} |
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.
nit: I'd prefer to name it as VirtualMachineDeleteProtection
.
AppComponentTemplating AppComponent = "templating" | ||
AppComponentTektonPipelines AppComponent = "tektonPipelines" | ||
AppComponentTektonTasks AppComponent = "tektonTasks" | ||
AppComponentVMDeletionProtection AppComponent = "vmDeleteProtection" |
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.
nit: I'd suggest to stick to AppComponentVmDeleteProtection
instead of AppComponentVMDeletionProtection
.
Reconcile() | ||
} | ||
|
||
func reconcileVAPB(request *common.Request) (common.ReconcileResult, 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.
nit: I'd name it as reconcileVAPBinding
and also the variables below then should be foundVapBinding
and expectedVapBinding
.
/test e2e-upgrade-functests |
@codingben: No presubmit jobs available for kubevirt/ssp-operator@main In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
@codingben Sorry, I didn't have the time to address your comments before it got merged. I will in a follow-up. |
No need to sorry because it was only nits and I came late to the review cycle. |
Hey @jcanocan, Too bad I wasn't aware of this PR before it merged. I find it concerning that it was merged without addressing the concerns that were raised up there and that no one of the reviewers were pinged to look at this PR. Am I missing something? Were the concerns addressed in any way? |
What this PR does / why we need it:
VirtualMachine objects are often managed by automation, CLI commands, 3rd party tools, etc. These automations may result in deleting accidentally VMs that should have not been deleted. These deletions may lead to a service degradation or out of service. Moreover, the deleted VMs may lead to information loss if the underlining PVC is deleted as a result of a cascaded delete.
It adds the ability to protect VirtualMachine objects from being deleted. If the label
kubevirt.io/vm-delete-protection
is set toTrue
, any attempt to delete the VM will be rejected by a VAP policy.This protection enables a protection against non-intended VM deletions, providing security and confidence to users.
Which issue(s) this PR fixes:
Fixes # CNV-50741
Release note: