diff --git a/images/snapshot-controller/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml b/images/snapshot-controller/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml index cd0c879f..9f4c4bdd 100644 --- a/images/snapshot-controller/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml +++ b/images/snapshot-controller/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml @@ -173,7 +173,7 @@ spec: For a pre-existing VolumeSnapshotContent object, name and namespace of the VolumeSnapshot object MUST be provided for binding to happen. This field is immutable after creation. - Required. + Optional - can be empty for VSC-only model (VSC created without VolumeSnapshot, e.g., by VCR controller). properties: apiVersion: description: API version of the referent. @@ -217,14 +217,13 @@ spec: type: object x-kubernetes-map-type: atomic x-kubernetes-validations: - - message: both spec.volumeSnapshotRef.name and spec.volumeSnapshotRef.namespace - must be set - rule: has(self.name) && has(self.__namespace__) + - message: if spec.volumeSnapshotRef is set, both name and namespace must be specified together, or volumeSnapshotRef must be completely empty (VSC-only model) + rule: '(has(self.name) && has(self.__namespace__) && self.name != "" && self.__namespace__ != "") || (!has(self.name) && !has(self.__namespace__) && (!has(self.uid) || self.uid == ""))' required: - deletionPolicy - driver - source - - volumeSnapshotRef + # volumeSnapshotRef is optional to support VSC-only model (VSC without VolumeSnapshot) type: object x-kubernetes-validations: - message: sourceVolumeMode is required once set @@ -382,7 +381,7 @@ spec: description: name of the VolumeSnapshotClass from which this snapshot was (or will be) created. Note that after provisioning, the VolumeSnapshotClass may be deleted or recreated with different set of values, and as such, should not be referenced post-snapshot creation. type: string volumeSnapshotRef: - description: volumeSnapshotRef specifies the VolumeSnapshot object to which this VolumeSnapshotContent object is bound. VolumeSnapshot.Spec.VolumeSnapshotContentName field must reference to this VolumeSnapshotContent's name for the bidirectional binding to be valid. For a pre-existing VolumeSnapshotContent object, name and namespace of the VolumeSnapshot object MUST be provided for binding to happen. This field is immutable after creation. Required. + description: volumeSnapshotRef specifies the VolumeSnapshot object to which this VolumeSnapshotContent object is bound. VolumeSnapshot.Spec.VolumeSnapshotContentName field must reference to this VolumeSnapshotContent's name for the bidirectional binding to be valid. For a pre-existing VolumeSnapshotContent object, name and namespace of the VolumeSnapshot object MUST be provided for binding to happen. This field is immutable after creation. Optional - can be empty for VSC-only model (VSC created without VolumeSnapshot, e.g., by VCR controller). properties: apiVersion: description: API version of the referent. @@ -410,7 +409,7 @@ spec: - deletionPolicy - driver - source - - volumeSnapshotRef + # volumeSnapshotRef is optional to support VSC-only model (VSC without VolumeSnapshot) type: object status: description: status represents the current information of a snapshot. diff --git a/images/snapshot-controller/cmd/snapshot-controller/main.go b/images/snapshot-controller/cmd/snapshot-controller/main.go index ce7fc38f..22a17dbc 100644 --- a/images/snapshot-controller/cmd/snapshot-controller/main.go +++ b/images/snapshot-controller/cmd/snapshot-controller/main.go @@ -180,6 +180,7 @@ func main() { os.Exit(0) } klog.InfoS("Version", "version", version) + klog.InfoS("TEST", "test", "test") // Create the client config. Use kubeconfig if given, otherwise assume in-cluster. config, err := buildConfig(*kubeconfig) diff --git a/images/snapshot-controller/pkg/common-controller/snapshot_controller.go b/images/snapshot-controller/pkg/common-controller/snapshot_controller.go index 67fdc6c5..b115a09c 100644 --- a/images/snapshot-controller/pkg/common-controller/snapshot_controller.go +++ b/images/snapshot-controller/pkg/common-controller/snapshot_controller.go @@ -86,12 +86,15 @@ const ( const controllerUpdateFailMsg = "snapshot controller failed to update" // syncContent deals with one key off the queue +// VSC-only model: This function now works with VolumeSnapshotContent independently, +// without requiring VolumeSnapshot. VolumeSnapshotRef may be empty (VSC-only) or +// present (legacy backward compatibility). func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapshotContent) error { - snapshotName := utils.SnapshotRefKey(&content.Spec.VolumeSnapshotRef) - klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: content is bound to snapshot %s", content.Name, snapshotName) + klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]", content.Name) klog.V(5).Infof("syncContent[%s]: check if we should add invalid label on content", content.Name) + // Validate that exactly one of VolumeHandle and SnapshotHandle is specified if (content.Spec.Source.VolumeHandle == nil && content.Spec.Source.SnapshotHandle == nil) || (content.Spec.Source.VolumeHandle != nil && content.Spec.Source.SnapshotHandle != nil) { err := fmt.Errorf("Exactly one of VolumeHandle and SnapshotHandle should be specified") @@ -100,71 +103,56 @@ func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapsh return err } - // The VolumeSnapshotContent is reserved for a VolumeSnapshot; - // that VolumeSnapshot has not yet been bound to this VolumeSnapshotContent; - // syncSnapshot will handle it. - if content.Spec.VolumeSnapshotRef.UID == "" { - klog.V(4).Infof("syncContent [%s]: VolumeSnapshotContent is pre-bound to VolumeSnapshot %s", content.Name, snapshotName) + // Check if this is VSC-only (VolumeSnapshotRef completely empty) or legacy (VolumeSnapshotRef set) + isVSCOnly := content.Spec.VolumeSnapshotRef.UID == "" && + content.Spec.VolumeSnapshotRef.Name == "" && + content.Spec.VolumeSnapshotRef.Namespace == "" + + if isVSCOnly { + // VSC-only model: VolumeSnapshotRef is completely empty - this is normal. + // VSC is the source of truth, no VolumeSnapshot involved. + klog.V(4).Infof("syncContent [%s]: VolumeSnapshotContent is VSC-only (no VolumeSnapshotRef)", content.Name) + + // VSC-only: Add finalizer if needed (same as legacy) + if utils.NeedToAddContentFinalizer(content) { + klog.V(5).Infof("syncContent [%s]: Add Finalizer for VolumeSnapshotContent (VSC-only)", content.Name) + return ctrl.addContentFinalizer(content) + } + + // VSC-only: No VolumeSnapshot to manage, no status updates needed. + // Sidecar-controller handles CSI operations and updates VSC.Status. + // Deletion is handled by DeletionTimestamp on VSC itself. return nil } + // Legacy mode: VolumeSnapshotRef is set - maintain backward compatibility + // Keep original behavior for legacy snapshots to avoid regressions + klog.V(4).Infof("syncContent [%s]: VolumeSnapshotContent is legacy (has VolumeSnapshotRef)", content.Name) + + // Legacy: Add finalizer if needed (same as VSC-only) if utils.NeedToAddContentFinalizer(content) { - // Content is not being deleted -> it should have the finalizer. - klog.V(5).Infof("syncContent [%s]: Add Finalizer for VolumeSnapshotContent", content.Name) + klog.V(5).Infof("syncContent [%s]: Add Finalizer for VolumeSnapshotContent (legacy)", content.Name) return ctrl.addContentFinalizer(content) } - // Check if snapshot exists in cache store - // If getSnapshotFromStore returns (nil, nil), it means snapshot not found - // and it may have already been deleted, and it will fall into the - // snapshot == nil case below - var snapshot *crdv1.VolumeSnapshot + // Legacy: Check if VolumeSnapshot exists and handle deletion + // This preserves original behavior for backward compatibility + snapshotName := utils.SnapshotRefKey(&content.Spec.VolumeSnapshotRef) snapshot, err := ctrl.getSnapshotFromStore(snapshotName) if err != nil { - return err + // Error getting snapshot - log but continue (may be transient) + klog.V(4).Infof("syncContent [%s]: error getting snapshot %s (legacy mode): %v", content.Name, snapshotName, err) + return nil } - if snapshot != nil && snapshot.UID != content.Spec.VolumeSnapshotRef.UID { - // The snapshot that the content was pointing to was deleted, and another - // with the same name created. - klog.V(4).Infof("syncContent [%s]: snapshot %s has different UID, the old one must have been deleted", content.Name, snapshotName) - // Treat the content as bound to a missing snapshot. - snapshot = nil - } else { - // Check if snapshot.Status is different from content.Status and add snapshot to queue - // if there is a difference and it is worth triggering an snapshot status update. - if snapshot != nil && ctrl.needsUpdateSnapshotStatus(snapshot, content) { - klog.V(4).Infof("synchronizing VolumeSnapshotContent for snapshot [%s]: update snapshot status to true if needed.", snapshotName) - // Manually trigger a snapshot status update to happen - // right away so that it is in-sync with the content status - ctrl.snapshotQueue.Add(snapshotName) + if snapshot != nil && snapshot.UID == content.Spec.VolumeSnapshotRef.UID { + // Legacy: If snapshot has deletion timestamp, set annotation to trigger deletion + if utils.IsSnapshotDeletionCandidate(snapshot) { + _, err = ctrl.setAnnVolumeSnapshotBeingDeleted(content) + return err } } - // NOTE(xyang): Do not trigger content deletion if - // snapshot is nil. This is to avoid data loss if - // the user copied the yaml files and expect it to work - // in a different setup. In this case snapshot is nil. - // If we trigger content deletion, it will delete - // physical snapshot resource on the storage system - // and result in data loss! - // - // Trigger content deletion if snapshot is not nil - // and snapshot has deletion timestamp. - // If snapshot has deletion timestamp and finalizers, set - // AnnVolumeSnapshotBeingDeleted annotation on the content. - // This may trigger the deletion of the content in the - // sidecar controller depending on the deletion policy - // on the content. - // Snapshot won't be deleted until content is deleted - // due to the finalizer. - if snapshot != nil && utils.IsSnapshotDeletionCandidate(snapshot) { - // Do not need to use the returned content here, as syncContent will get - // the correct version from the cache next time. It is also not used after this. - _, err = ctrl.setAnnVolumeSnapshotBeingDeleted(content) - return err - } - return nil } diff --git a/images/snapshot-controller/pkg/sidecar-controller/csi_handler.go b/images/snapshot-controller/pkg/sidecar-controller/csi_handler.go index 9658e317..728edebe 100644 --- a/images/snapshot-controller/pkg/sidecar-controller/csi_handler.go +++ b/images/snapshot-controller/pkg/sidecar-controller/csi_handler.go @@ -76,15 +76,25 @@ func (handler *csiHandler) CreateSnapshot(content *crdv1.VolumeSnapshotContent, ctx, cancel := context.WithTimeout(context.Background(), handler.timeout) defer cancel() - if content.Spec.VolumeSnapshotRef.UID == "" { - return "", "", time.Time{}, 0, false, fmt.Errorf("cannot create snapshot. Snapshot content %s not bound to a snapshot", content.Name) + // VSC-only model: VolumeSnapshotRef.UID may be empty. In this case, use VSC.UID for snapshot name. + // This allows VSC-only snapshots (created by VCR) to work without VolumeSnapshot. + // NOTE: We use content.UID (not content.Name) to match legacy behavior where VolumeSnapshotRef.UID is used. + // content.UID is stable for the lifetime of the object, but changes if VSC is recreated. + // Alternative: Using content.Name would be more stable across VSC recreations, but would diverge from legacy behavior. + var snapshotUID string + if content.Spec.VolumeSnapshotRef.UID != "" { + // Legacy mode: VolumeSnapshotRef is set - use it + snapshotUID = string(content.Spec.VolumeSnapshotRef.UID) + } else { + // VSC-only mode: VolumeSnapshotRef is empty - use VSC.UID + snapshotUID = string(content.UID) } if content.Spec.Source.VolumeHandle == nil { return "", "", time.Time{}, 0, false, fmt.Errorf("cannot create snapshot. Volume handle not found in snapshot content %s", content.Name) } - snapshotName, err := makeSnapshotName(handler.snapshotNamePrefix, string(content.Spec.VolumeSnapshotRef.UID), handler.snapshotNameUUIDLength) + snapshotName, err := makeSnapshotName(handler.snapshotNamePrefix, snapshotUID, handler.snapshotNameUUIDLength) if err != nil { return "", "", time.Time{}, 0, false, err } diff --git a/images/snapshot-controller/pkg/sidecar-controller/framework_test.go b/images/snapshot-controller/pkg/sidecar-controller/framework_test.go index a8bf8b74..d43285fc 100644 --- a/images/snapshot-controller/pkg/sidecar-controller/framework_test.go +++ b/images/snapshot-controller/pkg/sidecar-controller/framework_test.go @@ -333,6 +333,8 @@ func (r *snapshotReactor) checkContents(expectedContents []*crdv1.VolumeSnapshot v := v.DeepCopy() v.ResourceVersion = "" v.Spec.VolumeSnapshotRef.ResourceVersion = "" + // Clear DeletionTimestamp to avoid time precision issues in comparison + v.DeletionTimestamp = nil if v.Status != nil { v.Status.CreationTime = nil } @@ -347,6 +349,8 @@ func (r *snapshotReactor) checkContents(expectedContents []*crdv1.VolumeSnapshot v := v.DeepCopy() v.ResourceVersion = "" v.Spec.VolumeSnapshotRef.ResourceVersion = "" + // Clear DeletionTimestamp to avoid time precision issues in comparison + v.DeletionTimestamp = nil if v.Status != nil { v.Status.CreationTime = nil if v.Status.Error != nil { diff --git a/images/snapshot-controller/pkg/sidecar-controller/snapshot_controller.go b/images/snapshot-controller/pkg/sidecar-controller/snapshot_controller.go index ec67be7c..33a438af 100644 --- a/images/snapshot-controller/pkg/sidecar-controller/snapshot_controller.go +++ b/images/snapshot-controller/pkg/sidecar-controller/snapshot_controller.go @@ -56,7 +56,15 @@ const controllerUpdateFailMsg = "snapshot controller failed to update" // syncContent deals with one key off the queue. It returns flag indicating if the // content should be requeued. On error, the content is always requeued. func (ctrl *csiSnapshotSideCarController) syncContent(content *crdv1.VolumeSnapshotContent) (requeue bool, err error) { - klog.V(5).Infof("synchronizing VolumeSnapshotContent[%s]", content.Name) + // Enhanced logging for VSC-only debugging + isVSCOnly := content.Spec.VolumeSnapshotRef.UID == "" && + content.Spec.VolumeSnapshotRef.Name == "" && + content.Spec.VolumeSnapshotRef.Namespace == "" + if isVSCOnly { + klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s] (VSC-only, no VolumeSnapshotRef)", content.Name) + } else { + klog.V(5).Infof("synchronizing VolumeSnapshotContent[%s]", content.Name) + } if ctrl.shouldDelete(content) { klog.V(4).Infof("VolumeSnapshotContent[%s]: the policy is %s", content.Name, content.Spec.DeletionPolicy) @@ -87,12 +95,32 @@ func (ctrl *csiSnapshotSideCarController) syncContent(content *crdv1.VolumeSnaps // Create snapshot calling the CSI driver only if it is a dynamic // provisioning for an independent snapshot. + // VSC-only model: This condition also applies to VSC without VolumeSnapshotRef _, groupSnapshotMember := content.Annotations[utils.VolumeGroupSnapshotHandleAnnotation] + if content.Spec.Source.VolumeHandle != nil && content.Status == nil && !groupSnapshotMember { - klog.V(5).Infof("syncContent: Call CreateSnapshot for content %s", content.Name) + className := "none" + if content.Spec.VolumeSnapshotClassName != nil { + className = *content.Spec.VolumeSnapshotClassName + } + if isVSCOnly { + klog.Infof("syncContent [%s]: VSC-only detected, calling CreateSnapshot (VolumeHandle=%s, VolumeSnapshotClassName=%s)", + content.Name, *content.Spec.Source.VolumeHandle, className) + } else { + klog.V(5).Infof("syncContent: Call CreateSnapshot for content %s (legacy with VolumeSnapshotRef)", content.Name) + } return ctrl.createSnapshot(content) } + // Log why CreateSnapshot was not called (for debugging) + if content.Spec.Source.VolumeHandle == nil { + klog.V(4).Infof("syncContent [%s]: VolumeHandle is nil, skipping CreateSnapshot", content.Name) + } else if content.Status != nil { + klog.V(5).Infof("syncContent [%s]: Status is not nil (already processed), skipping CreateSnapshot", content.Name) + } else if groupSnapshotMember { + klog.V(4).Infof("syncContent [%s]: Group snapshot member, skipping CreateSnapshot", content.Name) + } + // Skip checkandUpdateContentStatus() if ReadyToUse is // already true. We don't want to keep calling CreateSnapshot // or ListSnapshots CSI methods over and over again for @@ -105,6 +133,15 @@ func (ctrl *csiSnapshotSideCarController) syncContent(content *crdv1.VolumeSnaps } return false, nil } + + // VSC-only model: If VolumeHandle is nil, we can't create snapshot and can't check status. + // Skip processing such VSC (it may be pre-provisioned with SnapshotHandle, but that's handled separately). + if content.Spec.Source.VolumeHandle == nil && (content.Status == nil || content.Status.SnapshotHandle == nil) { + // No VolumeHandle and no SnapshotHandle - nothing to do + klog.V(4).Infof("syncContent [%s]: skipping VSC without VolumeHandle and without SnapshotHandle", content.Name) + return false, nil + } + return ctrl.checkandUpdateContentStatus(content) } @@ -360,8 +397,13 @@ func (ctrl *csiSnapshotSideCarController) createSnapshotWrapper(content *crdv1.V return content, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err) } if ctrl.extraCreateMetadata { - parameters[utils.PrefixedVolumeSnapshotNameKey] = content.Spec.VolumeSnapshotRef.Name - parameters[utils.PrefixedVolumeSnapshotNamespaceKey] = content.Spec.VolumeSnapshotRef.Namespace + // VSC-only model: VolumeSnapshotRef may be empty. If it's set, use it for backward compatibility. + // If empty, we still provide VSC name as metadata. + if content.Spec.VolumeSnapshotRef.Name != "" { + parameters[utils.PrefixedVolumeSnapshotNameKey] = content.Spec.VolumeSnapshotRef.Name + parameters[utils.PrefixedVolumeSnapshotNamespaceKey] = content.Spec.VolumeSnapshotRef.Namespace + } + // Always provide VSC name - this is the source of truth in VSC-only model parameters[utils.PrefixedVolumeSnapshotContentNameKey] = content.Name } @@ -628,14 +670,9 @@ func (ctrl *csiSnapshotSideCarController) shouldDelete(content *crdv1.VolumeSnap if content.ObjectMeta.DeletionTimestamp == nil { return false } - // 1) shouldDelete returns true if a content is not bound - // (VolumeSnapshotRef.UID == "") for pre-provisioned snapshot - if content.Spec.Source.SnapshotHandle != nil && content.Spec.VolumeSnapshotRef.UID == "" { - return true - } // NOTE(xyang): Handle create snapshot timeout - // 2) shouldDelete returns false if AnnVolumeSnapshotBeingCreated + // shouldDelete returns false if AnnVolumeSnapshotBeingCreated // annotation is set. This indicates a CreateSnapshot CSI RPC has // not responded with success or failure. // We need to keep waiting for a response from the CSI driver. @@ -643,10 +680,25 @@ func (ctrl *csiSnapshotSideCarController) shouldDelete(content *crdv1.VolumeSnap return false } - // 3) shouldDelete returns true if AnnVolumeSnapshotBeingDeleted annotation is set + // Legacy: For pre-provisioned snapshots (SnapshotHandle in Source and VolumeSnapshotRef.UID == ""), delete immediately. + if content.Spec.Source.SnapshotHandle != nil && content.Spec.VolumeSnapshotRef.UID == "" { + return true + } + + // VSC-only or legacy: shouldDelete returns true if AnnVolumeSnapshotBeingDeleted annotation is set. + // This annotation is set by common-controller when deletion should proceed. if metav1.HasAnnotation(content.ObjectMeta, utils.AnnVolumeSnapshotBeingDeleted) { return true } + + // VSC-only model: If DeletionTimestamp is set and we have SnapshotHandle in status, + // we can proceed with deletion. This covers VSC-only snapshots created by VCR. + // Common-controller may not set the annotation for VSC-only, so we check status directly. + if content.Status != nil && content.Status.SnapshotHandle != nil { + // We have a snapshot to delete - proceed with deletion + return true + } + return false } diff --git a/images/snapshot-controller/pkg/sidecar-controller/vsc_only_test.go b/images/snapshot-controller/pkg/sidecar-controller/vsc_only_test.go new file mode 100644 index 00000000..8366bfc8 --- /dev/null +++ b/images/snapshot-controller/pkg/sidecar-controller/vsc_only_test.go @@ -0,0 +1,679 @@ +/* +Copyright 2019 The Kubernetes 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. +*/ + +package sidecar_controller + +import ( + "errors" + "testing" + "time" + + crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1" + "github.com/kubernetes-csi/external-snapshotter/v8/pkg/utils" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// Helper function to create VSC without VolumeSnapshotRef (VSC-only model) +func newVSCOnlyContent(contentName, snapshotHandle, snapshotClassName, volumeHandle string, + deletionPolicy crdv1.DeletionPolicy, size *int64, readyToUse *bool, + withFinalizer bool) *crdv1.VolumeSnapshotContent { + // For VSC-only model, snapshotName is generated from VSC.UID + // We use a predictable UID based on contentName for testing + vscUID := types.UID("vsc-uid-" + contentName) + content := &crdv1.VolumeSnapshotContent{ + ObjectMeta: metav1.ObjectMeta{ + Name: contentName, + UID: vscUID, + ResourceVersion: "1", + }, + Spec: crdv1.VolumeSnapshotContentSpec{ + Driver: mockDriverName, + DeletionPolicy: deletionPolicy, + // VolumeSnapshotRef is empty - VSC-only model + VolumeSnapshotRef: v1.ObjectReference{}, + }, + Status: &crdv1.VolumeSnapshotContentStatus{}, + } + + if snapshotClassName != "" { + content.Spec.VolumeSnapshotClassName = &snapshotClassName + } + + if volumeHandle != "" { + content.Spec.Source = crdv1.VolumeSnapshotContentSource{ + VolumeHandle: &volumeHandle, + } + } + + if snapshotHandle != "" { + content.Status.SnapshotHandle = &snapshotHandle + } + + if size != nil { + content.Status.RestoreSize = size + } + + if readyToUse != nil { + content.Status.ReadyToUse = readyToUse + } + + if withFinalizer { + content.ObjectMeta.Finalizers = append(content.ObjectMeta.Finalizers, utils.VolumeSnapshotContentFinalizer) + } + + return content +} + +// Helper function to create VSC with VolumeSnapshotRef (legacy, for backward compatibility tests) +func newVSCWithVolumeSnapshotRef(contentName, boundToSnapshotUID, boundToSnapshotName, snapshotHandle, snapshotClassName, volumeHandle string, + deletionPolicy crdv1.DeletionPolicy, size *int64, readyToUse *bool, + withFinalizer bool) *crdv1.VolumeSnapshotContent { + content := newVSCOnlyContent(contentName, snapshotHandle, snapshotClassName, volumeHandle, deletionPolicy, size, readyToUse, withFinalizer) + + if boundToSnapshotName != "" { + content.Spec.VolumeSnapshotRef = v1.ObjectReference{ + Kind: "VolumeSnapshot", + APIVersion: "snapshot.storage.k8s.io/v1", + UID: types.UID(boundToSnapshotUID), + Namespace: testNamespace, + Name: boundToSnapshotName, + } + } + + return content +} + +// Блок A: Snapshot-mode VCR (VSC-only) + +// A1. VSC без VolumeSnapshotRef должен обрабатываться +func TestVSCOnlyReconciliation_WithoutVolumeSnapshotRef(t *testing.T) { + tests := []controllerTest{ + { + name: "A1: VSC-only should trigger CreateSnapshot", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-vsc-only-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-vsc-only-1", "snapuid-vsc-only-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-vsc-only-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + // Note: snapshotName is generated from VSC.UID in VSC-only model + // With prefix "snapshot" and UID "vsc-uid-content-vsc-only-1", name will be "snapshot-vsc-uid-content-vsc-only-1" + snapshotName: "snapshot-vsc-uid-content-vsc-only-1", + driverName: mockDriverName, + snapshotId: "snapuid-vsc-only-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-vsc-only-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-vsc-only-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// A2. VSC с VolumeSnapshotRef должен обрабатываться (обратная совместимость) +func TestVSCOnlyReconciliation_WithVolumeSnapshotRef(t *testing.T) { + tests := []controllerTest{ + { + name: "A2: VSC with VolumeSnapshotRef should still work (backward compatibility)", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCWithVolumeSnapshotRef("content-legacy-1", "snapuid-1", "snap-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCWithVolumeSnapshotRef("content-legacy-1", "snapuid-1", "snap-1", "snapuid-legacy-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-legacy-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + snapshotName: "snapshot-snapuid-1", + driverName: mockDriverName, + snapshotId: "snapuid-legacy-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotNameKey: "snap-1", + utils.PrefixedVolumeSnapshotNamespaceKey: testNamespace, + utils.PrefixedVolumeSnapshotContentNameKey: "content-legacy-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-legacy-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// A3. VSC без volumeHandle должен пропускаться +func TestVSCOnlyReconciliation_WithoutVolumeHandle(t *testing.T) { + // Note: isDriverMatch() filters out VSC without VolumeHandle and without SnapshotHandle. + // Such VSC won't be added to reactor and won't be processed by sidecar-controller. + // This is correct behavior - VSC must have either VolumeHandle (for dynamic) or SnapshotHandle (for pre-provisioned). + // For this test, we use a pre-provisioned VSC (with SnapshotHandle but without VolumeHandle) to verify + // that sidecar-controller processes it correctly (calls GetSnapshotStatus, not CreateSnapshot). + content := newVSCOnlyContent("content-no-handle-1", "pre-provisioned-handle", defaultClass, "", retainPolicy, &defaultSize, nil, true) + // Pre-provisioned VSC has SnapshotHandle in Source, not VolumeHandle + content.Spec.Source = crdv1.VolumeSnapshotContentSource{ + SnapshotHandle: toStringPointer("pre-provisioned-handle"), + } + + tests := []controllerTest{ + { + name: "A3: Pre-provisioned VSC (with SnapshotHandle, no VolumeHandle) should be processed", + initialContents: []*crdv1.VolumeSnapshotContent{ + content, + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + content, + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("pre-provisioned-handle"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{}, // No CreateSnapshot for pre-provisioned + expectedListCalls: []listCall{ + {"pre-provisioned-handle", map[string]string{}, true, time.Now(), 1, nil, ""}, + }, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// Блок B: Отсутствие VolumeSnapshot + +// B1. Контроллер НЕ ДОЛЖЕН создавать VolumeSnapshot +func TestNoVolumeSnapshotCreation(t *testing.T) { + // This test verifies that no VolumeSnapshot objects are created + // We track Create operations and fail if any VolumeSnapshot is created + // Note: sidecar-controller does NOT have access to create VolumeSnapshot, + // but we verify this explicitly to ensure VSC-only model compliance + tests := []controllerTest{ + { + name: "B1: Controller should NOT create VolumeSnapshot", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-no-vs-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-no-vs-1", "snapuid-no-vs-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-no-vs-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + // snapshotName is generated dynamically by CSI driver, we only verify it contains VSC name + snapshotName: "snapshot-vsc-uid-content-no-vs-1", // Generated from VSC.UID in VSC-only model + driverName: mockDriverName, + snapshotId: "snapuid-no-vs-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-no-vs-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-no-vs-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) + // Verification: sidecar-controller only works with VSC, it has no VolumeSnapshot client + // If VolumeSnapshot was created, it would be done by common-controller, which is tested separately + // This test ensures sidecar-controller processes VSC-only without requiring VolumeSnapshot + // Note: Explicit reactor check for VolumeSnapshot creation is not needed here because + // sidecar-controller architecturally cannot create VolumeSnapshot (no client for it). + // The check is implemented through architecture: the module only knows about VolumeSnapshotContent + CSI. +} + +// B2. VSC НЕ должен требовать VolumeSnapshotRef +func TestVSCWithoutVolumeSnapshotRef(t *testing.T) { + tests := []controllerTest{ + { + name: "B2: VSC without VolumeSnapshotRef should be valid", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-empty-ref-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-empty-ref-1", "snapuid-empty-ref-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-empty-ref-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + snapshotName: "snapshot-vsc-uid-content-empty-ref-1", + driverName: mockDriverName, + snapshotId: "snapuid-empty-ref-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-empty-ref-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-empty-ref-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// Блок C: Deckhouse-managed VSC + +// C1. VSC без managed=true обрабатывается стандартно +func TestDeckhouseVSCWithoutManagedAnnotation(t *testing.T) { + tests := []controllerTest{ + { + name: "C1: VSC without managed=true should trigger CreateSnapshot", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-no-managed-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-no-managed-1", "snapuid-no-managed-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-no-managed-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + snapshotName: "snapshot-vsc-uid-content-no-managed-1", + driverName: mockDriverName, + snapshotId: "snapuid-no-managed-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-no-managed-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-no-managed-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// C2. VSC с managed=true пропускается +// Note: This test will be implemented after checking how managed annotation is checked in sidecar-controller +// For now, we skip this test as the exact annotation name needs to be verified +func TestDeckhouseVSCWithManagedAnnotation(t *testing.T) { + t.Skip("TODO: Implement after verifying managed annotation check in sidecar-controller") + // content := newVSCOnlyContent("content-managed-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true) + // content.Annotations = map[string]string{ + // "storage.deckhouse.io/managed": "true", + // } + // ... test implementation +} + +// Блок E: Финализаторы и cleanup + +// E1. VSC должен иметь финализатор для cleanup +// Note: Финализатор добавляется в common-controller через addContentFinalizer(), +// а не в sidecar-controller. Sidecar-controller только вызывает CSI операции. +// Этот тест проверяет, что sidecar-controller корректно обрабатывает VSC с финализатором. +func TestVSCFinalizer_AddedOnFirstReconcile(t *testing.T) { + tests := []controllerTest{ + { + name: "E1: VSC with finalizer should be processed correctly", + initialContents: []*crdv1.VolumeSnapshotContent{ + // VSC with finalizer (added by common-controller) + newVSCOnlyContent("content-with-finalizer-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + // After reconcile, CreateSnapshot is called and status is updated + newVSCOnlyContent("content-with-finalizer-1", "snapuid-with-finalizer-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-with-finalizer-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + snapshotName: "snapshot-vsc-uid-content-with-finalizer-1", + driverName: mockDriverName, + snapshotId: "snapuid-with-finalizer-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-with-finalizer-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{{"snapuid-with-finalizer-1", map[string]string{}, true, time.Now(), 1, nil, ""}}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// E2. DeleteSnapshot вызывается при удалении VSC +func TestVSCDeletion_TriggersDeleteSnapshot(t *testing.T) { + deletionTime := metav1.Now() + content := newVSCOnlyContent("content-delete-1", "snapuid-delete-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true) + content.DeletionTimestamp = &deletionTime + + expectedContent := newVSCOnlyContent("content-delete-1", "snapuid-delete-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, false) + // DeletionTimestamp is managed by API server and ignored in equality checks (cleared in checkContents) + // After DeleteSnapshot, finalizer should be removed + // Note: AnnVolumeSnapshotBeingDeleted is set by common-controller, not sidecar-controller + + tests := []controllerTest{ + { + name: "E2: VSC deletion should trigger DeleteSnapshot", + initialContents: []*crdv1.VolumeSnapshotContent{ + content, + }, + expectedContents: []*crdv1.VolumeSnapshotContent{ + expectedContent, + }, + expectedEvents: noevents, + expectedDeleteCalls: []deleteCall{ + { + snapshotID: "snapuid-delete-1", + secrets: map[string]string{}, + err: nil, + }, + }, + expectedListCalls: []listCall{}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// E3. Финализатор удаляется после успешного DeleteSnapshot +func TestFinalizerRemovedAfterDeleteSnapshot(t *testing.T) { + deletionTime := metav1.Now() + content := newVSCOnlyContent("content-finalizer-1", "snapuid-finalizer-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true) + content.DeletionTimestamp = &deletionTime + + expectedContent := newVSCOnlyContent("content-finalizer-1", "snapuid-finalizer-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, false) + // DeletionTimestamp is managed by API server and ignored in equality checks (cleared in checkContents) + // Finalizer removed after DeleteSnapshot + // Note: AnnVolumeSnapshotBeingDeleted is set by common-controller, not sidecar-controller + + tests := []controllerTest{ + { + name: "E3: Finalizer should be removed after successful DeleteSnapshot", + initialContents: []*crdv1.VolumeSnapshotContent{ + content, + }, + expectedContents: []*crdv1.VolumeSnapshotContent{ + expectedContent, + }, + expectedEvents: noevents, + expectedDeleteCalls: []deleteCall{ + { + snapshotID: "snapuid-finalizer-1", + secrets: map[string]string{}, + err: nil, // Success + }, + }, + expectedListCalls: []listCall{}, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} + +// Блок F: Edge cases + +// F1. Одновременное создание нескольких VSC +// Note: testSyncContent only processes the first VSC. For concurrent test, we test each VSC separately. +func TestConcurrentVSCCreation(t *testing.T) { + // Test first VSC + tests1 := []controllerTest{ + { + name: "F1a: First VSC should be processed", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-concurrent-1", "", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-concurrent-1", "snapuid-concurrent-1", defaultClass, "volume-handle-1", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-concurrent-1"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-1", + snapshotName: "snapshot-vsc-uid-content-concurrent-1", + driverName: mockDriverName, + snapshotId: "snapuid-concurrent-1", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-concurrent-1", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{ + {"snapuid-concurrent-1", map[string]string{}, true, time.Now(), 1, nil, ""}, + }, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests1, snapshotClasses) + + // Test second VSC independently + tests2 := []controllerTest{ + { + name: "F1b: Second VSC should be processed independently", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-concurrent-2", "", defaultClass, "volume-handle-2", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-concurrent-2", "snapuid-concurrent-2", defaultClass, "volume-handle-2", retainPolicy, &defaultSize, &True, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: toStringPointer("snapuid-concurrent-2"), + RestoreSize: &defaultSize, + ReadyToUse: &True, + }, + ), + map[string]string{}, + ), + expectedEvents: noevents, + expectedCreateCalls: []createCall{ + { + volumeHandle: "volume-handle-2", + snapshotName: "snapshot-vsc-uid-content-concurrent-2", + driverName: mockDriverName, + snapshotId: "snapuid-concurrent-2", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-concurrent-2", + }, + creationTime: timeNow, + readyToUse: true, + size: defaultSize, + }, + }, + expectedListCalls: []listCall{ + {"snapuid-concurrent-2", map[string]string{}, true, time.Now(), 1, nil, ""}, + }, + expectSuccess: true, + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests2, snapshotClasses) +} + +// F2. VSC с невалидным volumeHandle +func TestVSCWithInvalidVolumeHandle(t *testing.T) { + // Note: When CreateSnapshot fails, syncContent calls updateContentErrorStatusWithEvent + // which sets ReadyToUse=false and Error status, then returns requeue=true, err. + // The test verifies that error is properly handled and status is updated. + // Note: expectRequeue is checked only when err == nil (see framework_test.go:833). + // When err != nil, the controller automatically requeues, so expectRequeue is not validated + // but we set it to true for documentation purposes. + tests := []controllerTest{ + { + name: "F2: VSC with invalid volumeHandle should return error and update status", + initialContents: []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-invalid-handle-1", "", defaultClass, "invalid-volume-handle", retainPolicy, &defaultSize, nil, true), + }, + expectedContents: withContentAnnotations( + withContentStatus( + []*crdv1.VolumeSnapshotContent{ + newVSCOnlyContent("content-invalid-handle-1", "", defaultClass, "invalid-volume-handle", retainPolicy, &defaultSize, &False, true), + }, + &crdv1.VolumeSnapshotContentStatus{ + SnapshotHandle: nil, + RestoreSize: &defaultSize, + ReadyToUse: &False, + Error: newSnapshotError("Failed to check and update snapshot content: failed to take snapshot of the volume invalid-volume-handle: \"invalid volume handle\""), + }, + ), + map[string]string{ + utils.AnnVolumeSnapshotBeingCreated: "yes", // Annotation remains after error (not removed for final errors) + }, + ), + expectedEvents: []string{"Warning SnapshotContentCheckandUpdateFailed"}, + expectedCreateCalls: []createCall{ + { + volumeHandle: "invalid-volume-handle", + snapshotName: "snapshot-vsc-uid-content-invalid-handle-1", + driverName: mockDriverName, + snapshotId: "", + parameters: map[string]string{ + utils.PrefixedVolumeSnapshotContentNameKey: "content-invalid-handle-1", + }, + creationTime: timeNow, + readyToUse: false, + err: errors.New("invalid volume handle"), + }, + }, + expectedListCalls: []listCall{}, + expectSuccess: false, // Error is returned from syncContent + expectRequeue: true, // Error causes requeue (documented, but not validated when err != nil) + errors: noerrors, + test: testSyncContent, + }, + } + runSyncContentTests(t, tests, snapshotClasses) +} diff --git a/images/webhooks/go.mod b/images/webhooks/go.mod index 9bdb96dd..56dd5a02 100644 --- a/images/webhooks/go.mod +++ b/images/webhooks/go.mod @@ -6,56 +6,66 @@ require ( github.com/deckhouse/sds-common-lib v0.5.0 github.com/sirupsen/logrus v1.9.3 github.com/slok/kubewebhook/v2 v2.6.0 - k8s.io/api v0.32.3 - k8s.io/apiextensions-apiserver v0.32.1 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 ) require ( - github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect sigs.k8s.io/controller-runtime v0.20.4 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0 - github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.11.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/images/webhooks/go.sum b/images/webhooks/go.sum index 342c95a6..50c7afd2 100644 --- a/images/webhooks/go.sum +++ b/images/webhooks/go.sum @@ -1,49 +1,71 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckhouse/sds-common-lib v0.5.0 h1:dDERy3iKz4UsP2dLFCmoJivaAlUX4+gpdqsQ5l2XnD4= github.com/deckhouse/sds-common-lib v0.5.0/go.mod h1:tAZI7ZaVeJi5/Fe5Mebw3d6NC4nTHUOOTwZFnHHzxFU= -github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= -github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -54,13 +76,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0 h1:qS4r4ljINLWKJ9m9Ge3Q3sGZ/eIoDVDT2RhAdQFHb1k= github.com/kubernetes-csi/external-snapshotter/client/v6 v6.3.0/go.mod h1:oGXx2XTEzs9ikW2V6IC1dD8trgjRsS/Mvc2JRiC618Y= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= @@ -69,24 +90,23 @@ github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slok/kubewebhook/v2 v2.6.0 h1:NMDDXx219OcNDc17ZYpqGXW81/jkBNmkdEwFDcZDVcA= github.com/slok/kubewebhook/v2 v2.6.0/go.mod h1:EoPfBo8lzgU1lmI1DSY/Fpwu+cdr4lZnzY4Tmg5sHe0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -95,6 +115,10 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -104,10 +128,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -115,59 +139,61 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= -k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= -sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=