From bd8b47bd248def196d24d569e7ed5f40429da114 Mon Sep 17 00:00:00 2001 From: Nahshon Unna-Tsameret Date: Sun, 1 Sep 2024 17:43:09 +0300 Subject: [PATCH] Fix bug with user-defined labels in priorityClass HCO already supports user-defined labels in priorityClass, but if another change is done to the priorityClass, either a change in a required label, or a change in the spec fields, the user-defined labels are deleted. This is also happens if the other modofication is done in different request. This PR fixes this issue, and makes sure that user-defined labels are stay in place on update of the priorityClass. As part of the fix, HCO now does not remove and re-create the priorityClass, if only label were changed, but updates it. As result, the update_priority_class was removed as it is not relevant anymore. Signed-off-by: Nahshon Unna-Tsameret --- controllers/operands/kubevirt.go | 59 ++++- controllers/operands/kubevirt_test.go | 64 +++++- deploy/cluster_role.yaml | 1 + ...perator.v1.13.0.clusterserviceversion.yaml | 1 + ...perator.v1.13.0.clusterserviceversion.yaml | 3 +- go.mod | 3 +- go.sum | 12 ++ pkg/components/components.go | 2 +- tests/func-tests/dependency_objects_test.go | 2 + .../func-tests/update_priority_class_test.go | 89 -------- vendor/kubevirt.io/kubevirt/LICENSE | 202 ++++++++++++++++++ .../pkg/apimachinery/patch/BUILD.bazel | 22 ++ .../kubevirt/pkg/apimachinery/patch/patch.go | 164 ++++++++++++++ vendor/modules.txt | 5 +- 14 files changed, 525 insertions(+), 104 deletions(-) delete mode 100644 tests/func-tests/update_priority_class_test.go create mode 100644 vendor/kubevirt.io/kubevirt/LICENSE create mode 100644 vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/BUILD.bazel create mode 100644 vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/patch.go diff --git a/controllers/operands/kubevirt.go b/controllers/operands/kubevirt.go index c7aa3d5100..30a4d81e5b 100644 --- a/controllers/operands/kubevirt.go +++ b/controllers/operands/kubevirt.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "k8s.io/apimachinery/pkg/types" "maps" "os" "path" @@ -24,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" kubevirtcorev1 "kubevirt.io/api/core/v1" + "kubevirt.io/kubevirt/pkg/apimachinery/patch" hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1" "github.com/kubevirt/hyperconverged-cluster-operator/controllers/common" @@ -888,8 +890,8 @@ func (kvPriorityClassHooks) updateCr(req *common.HcoRequest, Client client.Clien } // at this point we found the object in the cache and we check if something was changed - if (pc.Name == found.Name) && (pc.Value == found.Value) && - (pc.Description == found.Description) && hcoutil.CompareLabels(&pc.ObjectMeta, &found.ObjectMeta) { + specEquals := (pc.Value == found.Value) && (pc.Description == found.Description) + if (pc.Name == found.Name) && specEquals && hcoutil.CompareLabels(&pc.ObjectMeta, &found.ObjectMeta) { return false, false, nil } @@ -899,16 +901,38 @@ func (kvPriorityClassHooks) updateCr(req *common.HcoRequest, Client client.Clien req.Logger.Info("Reconciling an externally updated PriorityClass's Spec to its opinionated values") } - // something was changed but since we can't patch a priority class object, we remove it - err := Client.Delete(req.Ctx, found, &client.DeleteOptions{}) - if err != nil { - return false, false, err + // make sure req labels are in place, while allowing user defined labels + labels := maps.Clone(found.Labels) + if labels == nil { + labels = make(map[string]string) + } + if len(pc.Labels) > 0 { + maps.Copy(labels, pc.Labels) } - // create the new object - err = Client.Create(req.Ctx, pc, &client.CreateOptions{}) - if err != nil { - return false, false, err + if !specEquals { + // something was changed but since we can't patch a priority class object, we remove it + err := Client.Delete(req.Ctx, found) + if err != nil { + return false, false, err + } + + // create the new object + pc.Labels = labels + err = Client.Create(req.Ctx, pc) + if err != nil { + return false, false, err + } + } else { + patch, err := getLabelPatch(found.Labels, labels) + if err != nil { + return false, false, err + } + + err = Client.Patch(req.Ctx, found, client.RawPatch(types.JSONPatchType, patch)) + if err != nil { + return false, false, err + } } pc.DeepCopyInto(found) @@ -986,3 +1010,18 @@ func hcoCertConfig2KvCertificateRotateStrategy(hcoCertConfig hcov1beta1.HyperCon }, } } + +func getLabelPatch(dest, src map[string]string) ([]byte, error) { + const labelPath = "/metadata/labels/" + patches := patch.New() + + for k, v := range src { + lbl, ok := dest[k] + if !ok { + patches.AddOption(patch.WithAdd(labelPath+patch.EscapeJSONPointer(k), v)) + } else if lbl != v { + patches.AddOption(patch.WithReplace(labelPath+patch.EscapeJSONPointer(k), v)) + } + } + return patches.GeneratePayload() +} diff --git a/controllers/operands/kubevirt_test.go b/controllers/operands/kubevirt_test.go index 3cb004c6d3..6aa963bc1d 100644 --- a/controllers/operands/kubevirt_test.go +++ b/controllers/operands/kubevirt_test.go @@ -122,7 +122,7 @@ var _ = Describe("KubeVirt Operand", func() { DescribeTable("should return error when there is something wrong", func(initiateErrors func(testClient *commontestutils.HcoTestClient) error) { modifiedResource := NewKubeVirtPriorityClass(hco) - modifiedResource.Labels = map[string]string{"foo": "bar"} + modifiedResource.Value = 1 cl := commontestutils.InitClient([]client.Object{modifiedResource}) expectedError := initiateErrors(cl) @@ -166,6 +166,68 @@ var _ = Describe("KubeVirt Operand", func() { }), ) + Context("check labels", func() { + It("should add missing labels", func(ctx context.Context) { + expectedResource := NewKubeVirtPriorityClass(hco) + delete(expectedResource.Labels, hcoutil.AppLabelComponent) + + cl := commontestutils.InitClient([]client.Object{expectedResource}) + handler := (*genericOperand)(newKvPriorityClassHandler(cl, commontestutils.GetScheme())) + res := handler.ensure(req) + Expect(res.Err).ToNot(HaveOccurred()) + + foundPC := schedulingv1.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: kvPriorityClass, + }, + } + + Expect(cl.Get(ctx, client.ObjectKeyFromObject(&foundPC), &foundPC)).To(Succeed()) + Expect(foundPC.Labels).To(HaveKeyWithValue(hcoutil.AppLabelComponent, string(hcoutil.AppComponentCompute))) + }) + + It("should fix wrong labels", func(ctx context.Context) { + expectedResource := NewKubeVirtPriorityClass(hco) + expectedResource.Labels[hcoutil.AppLabelComponent] = string(hcoutil.AppComponentStorage) + + cl := commontestutils.InitClient([]client.Object{expectedResource}) + handler := (*genericOperand)(newKvPriorityClassHandler(cl, commontestutils.GetScheme())) + res := handler.ensure(req) + Expect(res.Err).ToNot(HaveOccurred()) + + foundPC := schedulingv1.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: kvPriorityClass, + }, + } + + Expect(cl.Get(ctx, client.ObjectKeyFromObject(&foundPC), &foundPC)).To(Succeed()) + Expect(foundPC.Labels).To(HaveKeyWithValue(hcoutil.AppLabelComponent, string(hcoutil.AppComponentCompute))) + }) + + It("should keep user-defined labels", func(ctx context.Context) { + const customLabel = "custom-label" + expectedResource := NewKubeVirtPriorityClass(hco) + expectedResource.Labels[customLabel] = "test" + expectedResource.Labels[hcoutil.AppLabelComponent] = string(hcoutil.AppComponentStorage) + + cl := commontestutils.InitClient([]client.Object{expectedResource}) + handler := (*genericOperand)(newKvPriorityClassHandler(cl, commontestutils.GetScheme())) + res := handler.ensure(req) + Expect(res.Err).ToNot(HaveOccurred()) + + foundPC := schedulingv1.PriorityClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: kvPriorityClass, + }, + } + + Expect(cl.Get(ctx, client.ObjectKeyFromObject(&foundPC), &foundPC)).To(Succeed()) + Expect(foundPC.Labels).To(HaveKeyWithValue(customLabel, "test")) + Expect(foundPC.Labels).To(HaveKeyWithValue(hcoutil.AppLabelComponent, string(hcoutil.AppComponentCompute))) + }) + }) + }) Context("KubeVirt", func() { diff --git a/deploy/cluster_role.yaml b/deploy/cluster_role.yaml index d1be1e55df..c43e3e5d4d 100644 --- a/deploy/cluster_role.yaml +++ b/deploy/cluster_role.yaml @@ -944,6 +944,7 @@ rules: - watch - create - delete + - patch - apiGroups: - admissionregistration.k8s.io resources: diff --git a/deploy/index-image/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml b/deploy/index-image/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml index 943c20d24e..375b42b4f6 100644 --- a/deploy/index-image/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml +++ b/deploy/index-image/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml @@ -414,6 +414,7 @@ spec: - watch - create - delete + - patch - apiGroups: - admissionregistration.k8s.io resources: diff --git a/deploy/olm-catalog/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml b/deploy/olm-catalog/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml index abfc241ea9..e70a89d309 100644 --- a/deploy/olm-catalog/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml +++ b/deploy/olm-catalog/community-kubevirt-hyperconverged/1.13.0/manifests/kubevirt-hyperconverged-operator.v1.13.0.clusterserviceversion.yaml @@ -9,7 +9,7 @@ metadata: certified: "false" console.openshift.io/disable-operand-delete: "true" containerImage: quay.io/kubevirt/hyperconverged-cluster-operator:1.13.0-unstable - createdAt: "2024-08-31 05:04:17" + createdAt: "2024-09-02 10:50:10" description: A unified operator deploying and controlling KubeVirt and its supporting operators with opinionated defaults features.operators.openshift.io/cnf: "false" @@ -414,6 +414,7 @@ spec: - watch - create - delete + - patch - apiGroups: - admissionregistration.k8s.io resources: diff --git a/go.mod b/go.mod index 8fe86a818d..c9d5557cff 100644 --- a/go.mod +++ b/go.mod @@ -36,12 +36,13 @@ require ( k8s.io/apimachinery v0.30.4 k8s.io/apiserver v0.30.4 k8s.io/client-go v12.0.0+incompatible - k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a + k8s.io/kube-openapi v0.30.0 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 kubevirt.io/api v1.3.1 kubevirt.io/application-aware-quota v1.3.0 kubevirt.io/containerized-data-importer-api v1.60.1 kubevirt.io/controller-lifecycle-operator-sdk/api v0.2.4 + kubevirt.io/kubevirt v1.3.1 kubevirt.io/ssp-operator/api v0.21.1 sigs.k8s.io/controller-runtime v0.18.4 sigs.k8s.io/controller-tools v0.15.0 diff --git a/go.sum b/go.sum index 9e54e1acb4..7da245ba66 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,12 @@ github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlL github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -80,6 +86,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -481,10 +489,14 @@ kubevirt.io/api v1.3.1 h1:MoTNo/zvDlZ44c2ocXLPln8XTaQOeUodiYbEKrTCqv4= kubevirt.io/api v1.3.1/go.mod h1:tCn7VAZktEvymk490iPSMPCmKM9UjbbfH2OsFR/IOLU= kubevirt.io/application-aware-quota v1.3.0 h1:19wWg9bWsGGnY5NxIXgsC3MOFS28PK89zE8iLwEb5PU= kubevirt.io/application-aware-quota v1.3.0/go.mod h1:DCiwU/Y9ZhzOSz8Enio7+rxxHYLfjW9xP/B4YhhEXiE= +kubevirt.io/client-go v1.0.0 h1:MMn41j/lFd+lJ7gWn7yuIZYW/aT9fI3bUimAuxAQ+Xk= +kubevirt.io/client-go v1.0.0/go.mod h1:zvYQ/L5OmTP2Pn7XcawtoR07p8VhgviADWtgdGCniWA= kubevirt.io/containerized-data-importer-api v1.60.1 h1:chmxuINvA7TPmIe8LpShCoKPxoegcKjkG9tYboFBs/U= kubevirt.io/containerized-data-importer-api v1.60.1/go.mod h1:8mwrkZIdy8j/LmCyKt2wFXbiMavLUIqDaegaIF67CZs= kubevirt.io/controller-lifecycle-operator-sdk/api v0.2.4 h1:fZYvD3/Vnitfkx6IJxjLAk8ugnZQ7CXVYcRfkSKmuZY= kubevirt.io/controller-lifecycle-operator-sdk/api v0.2.4/go.mod h1:018lASpFYBsYN6XwmA2TIrPCx6e0gviTd/ZNtSitKgc= +kubevirt.io/kubevirt v1.3.1 h1:wWBfXqaLokfGkn4Gr4BiOCd5rHo3bNfj9YlN27nTZGo= +kubevirt.io/kubevirt v1.3.1/go.mod h1:5CmvdK0pIwSdTMN5kb4JDMMn2yARqQhuW9fSqZAOA/I= kubevirt.io/ssp-operator/api v0.21.1 h1:SFhwep5ISGsfFvgjKnoTC+No/kQGDg6qffzT+sU0FJ4= kubevirt.io/ssp-operator/api v0.21.1/go.mod h1:5oWbTQBAnKa7JDqooinhI+SwaVqurHicYtHV7aBgzmY= sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw= diff --git a/pkg/components/components.go b/pkg/components/components.go index 89c96a4475..d1fce7c921 100644 --- a/pkg/components/components.go +++ b/pkg/components/components.go @@ -537,7 +537,7 @@ func GetClusterPermissions() []rbacv1.PolicyRule { { APIGroups: stringListToSlice("scheduling.k8s.io"), Resources: stringListToSlice("priorityclasses"), - Verbs: stringListToSlice("get", "list", "watch", "create", "delete"), + Verbs: stringListToSlice("get", "list", "watch", "create", "delete", "patch"), }, { APIGroups: stringListToSlice("admissionregistration.k8s.io"), diff --git a/tests/func-tests/dependency_objects_test.go b/tests/func-tests/dependency_objects_test.go index a6560b05b5..ce857b454b 100644 --- a/tests/func-tests/dependency_objects_test.go +++ b/tests/func-tests/dependency_objects_test.go @@ -11,6 +11,8 @@ import ( tests "github.com/kubevirt/hyperconverged-cluster-operator/tests/func-tests" ) +const priorityClassName = "kubevirt-cluster-critical" + var _ = Describe("[rfe_id:5672][crit:medium][vendor:cnv-qe@redhat.com][level:system]Dependency objects", Label("PriorityClass"), func() { flag.Parse() diff --git a/tests/func-tests/update_priority_class_test.go b/tests/func-tests/update_priority_class_test.go deleted file mode 100644 index 1cc5414ab9..0000000000 --- a/tests/func-tests/update_priority_class_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package tests_test - -import ( - "context" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - - tests "github.com/kubevirt/hyperconverged-cluster-operator/tests/func-tests" -) - -const priorityClassName = "kubevirt-cluster-critical" - -var _ = Describe("check update priorityClass", Ordered, Serial, func() { - var ( - cli client.Client - cliSet *kubernetes.Clientset - oldPriorityClassUID types.UID - ) - - tests.FlagParse() - - BeforeAll(func(ctx context.Context) { - var err error - cli = tests.GetControllerRuntimeClient() - cliSet = tests.GetK8sClientSet() - - pc, err := cliSet.SchedulingV1().PriorityClasses().Get(ctx, priorityClassName, metav1.GetOptions{}) - Expect(err).ToNot(HaveOccurred()) - - Expect(pc.UID).ToNot(BeEmpty()) - oldPriorityClassUID = pc.UID - }) - - It("should have the right reference for the priorityClass in the HyperConverged CR", func(ctx context.Context) { - uid := getPriorityClassHCORef(ctx, cli) - Expect(uid).To(Equal(oldPriorityClassUID)) - }) - - It("should recreate the priorityClass on update", func(ctx context.Context) { - GinkgoWriter.Printf("oldPriorityClassUID: %q\n", oldPriorityClassUID) - // `~1` is the jsonpatch escapoe sequence for `\` - patch := []byte(`[{"op": "replace", "path": "/metadata/labels/app.kubernetes.io~1managed-by", "value": "test"}]`) - - Eventually(func(ctx context.Context) error { - _, err := cliSet.SchedulingV1().PriorityClasses().Patch(ctx, priorityClassName, types.JSONPatchType, patch, metav1.PatchOptions{}) - return err - }).WithTimeout(time.Second * 5).WithPolling(time.Millisecond * 100).WithContext(ctx).Should(Succeed()) - - var newUID types.UID - Eventually(func(g Gomega, ctx context.Context) { - By("make sure a new priority class was created, by checking its UID") - pc, err := cliSet.SchedulingV1().PriorityClasses().Get(ctx, priorityClassName, metav1.GetOptions{}) - g.Expect(err).ToNot(HaveOccurred()) - - newUID = pc.UID - g.Expect(newUID).ToNot(Or(Equal(types.UID("")), Equal(oldPriorityClassUID))) - g.Expect(pc.GetLabels()).ToNot(HaveKey("test")) - }).WithTimeout(30 * time.Second). - WithPolling(100 * time.Millisecond). - WithContext(ctx). - Should(Succeed()) - - GinkgoWriter.Printf("oldPriorityClassUID: %q; newUID: %q\n", oldPriorityClassUID, newUID) - Eventually(func(ctx context.Context) types.UID { - return getPriorityClassHCORef(ctx, cli) - }). - WithTimeout(5 * time.Minute). - WithPolling(time.Second). - WithContext(ctx). - Should(And(Not(BeEmpty()), Equal(newUID))) - }) -}) - -func getPriorityClassHCORef(ctx context.Context, cli client.Client) types.UID { - hc := tests.GetHCO(ctx, cli) - - for _, obj := range hc.Status.RelatedObjects { - if obj.Kind == "PriorityClass" && obj.Name == priorityClassName { - return obj.UID - } - } - return "" -} diff --git a/vendor/kubevirt.io/kubevirt/LICENSE b/vendor/kubevirt.io/kubevirt/LICENSE new file mode 100644 index 0000000000..549d874d4f --- /dev/null +++ b/vendor/kubevirt.io/kubevirt/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 The KubeVirt Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/BUILD.bazel b/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/BUILD.bazel new file mode 100644 index 0000000000..f46ad97bb9 --- /dev/null +++ b/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["patch.go"], + importpath = "kubevirt.io/kubevirt/pkg/apimachinery/patch", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = [ + "patch_suite_test.go", + "patch_test.go", + ], + deps = [ + ":go_default_library", + "//staging/src/kubevirt.io/client-go/testutils:go_default_library", + "//vendor/github.com/onsi/ginkgo/v2:go_default_library", + "//vendor/github.com/onsi/gomega:go_default_library", + ], +) diff --git a/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/patch.go b/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/patch.go new file mode 100644 index 0000000000..15c6994382 --- /dev/null +++ b/vendor/kubevirt.io/kubevirt/pkg/apimachinery/patch/patch.go @@ -0,0 +1,164 @@ +/* + * This file is part of the KubeVirt project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2018 Red Hat, Inc. + * + */ + +package patch + +import ( + "encoding/json" + "fmt" + "strings" +) + +type PatchOperation struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` +} + +const ( + PatchReplaceOp = "replace" + PatchTestOp = "test" + PatchAddOp = "add" + PatchRemoveOp = "remove" +) + +func (p *PatchOperation) MarshalJSON() ([]byte, error) { + switch p.Op { + // The 'remove' operation is the only patching operation without a value + // and it needs to be parsed differently. + case PatchRemoveOp: + return json.Marshal(&struct { + Op string `json:"op"` + Path string `json:"path"` + }{ + Op: p.Op, + Path: p.Path, + }) + case PatchTestOp, PatchReplaceOp, PatchAddOp: + return json.Marshal(&struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` + }{ + Op: p.Op, + Path: p.Path, + Value: p.Value, + }) + default: + return nil, fmt.Errorf("operation %s not recognized", p.Op) + } +} + +type PatchSet struct { + patches []PatchOperation +} + +type PatchOption func(patches *PatchSet) + +func New(opts ...PatchOption) *PatchSet { + p := &PatchSet{} + p.AddOption(opts...) + return p +} + +func (p *PatchSet) AddOption(opts ...PatchOption) { + for _, f := range opts { + f(p) + } +} + +func (p *PatchSet) addOp(op, path string, value interface{}) { + p.patches = append(p.patches, PatchOperation{ + Op: op, + Path: path, + Value: value, + }) +} + +func WithTest(path string, value interface{}) PatchOption { + return func(p *PatchSet) { + p.addOp(PatchTestOp, path, value) + } +} + +func WithAdd(path string, value interface{}) PatchOption { + return func(p *PatchSet) { + p.addOp(PatchAddOp, path, value) + } +} + +func WithReplace(path string, value interface{}) PatchOption { + return func(p *PatchSet) { + p.addOp(PatchReplaceOp, path, value) + } +} + +func WithRemove(path string) PatchOption { + return func(p *PatchSet) { + p.addOp(PatchRemoveOp, path, nil) + } +} + +func (p *PatchSet) GeneratePayload() ([]byte, error) { + return GeneratePatchPayload(p.patches...) +} + +func (p *PatchSet) IsEmpty() bool { + return len(p.patches) < 1 +} + +func GeneratePatchPayload(patches ...PatchOperation) ([]byte, error) { + if len(patches) == 0 { + return nil, fmt.Errorf("list of patches is empty") + } + + payloadBytes, err := json.Marshal(patches) + if err != nil { + return nil, err + } + + return payloadBytes, nil +} + +func GenerateTestReplacePatch(path string, oldValue, newValue interface{}) ([]byte, error) { + return GeneratePatchPayload( + PatchOperation{ + Op: PatchTestOp, + Path: path, + Value: oldValue, + }, + PatchOperation{ + Op: PatchReplaceOp, + Path: path, + Value: newValue, + }, + ) +} + +func UnmarshalPatch(patch []byte) ([]PatchOperation, error) { + var p []PatchOperation + err := json.Unmarshal(patch, &p) + + return p, err +} + +func EscapeJSONPointer(ptr string) string { + s := strings.ReplaceAll(ptr, "~", "~0") + return strings.ReplaceAll(s, "/", "~1") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6ebaff7162..178baf461f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -687,7 +687,7 @@ k8s.io/klog/v2/internal/dbg k8s.io/klog/v2/internal/serialize k8s.io/klog/v2/internal/severity k8s.io/klog/v2/internal/sloghandler -# k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a => k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a +# k8s.io/kube-openapi v0.30.0 => k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a ## explicit; go 1.20 k8s.io/kube-openapi/pkg/cached k8s.io/kube-openapi/pkg/common @@ -724,6 +724,9 @@ kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1 # kubevirt.io/controller-lifecycle-operator-sdk/api v0.2.4 ## explicit; go 1.17 kubevirt.io/controller-lifecycle-operator-sdk/api +# kubevirt.io/kubevirt v1.3.1 +## explicit; go 1.22.0 +kubevirt.io/kubevirt/pkg/apimachinery/patch # kubevirt.io/ssp-operator/api v0.21.1 ## explicit; go 1.22.4 kubevirt.io/ssp-operator/api/v1beta2