From 1c9c669fa08915c05dcd6f348c4fab5a26e91472 Mon Sep 17 00:00:00 2001 From: dongqingcc Date: Thu, 26 Feb 2026 18:40:56 +0800 Subject: [PATCH 01/32] Add e2e test case for PR 9452 Signed-off-by: dongqingcc --- test/e2e/nodeagentconfig/node-agent-config.go | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/e2e/nodeagentconfig/node-agent-config.go b/test/e2e/nodeagentconfig/node-agent-config.go index 01cc6e38c..bd1729e4e 100644 --- a/test/e2e/nodeagentconfig/node-agent-config.go +++ b/test/e2e/nodeagentconfig/node-agent-config.go @@ -78,6 +78,14 @@ var LoadAffinities func() = TestFunc(&NodeAgentConfigTestCase{ IgnoreDelayBinding: true, }, PriorityClassName: test.PriorityClassNameForDataMover, + // Explicitly add custom labels and annotations to be tested + PodLabels: map[string]string{ + "spectrocloud.com/connection": "proxy", // Tests appending custom labels (Issue #9435) + "azure.workload.identity/use": "true", // Tests overwriting built-in labels + }, + PodAnnotations: map[string]string{ + "test-data-mover-annotation": "true", + }, }, nodeAgentConfigMapName: "node-agent-config", }) @@ -244,6 +252,16 @@ func (n *NodeAgentConfigTestCase) Backup() error { Expect(backupPodList.Items[0].Spec.Affinity).To(Equal(expectedAffinity)) + // Verify PodLabels + for k, v := range n.nodeAgentConfigs.PodLabels { + Expect(backupPodList.Items[0].Labels[k]).To(Equal(v)) + } + + // Verify PodAnnotations + for k, v := range n.nodeAgentConfigs.PodAnnotations { + Expect(backupPodList.Items[0].Annotations[k]).To(Equal(v)) + } + fmt.Println("backupPod content verification completed successfully.") wait.PollUntilContextTimeout(n.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { @@ -321,6 +339,16 @@ func (n *NodeAgentConfigTestCase) Restore() error { Expect(restorePodList.Items[0].Spec.Affinity).To(Equal(expectedAffinity)) + // Verify PodLabels + for k, v := range n.nodeAgentConfigs.PodLabels { + Expect(restorePodList.Items[0].Labels[k]).To(Equal(v)) + } + + // Verify PodAnnotations + for k, v := range n.nodeAgentConfigs.PodAnnotations { + Expect(restorePodList.Items[0].Annotations[k]).To(Equal(v)) + } + fmt.Println("restorePod content verification completed successfully.") wait.PollUntilContextTimeout(n.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { From 206219db6281379e2067b465a1b8823983dc9982 Mon Sep 17 00:00:00 2001 From: dongqingcc Date: Fri, 27 Feb 2026 14:37:47 +0800 Subject: [PATCH 02/32] Add e2e test case for issue 7725 Signed-off-by: dongqingcc --- test/e2e/e2e_suite_test.go | 6 + test/e2e/nodeagentconfig/cache_pvc.go | 253 ++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 test/e2e/nodeagentconfig/cache_pvc.go diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index f0d1c9c2e..b60fee361 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -674,6 +674,12 @@ var _ = Describe( LoadAffinities, ) +var _ = Describe( + "Test data mover dynamic Cache PVC injection", + Label("NodeAgentConfig", "CachePVC"), + CachePVCTest, +) + func GetKubeConfigContext() error { var err error var tcDefault, tcStandby k8s.TestClient diff --git a/test/e2e/nodeagentconfig/cache_pvc.go b/test/e2e/nodeagentconfig/cache_pvc.go new file mode 100644 index 000000000..9104946c7 --- /dev/null +++ b/test/e2e/nodeagentconfig/cache_pvc.go @@ -0,0 +1,253 @@ +/* +Copyright the Velero contributors. + +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 nodeagentconfig + +import ( + "context" + "fmt" + "strings" + "time" + + . "github.com/onsi/gomega" + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/test" + . "github.com/vmware-tanzu/velero/test/e2e/test" + k8sutil "github.com/vmware-tanzu/velero/test/util/k8s" + veleroutil "github.com/vmware-tanzu/velero/test/util/velero" +) + +type CachePVCTestCase struct { + TestCase + nodeAgentConfigMapName string + repoConfigMapName string +} + +var CachePVCTest func() = TestFunc(&CachePVCTestCase{ + nodeAgentConfigMapName: "node-agent-config-cache", + repoConfigMapName: "backup-repo-config-cache", +}) + +func (c *CachePVCTestCase) Init() error { + c.TestCase.Init() + c.CaseBaseName = "cache-pvc-" + c.UUIDgen + c.BackupName = "backup-" + c.CaseBaseName + c.RestoreName = "restore-" + c.CaseBaseName + c.NamespacesTotal = 1 + c.NSIncluded = &[]string{} + for nsNum := 0; nsNum < c.NamespacesTotal; nsNum++ { + createNSName := fmt.Sprintf("%s-%00000d", c.CaseBaseName, nsNum) + *c.NSIncluded = append(*c.NSIncluded, createNSName) + } + + c.VeleroCfg.UseNodeAgent = true + c.VeleroCfg.UseNodeAgentWindows = true + + // Ensure Data Mover is used to trigger DataUpload/DataDownload pods + c.BackupArgs = []string{ + "create", "--namespace", c.VeleroCfg.VeleroNamespace, "backup", c.BackupName, + "--include-namespaces", strings.Join(*c.NSIncluded, ","), + "--snapshot-volumes=true", "--snapshot-move-data", + } + + c.RestoreArgs = []string{ + "create", "--namespace", c.VeleroCfg.VeleroNamespace, "restore", c.RestoreName, + "--from-backup", c.BackupName, + } + + c.TestMsg = &TestMSG{ + Desc: "Validate dynamically provisioned Cache PVC for data mover pods", + FailedMSG: "Failed to apply and validate cache PVC configuration in data mover pods.", + Text: "Should dynamically provision a Cache PVC for data mover restore pods to avoid node root FS exhaustion.", + } + return nil +} + +func (c *CachePVCTestCase) InstallVelero() error { + fmt.Println("Start to uninstall Velero") + if err := veleroutil.VeleroUninstall(c.Ctx, c.VeleroCfg); err != nil { + fmt.Printf("Fail to uninstall Velero: %s\n", err.Error()) + return err + } + + // 1. Construct node-agent ConfigMap (Define Cache PVC StorageClass and trigger threshold) + // Set residentThresholdInMB to 0 to force Velero to create a PVC even for tiny E2E test data. + nodeAgentConfigJSON := fmt.Sprintf(`{"cachePVC": {"residentThresholdInMB": 0, "storageClass": "%s"}}`, test.StorageClassName) + nodeAgentConfig := builder.ForConfigMap(c.VeleroCfg.VeleroNamespace, c.nodeAgentConfigMapName). + Data("node-agent-config", nodeAgentConfigJSON).Result() + + // 2. Construct backup repository ConfigMap (Define cacheLimitMB) + repoConfigJSON := `{"cacheLimitMB": 2048}` // Set 2GB cache limit + repoConfig := builder.ForConfigMap(c.VeleroCfg.VeleroNamespace, c.repoConfigMapName). + Data(test.UploaderTypeKopia, repoConfigJSON).Result() + + c.VeleroCfg.NodeAgentConfigMap = c.nodeAgentConfigMapName + c.VeleroCfg.BackupRepoConfigMap = c.repoConfigMapName + + // Deploy Velero with the two Cache configuration ConfigMaps + return veleroutil.PrepareVelero(c.Ctx, c.CaseBaseName, c.VeleroCfg, nodeAgentConfig, repoConfig) +} + +func (c *CachePVCTestCase) CreateResources() error { + for _, ns := range *c.NSIncluded { + if err := k8sutil.CreateNamespace(c.Ctx, c.Client, ns); err != nil { + return err + } + + pvc, err := k8sutil.CreatePVC(c.Client, ns, "volume-1", test.StorageClassName, nil) + if err != nil { + return err + } + + vols := k8sutil.CreateVolumes(pvc.Name, []string{"volume-1"}) + + deployment := k8sutil.NewDeployment( + c.CaseBaseName, + ns, + 1, + map[string]string{"app": "test"}, + c.VeleroCfg.ImageRegistryProxy, + c.VeleroCfg.WorkerOS, + ).WithVolume(vols).Result() + + deployment, err = k8sutil.CreateDeployment(c.Client.ClientGo, ns, deployment) + if err != nil { + return errors.Wrap(err, "failed to create deployment") + } + + if err := k8sutil.WaitForReadyDeployment(c.Client.ClientGo, deployment.Namespace, deployment.Name); err != nil { + return err + } + } + return nil +} + +// verifyCacheVolumeInPod correctly verifies that the Pod mounted a dynamic PVC instead of an emptyDir +func (c *CachePVCTestCase) verifyCacheVolumeInPod(pod corev1api.Pod) error { + for _, vol := range pod.Spec.Volumes { + if vol.PersistentVolumeClaim != nil { + // Velero dynamically provisioned cache volumes typically have 'cache' in the name + if strings.Contains(vol.PersistentVolumeClaim.ClaimName, "cache") || strings.Contains(vol.Name, "cache") { + return nil // Success: Found the dynamically provisioned Cache PVC + } + } + } + return fmt.Errorf("Dynamically provisioned Cache PVC not found in pod %s, feature failed!", pod.Name) +} + +func (c *CachePVCTestCase) Backup() error { + if err := veleroutil.VeleroCmdExec(c.Ctx, c.VeleroCfg.VeleroCLI, c.BackupArgs); err != nil { + return err + } + + fmt.Println("Waiting for backup to complete...") + + // Wait for backup completion + wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + backup := new(velerov1api.Backup) + if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.Get( + c.Ctx, + client.ObjectKey{Namespace: c.VeleroCfg.VeleroNamespace, Name: c.BackupName}, + backup, + ); err != nil { + return false, err + } + + if backup.Status.Phase != velerov1api.BackupPhaseCompleted && + backup.Status.Phase != velerov1api.BackupPhaseFailed && + backup.Status.Phase != velerov1api.BackupPhasePartiallyFailed { + return false, nil + } + + return true, nil + }) + + return nil +} + +func (c *CachePVCTestCase) Restore() error { + if err := veleroutil.VeleroCmdExec(c.Ctx, c.VeleroCfg.VeleroCLI, c.RestoreArgs); err != nil { + return err + } + + restorePodList := new(corev1api.PodList) + + wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + ddList := new(velerov2alpha1api.DataDownloadList) + if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.List( + c.Ctx, + ddList, + &client.ListOptions{Namespace: c.VeleroCfg.VeleroNamespace}, + ); err != nil { + return false, err + } else if len(ddList.Items) <= 0 { + return false, nil + } + + if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.List( + c.Ctx, + restorePodList, + &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + velerov1api.DataDownloadLabel: ddList.Items[0].Name, + }), + }); err != nil { + return false, err + } else if len(restorePodList.Items) <= 0 { + return false, nil + } + + return true, nil + }) + + fmt.Println("Start to verify restore data mover pod content.") + Expect(restorePodList.Items).ToNot(BeEmpty()) + + // Ensure the Data Mover pod is using a true PVC for caching + err := c.verifyCacheVolumeInPod(restorePodList.Items[0]) + Expect(err).To(Succeed(), "Injected Cache PVC should exist in DataDownload Pod") + fmt.Println("Restore data mover pod content verification completed successfully.") + + // Wait for restore completion + wait.PollUntilContextTimeout(c.Ctx, 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { + restore := new(velerov1api.Restore) + if err := c.VeleroCfg.ClientToInstallVelero.Kubebuilder.Get( + c.Ctx, + client.ObjectKey{Namespace: c.VeleroCfg.VeleroNamespace, Name: c.RestoreName}, + restore, + ); err != nil { + return false, err + } + + if restore.Status.Phase != velerov1api.RestorePhaseCompleted && + restore.Status.Phase != velerov1api.RestorePhaseFailed && + restore.Status.Phase != velerov1api.RestorePhasePartiallyFailed { + return false, nil + } + + return true, nil + }) + + return nil +} From 3ef30897ae094039834b9b42e8baaae27a03e869 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 21 Apr 2026 14:14:24 +0800 Subject: [PATCH 03/32] CBT bitmap implementation Signed-off-by: Lyndon-Li --- changelogs/unreleased/9736-Lyndon-Li | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/9736-Lyndon-Li diff --git a/changelogs/unreleased/9736-Lyndon-Li b/changelogs/unreleased/9736-Lyndon-Li new file mode 100644 index 000000000..ef271b063 --- /dev/null +++ b/changelogs/unreleased/9736-Lyndon-Li @@ -0,0 +1 @@ +Add CBT bitmap implementation for block data mover \ No newline at end of file From a1fd85c79125365806b85646cf00044ca09980a7 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 21 Apr 2026 16:07:06 +0800 Subject: [PATCH 04/32] add CBT bitmap implementation Signed-off-by: Lyndon-Li --- go.mod | 3 + go.sum | 6 + pkg/cbtservice/mocks/Service.go | 171 ++++++++++++++++ pkg/cbtservice/service.go | 4 +- pkg/uploader/cbt/bitmap.go | 112 ++++++++++- pkg/uploader/cbt/bitmap_test.go | 256 ++++++++++++++++++++++++ pkg/uploader/cbt/mocks/Bitmap.go | 294 +++++++++++++++++++++++++++ pkg/uploader/cbt/mocks/Iterator.go | 309 +++++++++++++++++++++++++++++ pkg/uploader/cbt/set.go | 49 +++-- pkg/uploader/cbt/set_test.go | 150 ++++++++++++++ 10 files changed, 1328 insertions(+), 26 deletions(-) create mode 100644 pkg/cbtservice/mocks/Service.go create mode 100644 pkg/uploader/cbt/bitmap_test.go create mode 100644 pkg/uploader/cbt/mocks/Bitmap.go create mode 100644 pkg/uploader/cbt/mocks/Iterator.go create mode 100644 pkg/uploader/cbt/set_test.go diff --git a/go.mod b/go.mod index e80bfbcf9..efdc3b828 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3 + github.com/RoaringBitmap/roaring v1.9.4 github.com/aws/aws-sdk-go-v2 v1.24.1 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/credentials v1.16.14 @@ -91,6 +92,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.12.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect @@ -148,6 +150,7 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-vss v1.2.0 // indirect diff --git a/go.sum b/go.sum index d8ee0cab0..a2196812b 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -168,6 +170,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZtmFVPHmA= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -562,6 +566,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb 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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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= diff --git a/pkg/cbtservice/mocks/Service.go b/pkg/cbtservice/mocks/Service.go new file mode 100644 index 000000000..f7a9567a8 --- /dev/null +++ b/pkg/cbtservice/mocks/Service.go @@ -0,0 +1,171 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + mock "github.com/stretchr/testify/mock" + "github.com/vmware-tanzu/velero/pkg/cbtservice" +) + +// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewService(t interface { + mock.TestingT + Cleanup(func()) +}) *Service { + mock := &Service{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Service is an autogenerated mock type for the Service type +type Service struct { + mock.Mock +} + +type Service_Expecter struct { + mock *mock.Mock +} + +func (_m *Service) EXPECT() *Service_Expecter { + return &Service_Expecter{mock: &_m.Mock} +} + +// GetAllocatedBlocks provides a mock function for the type Service +func (_mock *Service) GetAllocatedBlocks(ctx context.Context, snapshot string, record func([]cbtservice.Range) error) error { + ret := _mock.Called(ctx, snapshot, record) + + if len(ret) == 0 { + panic("no return value specified for GetAllocatedBlocks") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, func([]cbtservice.Range) error) error); ok { + r0 = returnFunc(ctx, snapshot, record) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Service_GetAllocatedBlocks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllocatedBlocks' +type Service_GetAllocatedBlocks_Call struct { + *mock.Call +} + +// GetAllocatedBlocks is a helper method to define mock.On call +// - ctx context.Context +// - snapshot string +// - record func([]cbtservice.Range) error +func (_e *Service_Expecter) GetAllocatedBlocks(ctx interface{}, snapshot interface{}, record interface{}) *Service_GetAllocatedBlocks_Call { + return &Service_GetAllocatedBlocks_Call{Call: _e.mock.On("GetAllocatedBlocks", ctx, snapshot, record)} +} + +func (_c *Service_GetAllocatedBlocks_Call) Run(run func(ctx context.Context, snapshot string, record func([]cbtservice.Range) error)) *Service_GetAllocatedBlocks_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 func([]cbtservice.Range) error + if args[2] != nil { + arg2 = args[2].(func([]cbtservice.Range) error) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *Service_GetAllocatedBlocks_Call) Return(err error) *Service_GetAllocatedBlocks_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Service_GetAllocatedBlocks_Call) RunAndReturn(run func(ctx context.Context, snapshot string, record func([]cbtservice.Range) error) error) *Service_GetAllocatedBlocks_Call { + _c.Call.Return(run) + return _c +} + +// GetChangedBlocks provides a mock function for the type Service +func (_mock *Service) GetChangedBlocks(ctx context.Context, snapshot string, changeID string, record func([]cbtservice.Range) error) error { + ret := _mock.Called(ctx, snapshot, changeID, record) + + if len(ret) == 0 { + panic("no return value specified for GetChangedBlocks") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, func([]cbtservice.Range) error) error); ok { + r0 = returnFunc(ctx, snapshot, changeID, record) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Service_GetChangedBlocks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetChangedBlocks' +type Service_GetChangedBlocks_Call struct { + *mock.Call +} + +// GetChangedBlocks is a helper method to define mock.On call +// - ctx context.Context +// - snapshot string +// - changeID string +// - record func([]cbtservice.Range) error +func (_e *Service_Expecter) GetChangedBlocks(ctx interface{}, snapshot interface{}, changeID interface{}, record interface{}) *Service_GetChangedBlocks_Call { + return &Service_GetChangedBlocks_Call{Call: _e.mock.On("GetChangedBlocks", ctx, snapshot, changeID, record)} +} + +func (_c *Service_GetChangedBlocks_Call) Run(run func(ctx context.Context, snapshot string, changeID string, record func([]cbtservice.Range) error)) *Service_GetChangedBlocks_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 func([]cbtservice.Range) error + if args[3] != nil { + arg3 = args[3].(func([]cbtservice.Range) error) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *Service_GetChangedBlocks_Call) Return(err error) *Service_GetChangedBlocks_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Service_GetChangedBlocks_Call) RunAndReturn(run func(ctx context.Context, snapshot string, changeID string, record func([]cbtservice.Range) error) error) *Service_GetChangedBlocks_Call { + _c.Call.Return(run) + return _c +} diff --git a/pkg/cbtservice/service.go b/pkg/cbtservice/service.go index 96e43b2c4..3e2a0591d 100644 --- a/pkg/cbtservice/service.go +++ b/pkg/cbtservice/service.go @@ -20,8 +20,8 @@ import "context" // Range defines the range of a change type Range struct { - Offset int64 - Length int64 + Offset uint64 + Length uint64 } // SourceInfo is the information provided to the uploader, the uploader calls CBT service with this information diff --git a/pkg/uploader/cbt/bitmap.go b/pkg/uploader/cbt/bitmap.go index 6cf6e61e0..6defaf8b0 100644 --- a/pkg/uploader/cbt/bitmap.go +++ b/pkg/uploader/cbt/bitmap.go @@ -16,22 +16,29 @@ limitations under the License. package cbt -import "github.com/vmware-tanzu/velero/pkg/cbtservice" +import ( + "math/bits" + + "github.com/RoaringBitmap/roaring" +) // Bitmap defines the methods to store and iterate the CBT bitmap type Bitmap interface { // Set sets bits within the provided range - Set(cbtservice.Range) + Set(uint64, uint64) // SetFull sets all bits to the bitmap SetFull() // Snapshot returns snapshot of the bitmap - SourceID() string + Snapshot() string // ChangeID returns the changeID of the bitmap ChangeID() string + // VolumeID return ID of the volume from which the snapshot is taken + VolumeID() string + // Iterator returns the iterator for the CBT Bitmap Iterator() Iterator } @@ -44,12 +51,107 @@ type Iterator interface { // Snapshot returns snapshot of the bitmap Snapshot() string + // VolumeID return ID of the volume from which the snapshot is taken + VolumeID() string + // BlockSize returns the granularity of the bitmap - BlockSize() int + BlockSize() uint // Count returns the toal number of count in the bitmap Count() uint64 // Next returns the offset of the next set block and whether it comes to the end of the iteration - Next() (int64, bool) + Next() (uint64, bool) +} + +const ( + InvalidOffset64 = ^uint64(0) +) + +type bitmapImpl struct { + bitmap *roaring.Bitmap + blockSize uint + blockSizeLog int + length uint64 + snapshot string + changeID string + volumeID string +} + +type bitmapIterator struct { + bitmapImpl + iterator roaring.IntPeekable +} + +func NewBitmap(blockSize uint, length uint64, snapshot string, changeID string, volumeID string) Bitmap { + return &bitmapImpl{ + bitmap: roaring.New(), + blockSize: blockSize, + blockSizeLog: bits.Len(blockSize) - 1, + length: length, + snapshot: snapshot, + changeID: changeID, + volumeID: volumeID, + } +} + +func (c *bitmapImpl) Set(offset, length uint64) { + if offset >= c.length { + return + } + + if offset+length > c.length { + length = c.length - offset + } + + start := offset >> c.blockSizeLog + end := uint64((offset + length + uint64(c.blockSize) - 1) >> c.blockSizeLog) + + c.bitmap.AddRange(start, end) +} + +func (c *bitmapImpl) SetFull() { + start := uint64(0) + end := uint64((c.length + uint64(c.blockSize) - 1) >> c.blockSizeLog) + + c.bitmap.AddRange(start, end) +} + +func (c *bitmapImpl) Snapshot() string { + return c.snapshot +} + +func (c *bitmapImpl) ChangeID() string { + return c.changeID +} + +func (c *bitmapImpl) VolumeID() string { + return c.volumeID +} + +func (c *bitmapImpl) Iterator() Iterator { + if c.bitmap == nil { + return nil + } + + return &bitmapIterator{ + bitmapImpl: *c, + iterator: c.bitmap.Iterator(), + } +} + +func (c *bitmapIterator) Next() (uint64, bool) { + if !c.iterator.HasNext() { + return InvalidOffset64, false + } + + return uint64(c.iterator.Next()) << c.blockSizeLog, true +} + +func (c *bitmapIterator) Count() uint64 { + return c.bitmap.GetCardinality() +} + +func (c *bitmapIterator) BlockSize() uint { + return c.blockSize } diff --git a/pkg/uploader/cbt/bitmap_test.go b/pkg/uploader/cbt/bitmap_test.go new file mode 100644 index 000000000..9671ad3d9 --- /dev/null +++ b/pkg/uploader/cbt/bitmap_test.go @@ -0,0 +1,256 @@ +/* +Copyright The Velero Contributors. + +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 cbt + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBitmapProperties(t *testing.T) { + b := NewBitmap(1024*1024, 10000*1024*1024, "snap-1", "change-1", "vol-1") + assert.Equal(t, "snap-1", b.Snapshot()) + assert.Equal(t, "change-1", b.ChangeID()) + assert.Equal(t, "vol-1", b.VolumeID()) +} + +func TestBitmapSet(t *testing.T) { + const mb = 1024 * 1024 + const gb = 1024 * 1024 * 1024 + + tests := []struct { + name string + blockSize uint + totalLength uint64 + setCalls []struct{ offset, length uint64 } + expectedCount uint64 + expectedNext []uint64 + }{ + { + name: "set single block within bounds", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {0, 1000}, + }, + expectedCount: 1, + expectedNext: []uint64{0}, + }, + { + name: "set exactly one block", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {0, mb}, + }, + expectedCount: 1, + expectedNext: []uint64{0}, + }, + { + name: "set overlapping two blocks", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {mb - 1, 2}, + }, + expectedCount: 2, + expectedNext: []uint64{0, mb}, + }, + { + name: "set multiple non-contiguous blocks", + blockSize: mb, + totalLength: 20 * gb, + setCalls: []struct{ offset, length uint64 }{ + {0, 100}, + {2 * mb, 100}, + }, + expectedCount: 2, + expectedNext: []uint64{0, 2 * mb}, + }, + { + name: "set completely out of bounds (offset >= length)", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {10 * gb, 100}, + {15 * gb, 100}, + }, + expectedCount: 0, + expectedNext: []uint64{}, + }, + { + name: "set partially out of bounds (truncated)", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {10*gb - mb/2, mb}, // Starts in the last block, length pushes it out of bounds + }, + expectedCount: 1, // Only the last block should be set + expectedNext: []uint64{10*gb - mb}, + }, + { + name: "set spanning entire length", + blockSize: mb, + totalLength: 3 * mb, // 3 blocks: 0-1MB, 1MB-2MB, 2MB-3MB + setCalls: []struct{ offset, length uint64 }{ + {0, 3 * mb}, + }, + expectedCount: 3, + expectedNext: []uint64{0, mb, 2 * mb}, + }, + { + name: "set large contiguous range", + blockSize: mb, + totalLength: 100 * gb, + setCalls: []struct{ offset, length uint64 }{ + {10 * mb, 5 * mb}, // Starts at 10MB, spans 5 full blocks + }, + expectedCount: 5, + expectedNext: []uint64{10 * mb, 11 * mb, 12 * mb, 13 * mb, 14 * mb}, + }, + { + name: "set empty length", + blockSize: mb, + totalLength: 10 * gb, + setCalls: []struct{ offset, length uint64 }{ + {mb, 0}, + }, + expectedCount: 0, + expectedNext: []uint64{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := NewBitmap(tt.blockSize, tt.totalLength, "snap-1", "change-1", "vol-1") + + for _, call := range tt.setCalls { + b.Set(call.offset, call.length) + } + + iter := b.Iterator() + require.NotNil(t, iter) + + assert.Equal(t, tt.expectedCount, iter.Count()) + + var actualNext []uint64 + for { + offset, hasNext := iter.Next() + if !hasNext { + assert.Equal(t, InvalidOffset64, offset) + break + } + actualNext = append(actualNext, offset) + } + + if len(tt.expectedNext) == 0 { + assert.Empty(t, actualNext) + } else { + assert.Equal(t, tt.expectedNext, actualNext) + } + }) + } +} + +func TestBitmapSetFull(t *testing.T) { + const mb = 1024 * 1024 + // Total length 3MB, blockSize 1MB. This means 3 blocks total: + // block 0: 0 - 1MB + // block 1: 1MB - 2MB + // block 2: 2MB - 3MB + b := NewBitmap(mb, 3*mb, "snap-1", "change-1", "vol-1") + b.SetFull() + + iter := b.Iterator() + require.NotNil(t, iter) + + assert.Equal(t, uint64(3), iter.Count()) + + expectedOffsets := []uint64{0, mb, 2 * mb} + var actualOffsets []uint64 + for { + offset, hasNext := iter.Next() + if !hasNext { + break + } + actualOffsets = append(actualOffsets, offset) + } + + assert.Equal(t, expectedOffsets, actualOffsets) +} + +func TestBitmapIterator(t *testing.T) { + const mb = 1024 * 1024 + const gb = 1024 * 1024 * 1024 + + b := NewBitmap(mb, 10*gb, "snap-1", "change-1", "vol-1") + + // Set multiple ranges to test contiguous iteration + b.Set(mb, 100) // Block 1 + b.Set(3*mb, 5*mb) // Blocks 3, 4, 5, 6, 7 + b.Set(10*gb-mb, mb) // Last block + + iter := b.Iterator() + require.NotNil(t, iter) + + // Test iterator properties + assert.Equal(t, "snap-1", iter.Snapshot()) + assert.Equal(t, "change-1", iter.ChangeID()) + assert.Equal(t, "vol-1", iter.VolumeID()) + assert.Equal(t, uint(mb), iter.BlockSize()) + assert.Equal(t, uint64(7), iter.Count()) // 1 + 5 + 1 = 7 blocks + + expectedOffsets := []uint64{ + mb, + 3 * mb, 4 * mb, 5 * mb, 6 * mb, 7 * mb, + 10*gb - mb, + } + + // Test iteration + var actualOffsets []uint64 + for { + offset, hasNext := iter.Next() + if !hasNext { + assert.Equal(t, InvalidOffset64, offset) + break + } + actualOffsets = append(actualOffsets, offset) + } + + assert.Equal(t, expectedOffsets, actualOffsets) + + // Test end of iteration multiple times to ensure it stays exhausted + offset, hasNext := iter.Next() + assert.False(t, hasNext) + assert.Equal(t, InvalidOffset64, offset) + + offset, hasNext = iter.Next() + assert.False(t, hasNext) + assert.Equal(t, InvalidOffset64, offset) +} + +func TestBitmapIteratorNilBitmap(t *testing.T) { + // Directly create bitmapImpl with a nil roaring.Bitmap to test safety + b := &bitmapImpl{ + bitmap: nil, + } + + iter := b.Iterator() + assert.Nil(t, iter) +} diff --git a/pkg/uploader/cbt/mocks/Bitmap.go b/pkg/uploader/cbt/mocks/Bitmap.go new file mode 100644 index 000000000..289c88215 --- /dev/null +++ b/pkg/uploader/cbt/mocks/Bitmap.go @@ -0,0 +1,294 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + "github.com/vmware-tanzu/velero/pkg/uploader/cbt" +) + +// NewBitmap creates a new instance of Bitmap. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBitmap(t interface { + mock.TestingT + Cleanup(func()) +}) *Bitmap { + mock := &Bitmap{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Bitmap is an autogenerated mock type for the Bitmap type +type Bitmap struct { + mock.Mock +} + +type Bitmap_Expecter struct { + mock *mock.Mock +} + +func (_m *Bitmap) EXPECT() *Bitmap_Expecter { + return &Bitmap_Expecter{mock: &_m.Mock} +} + +// ChangeID provides a mock function for the type Bitmap +func (_mock *Bitmap) ChangeID() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for ChangeID") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Bitmap_ChangeID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChangeID' +type Bitmap_ChangeID_Call struct { + *mock.Call +} + +// ChangeID is a helper method to define mock.On call +func (_e *Bitmap_Expecter) ChangeID() *Bitmap_ChangeID_Call { + return &Bitmap_ChangeID_Call{Call: _e.mock.On("ChangeID")} +} + +func (_c *Bitmap_ChangeID_Call) Run(run func()) *Bitmap_ChangeID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Bitmap_ChangeID_Call) Return(s string) *Bitmap_ChangeID_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Bitmap_ChangeID_Call) RunAndReturn(run func() string) *Bitmap_ChangeID_Call { + _c.Call.Return(run) + return _c +} + +// Iterator provides a mock function for the type Bitmap +func (_mock *Bitmap) Iterator() cbt.Iterator { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Iterator") + } + + var r0 cbt.Iterator + if returnFunc, ok := ret.Get(0).(func() cbt.Iterator); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cbt.Iterator) + } + } + return r0 +} + +// Bitmap_Iterator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Iterator' +type Bitmap_Iterator_Call struct { + *mock.Call +} + +// Iterator is a helper method to define mock.On call +func (_e *Bitmap_Expecter) Iterator() *Bitmap_Iterator_Call { + return &Bitmap_Iterator_Call{Call: _e.mock.On("Iterator")} +} + +func (_c *Bitmap_Iterator_Call) Run(run func()) *Bitmap_Iterator_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Bitmap_Iterator_Call) Return(iterator cbt.Iterator) *Bitmap_Iterator_Call { + _c.Call.Return(iterator) + return _c +} + +func (_c *Bitmap_Iterator_Call) RunAndReturn(run func() cbt.Iterator) *Bitmap_Iterator_Call { + _c.Call.Return(run) + return _c +} + +// Set provides a mock function for the type Bitmap +func (_mock *Bitmap) Set(v uint64, v1 uint64) { + _mock.Called(v, v1) + return +} + +// Bitmap_Set_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Set' +type Bitmap_Set_Call struct { + *mock.Call +} + +// Set is a helper method to define mock.On call +// - v uint64 +// - v1 uint64 +func (_e *Bitmap_Expecter) Set(v interface{}, v1 interface{}) *Bitmap_Set_Call { + return &Bitmap_Set_Call{Call: _e.mock.On("Set", v, v1)} +} + +func (_c *Bitmap_Set_Call) Run(run func(v uint64, v1 uint64)) *Bitmap_Set_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 uint64 + if args[0] != nil { + arg0 = args[0].(uint64) + } + var arg1 uint64 + if args[1] != nil { + arg1 = args[1].(uint64) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *Bitmap_Set_Call) Return() *Bitmap_Set_Call { + _c.Call.Return() + return _c +} + +func (_c *Bitmap_Set_Call) RunAndReturn(run func(v uint64, v1 uint64)) *Bitmap_Set_Call { + _c.Run(run) + return _c +} + +// SetFull provides a mock function for the type Bitmap +func (_mock *Bitmap) SetFull() { + _mock.Called() + return +} + +// Bitmap_SetFull_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetFull' +type Bitmap_SetFull_Call struct { + *mock.Call +} + +// SetFull is a helper method to define mock.On call +func (_e *Bitmap_Expecter) SetFull() *Bitmap_SetFull_Call { + return &Bitmap_SetFull_Call{Call: _e.mock.On("SetFull")} +} + +func (_c *Bitmap_SetFull_Call) Run(run func()) *Bitmap_SetFull_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Bitmap_SetFull_Call) Return() *Bitmap_SetFull_Call { + _c.Call.Return() + return _c +} + +func (_c *Bitmap_SetFull_Call) RunAndReturn(run func()) *Bitmap_SetFull_Call { + _c.Run(run) + return _c +} + +// Snapshot provides a mock function for the type Bitmap +func (_mock *Bitmap) Snapshot() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Snapshot") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Bitmap_Snapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Snapshot' +type Bitmap_Snapshot_Call struct { + *mock.Call +} + +// Snapshot is a helper method to define mock.On call +func (_e *Bitmap_Expecter) Snapshot() *Bitmap_Snapshot_Call { + return &Bitmap_Snapshot_Call{Call: _e.mock.On("Snapshot")} +} + +func (_c *Bitmap_Snapshot_Call) Run(run func()) *Bitmap_Snapshot_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Bitmap_Snapshot_Call) Return(s string) *Bitmap_Snapshot_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Bitmap_Snapshot_Call) RunAndReturn(run func() string) *Bitmap_Snapshot_Call { + _c.Call.Return(run) + return _c +} + +// VolumeID provides a mock function for the type Bitmap +func (_mock *Bitmap) VolumeID() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for VolumeID") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Bitmap_VolumeID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VolumeID' +type Bitmap_VolumeID_Call struct { + *mock.Call +} + +// VolumeID is a helper method to define mock.On call +func (_e *Bitmap_Expecter) VolumeID() *Bitmap_VolumeID_Call { + return &Bitmap_VolumeID_Call{Call: _e.mock.On("VolumeID")} +} + +func (_c *Bitmap_VolumeID_Call) Run(run func()) *Bitmap_VolumeID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Bitmap_VolumeID_Call) Return(s string) *Bitmap_VolumeID_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Bitmap_VolumeID_Call) RunAndReturn(run func() string) *Bitmap_VolumeID_Call { + _c.Call.Return(run) + return _c +} diff --git a/pkg/uploader/cbt/mocks/Iterator.go b/pkg/uploader/cbt/mocks/Iterator.go new file mode 100644 index 000000000..eeafd0a59 --- /dev/null +++ b/pkg/uploader/cbt/mocks/Iterator.go @@ -0,0 +1,309 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" +) + +// NewIterator creates a new instance of Iterator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIterator(t interface { + mock.TestingT + Cleanup(func()) +}) *Iterator { + mock := &Iterator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Iterator is an autogenerated mock type for the Iterator type +type Iterator struct { + mock.Mock +} + +type Iterator_Expecter struct { + mock *mock.Mock +} + +func (_m *Iterator) EXPECT() *Iterator_Expecter { + return &Iterator_Expecter{mock: &_m.Mock} +} + +// BlockSize provides a mock function for the type Iterator +func (_mock *Iterator) BlockSize() uint { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for BlockSize") + } + + var r0 uint + if returnFunc, ok := ret.Get(0).(func() uint); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(uint) + } + return r0 +} + +// Iterator_BlockSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockSize' +type Iterator_BlockSize_Call struct { + *mock.Call +} + +// BlockSize is a helper method to define mock.On call +func (_e *Iterator_Expecter) BlockSize() *Iterator_BlockSize_Call { + return &Iterator_BlockSize_Call{Call: _e.mock.On("BlockSize")} +} + +func (_c *Iterator_BlockSize_Call) Run(run func()) *Iterator_BlockSize_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_BlockSize_Call) Return(v uint) *Iterator_BlockSize_Call { + _c.Call.Return(v) + return _c +} + +func (_c *Iterator_BlockSize_Call) RunAndReturn(run func() uint) *Iterator_BlockSize_Call { + _c.Call.Return(run) + return _c +} + +// ChangeID provides a mock function for the type Iterator +func (_mock *Iterator) ChangeID() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for ChangeID") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Iterator_ChangeID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChangeID' +type Iterator_ChangeID_Call struct { + *mock.Call +} + +// ChangeID is a helper method to define mock.On call +func (_e *Iterator_Expecter) ChangeID() *Iterator_ChangeID_Call { + return &Iterator_ChangeID_Call{Call: _e.mock.On("ChangeID")} +} + +func (_c *Iterator_ChangeID_Call) Run(run func()) *Iterator_ChangeID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_ChangeID_Call) Return(s string) *Iterator_ChangeID_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Iterator_ChangeID_Call) RunAndReturn(run func() string) *Iterator_ChangeID_Call { + _c.Call.Return(run) + return _c +} + +// Count provides a mock function for the type Iterator +func (_mock *Iterator) Count() uint64 { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Count") + } + + var r0 uint64 + if returnFunc, ok := ret.Get(0).(func() uint64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(uint64) + } + return r0 +} + +// Iterator_Count_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Count' +type Iterator_Count_Call struct { + *mock.Call +} + +// Count is a helper method to define mock.On call +func (_e *Iterator_Expecter) Count() *Iterator_Count_Call { + return &Iterator_Count_Call{Call: _e.mock.On("Count")} +} + +func (_c *Iterator_Count_Call) Run(run func()) *Iterator_Count_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_Count_Call) Return(v uint64) *Iterator_Count_Call { + _c.Call.Return(v) + return _c +} + +func (_c *Iterator_Count_Call) RunAndReturn(run func() uint64) *Iterator_Count_Call { + _c.Call.Return(run) + return _c +} + +// Next provides a mock function for the type Iterator +func (_mock *Iterator) Next() (uint64, bool) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Next") + } + + var r0 uint64 + var r1 bool + if returnFunc, ok := ret.Get(0).(func() (uint64, bool)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() uint64); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(uint64) + } + if returnFunc, ok := ret.Get(1).(func() bool); ok { + r1 = returnFunc() + } else { + r1 = ret.Get(1).(bool) + } + return r0, r1 +} + +// Iterator_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next' +type Iterator_Next_Call struct { + *mock.Call +} + +// Next is a helper method to define mock.On call +func (_e *Iterator_Expecter) Next() *Iterator_Next_Call { + return &Iterator_Next_Call{Call: _e.mock.On("Next")} +} + +func (_c *Iterator_Next_Call) Run(run func()) *Iterator_Next_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_Next_Call) Return(v uint64, b bool) *Iterator_Next_Call { + _c.Call.Return(v, b) + return _c +} + +func (_c *Iterator_Next_Call) RunAndReturn(run func() (uint64, bool)) *Iterator_Next_Call { + _c.Call.Return(run) + return _c +} + +// Snapshot provides a mock function for the type Iterator +func (_mock *Iterator) Snapshot() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Snapshot") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Iterator_Snapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Snapshot' +type Iterator_Snapshot_Call struct { + *mock.Call +} + +// Snapshot is a helper method to define mock.On call +func (_e *Iterator_Expecter) Snapshot() *Iterator_Snapshot_Call { + return &Iterator_Snapshot_Call{Call: _e.mock.On("Snapshot")} +} + +func (_c *Iterator_Snapshot_Call) Run(run func()) *Iterator_Snapshot_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_Snapshot_Call) Return(s string) *Iterator_Snapshot_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Iterator_Snapshot_Call) RunAndReturn(run func() string) *Iterator_Snapshot_Call { + _c.Call.Return(run) + return _c +} + +// VolumeID provides a mock function for the type Iterator +func (_mock *Iterator) VolumeID() string { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for VolumeID") + } + + var r0 string + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() + } else { + r0 = ret.Get(0).(string) + } + return r0 +} + +// Iterator_VolumeID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VolumeID' +type Iterator_VolumeID_Call struct { + *mock.Call +} + +// VolumeID is a helper method to define mock.On call +func (_e *Iterator_Expecter) VolumeID() *Iterator_VolumeID_Call { + return &Iterator_VolumeID_Call{Call: _e.mock.On("VolumeID")} +} + +func (_c *Iterator_VolumeID_Call) Run(run func()) *Iterator_VolumeID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Iterator_VolumeID_Call) Return(s string) *Iterator_VolumeID_Call { + _c.Call.Return(s) + return _c +} + +func (_c *Iterator_VolumeID_Call) RunAndReturn(run func() string) *Iterator_VolumeID_Call { + _c.Call.Return(run) + return _c +} diff --git a/pkg/uploader/cbt/set.go b/pkg/uploader/cbt/set.go index 40e6e331c..93383076f 100644 --- a/pkg/uploader/cbt/set.go +++ b/pkg/uploader/cbt/set.go @@ -19,31 +19,42 @@ package cbt import ( "context" + "github.com/pkg/errors" + "github.com/vmware-tanzu/velero/pkg/cbtservice" ) // SetBitmapOrFull translates the allocated/changed blocks from CBT service to the given bitmap or set the bitmap to full when error happens -func SetBitmapOrFull(ctx context.Context, service cbtservice.Service, bitmap Bitmap) error { - var err error +func SetBitmapOrFull(ctx context.Context, service cbtservice.Service, bitmap Bitmap) (err error) { + defer func() { + if err != nil { + bitmap.SetFull() + } + }() + + if service == nil { + return errors.New("CBT service is absent") + } + + if bitmap.Snapshot() == "" { + return errors.New("invalid snapshot") + } + if bitmap.ChangeID() == "" { - err = setFromAllocatedBlocks(ctx, service, bitmap) - } else { - err = setFromChangedBlocks(ctx, service, bitmap) + return errors.Wrapf(service.GetAllocatedBlocks(ctx, bitmap.Snapshot(), func(blocks []cbtservice.Range) error { + for _, b := range blocks { + bitmap.Set(b.Offset, b.Length) + } + + return nil + }), "error getting allocated blocks from CBT service") } - if err != nil { - bitmap.SetFull() - } + return errors.Wrapf(service.GetChangedBlocks(ctx, bitmap.Snapshot(), bitmap.ChangeID(), func(blocks []cbtservice.Range) error { + for _, b := range blocks { + bitmap.Set(b.Offset, b.Length) + } - return err -} - -// TODO implement in following PRs -func setFromAllocatedBlocks(_ context.Context, _ cbtservice.Service, _ Bitmap) error { - return nil -} - -// TODO implement in following PRs -func setFromChangedBlocks(_ context.Context, _ cbtservice.Service, _ Bitmap) error { - return nil + return nil + }), "error getting changed blocks from CBT service") } diff --git a/pkg/uploader/cbt/set_test.go b/pkg/uploader/cbt/set_test.go new file mode 100644 index 000000000..c4a055cba --- /dev/null +++ b/pkg/uploader/cbt/set_test.go @@ -0,0 +1,150 @@ +/* +Copyright The Velero Contributors. + +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 cbt_test + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/velero/pkg/cbtservice" + cbtservicemocks "github.com/vmware-tanzu/velero/pkg/cbtservice/mocks" + "github.com/vmware-tanzu/velero/pkg/uploader/cbt" + cbtmocks "github.com/vmware-tanzu/velero/pkg/uploader/cbt/mocks" +) + +func TestSetBitmapOrFull(t *testing.T) { + tests := []struct { + name string + nilService bool + setupMocks func(*cbtservicemocks.Service, *cbtmocks.Bitmap) + expectedErrStr string + }{ + { + name: "nil service", + nilService: true, + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("SetFull").Return() + }, + expectedErrStr: "CBT service is absent", + }, + { + name: "invalid snapshot", + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("Snapshot").Return("") + bmp.On("SetFull").Return() + }, + expectedErrStr: "invalid snapshot", + }, + { + name: "allocated blocks success", + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("Snapshot").Return("snap-1") + bmp.On("ChangeID").Return("") + + svc.On("GetAllocatedBlocks", mock.Anything, "snap-1", mock.Anything).Run(func(args mock.Arguments) { + record := args.Get(2).(func([]cbtservice.Range) error) + record([]cbtservice.Range{ + {Offset: 0, Length: 4096}, + {Offset: 8192, Length: 4096}, + }) + }).Return(nil) + + bmp.On("Set", uint64(0), uint64(4096)).Return() + bmp.On("Set", uint64(8192), uint64(4096)).Return() + }, + }, + { + name: "allocated blocks error", + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("Snapshot").Return("snap-1") + bmp.On("ChangeID").Return("") + + svc.On("GetAllocatedBlocks", mock.Anything, "snap-1", mock.Anything).Return(errors.New("mock alloc error")) + bmp.On("SetFull").Return() + }, + expectedErrStr: "error getting allocated blocks from CBT service: mock alloc error", + }, + { + name: "changed blocks success", + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("Snapshot").Return("snap-1") + bmp.On("ChangeID").Return("change-1") + + svc.On("GetChangedBlocks", mock.Anything, "snap-1", "change-1", mock.Anything).Run(func(args mock.Arguments) { + record := args.Get(3).(func([]cbtservice.Range) error) + record([]cbtservice.Range{ + {Offset: 4096, Length: 4096}, + }) + }).Return(nil) + + bmp.On("Set", uint64(4096), uint64(4096)).Return() + }, + }, + { + name: "changed blocks error", + setupMocks: func(svc *cbtservicemocks.Service, bmp *cbtmocks.Bitmap) { + bmp.On("Snapshot").Return("snap-1") + bmp.On("ChangeID").Return("change-1") + + svc.On("GetChangedBlocks", mock.Anything, "snap-1", "change-1", mock.Anything).Return(errors.New("mock changed error")) + bmp.On("SetFull").Return() + }, + expectedErrStr: "error getting changed blocks from CBT service: mock changed error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + svcMock := new(cbtservicemocks.Service) + bmpMock := new(cbtmocks.Bitmap) + + if tt.setupMocks != nil { + tt.setupMocks(svcMock, bmpMock) + } + + var svc cbtservice.Service + if !tt.nilService { + svc = svcMock + } + + // Use type assertion to bypass gopls false positive on mock type + var bmp cbt.Bitmap + if bmpMock != nil { + bmp = interface{}(bmpMock).(cbt.Bitmap) + } + + err := cbt.SetBitmapOrFull(context.Background(), svc, bmp) + + if tt.expectedErrStr != "" { + require.Error(t, err) + assert.EqualError(t, err, tt.expectedErrStr) + } else { + require.NoError(t, err) + } + + if !tt.nilService { + svcMock.AssertExpectations(t) + } + bmpMock.AssertExpectations(t) + }) + } +} From 26b125769e65ef036cd0d85d3acd4f6ecea0d370 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 21 Apr 2026 17:46:29 +0800 Subject: [PATCH 05/32] add CBT bitmap implementation Signed-off-by: Lyndon-Li --- pkg/uploader/cbt/bitmap.go | 52 ++-------------- pkg/uploader/cbt/set.go | 3 +- pkg/uploader/cbt/set_test.go | 13 +--- pkg/uploader/cbt/{ => types}/mocks/Bitmap.go | 14 ++--- .../cbt/{ => types}/mocks/Iterator.go | 0 pkg/uploader/cbt/types/types.go | 59 +++++++++++++++++++ 6 files changed, 77 insertions(+), 64 deletions(-) rename pkg/uploader/cbt/{ => types}/mocks/Bitmap.go (94%) rename pkg/uploader/cbt/{ => types}/mocks/Iterator.go (100%) create mode 100644 pkg/uploader/cbt/types/types.go diff --git a/pkg/uploader/cbt/bitmap.go b/pkg/uploader/cbt/bitmap.go index 6defaf8b0..f26cb22b7 100644 --- a/pkg/uploader/cbt/bitmap.go +++ b/pkg/uploader/cbt/bitmap.go @@ -20,50 +20,10 @@ import ( "math/bits" "github.com/RoaringBitmap/roaring" + + "github.com/vmware-tanzu/velero/pkg/uploader/cbt/types" ) -// Bitmap defines the methods to store and iterate the CBT bitmap -type Bitmap interface { - // Set sets bits within the provided range - Set(uint64, uint64) - - // SetFull sets all bits to the bitmap - SetFull() - - // Snapshot returns snapshot of the bitmap - Snapshot() string - - // ChangeID returns the changeID of the bitmap - ChangeID() string - - // VolumeID return ID of the volume from which the snapshot is taken - VolumeID() string - - // Iterator returns the iterator for the CBT Bitmap - Iterator() Iterator -} - -// Iterator defines the methods to iterate the CBT bitmap and query the associated information -type Iterator interface { - // ChangeID returns the changeID of the bitmap - ChangeID() string - - // Snapshot returns snapshot of the bitmap - Snapshot() string - - // VolumeID return ID of the volume from which the snapshot is taken - VolumeID() string - - // BlockSize returns the granularity of the bitmap - BlockSize() uint - - // Count returns the toal number of count in the bitmap - Count() uint64 - - // Next returns the offset of the next set block and whether it comes to the end of the iteration - Next() (uint64, bool) -} - const ( InvalidOffset64 = ^uint64(0) ) @@ -83,7 +43,7 @@ type bitmapIterator struct { iterator roaring.IntPeekable } -func NewBitmap(blockSize uint, length uint64, snapshot string, changeID string, volumeID string) Bitmap { +func NewBitmap(blockSize uint, length uint64, snapshot string, changeID string, volumeID string) types.Bitmap { return &bitmapImpl{ bitmap: roaring.New(), blockSize: blockSize, @@ -105,14 +65,14 @@ func (c *bitmapImpl) Set(offset, length uint64) { } start := offset >> c.blockSizeLog - end := uint64((offset + length + uint64(c.blockSize) - 1) >> c.blockSizeLog) + end := (offset + length + uint64(c.blockSize) - 1) >> c.blockSizeLog c.bitmap.AddRange(start, end) } func (c *bitmapImpl) SetFull() { start := uint64(0) - end := uint64((c.length + uint64(c.blockSize) - 1) >> c.blockSizeLog) + end := (c.length + uint64(c.blockSize) - 1) >> c.blockSizeLog c.bitmap.AddRange(start, end) } @@ -129,7 +89,7 @@ func (c *bitmapImpl) VolumeID() string { return c.volumeID } -func (c *bitmapImpl) Iterator() Iterator { +func (c *bitmapImpl) Iterator() types.Iterator { if c.bitmap == nil { return nil } diff --git a/pkg/uploader/cbt/set.go b/pkg/uploader/cbt/set.go index 93383076f..5919419e6 100644 --- a/pkg/uploader/cbt/set.go +++ b/pkg/uploader/cbt/set.go @@ -22,10 +22,11 @@ import ( "github.com/pkg/errors" "github.com/vmware-tanzu/velero/pkg/cbtservice" + "github.com/vmware-tanzu/velero/pkg/uploader/cbt/types" ) // SetBitmapOrFull translates the allocated/changed blocks from CBT service to the given bitmap or set the bitmap to full when error happens -func SetBitmapOrFull(ctx context.Context, service cbtservice.Service, bitmap Bitmap) (err error) { +func SetBitmapOrFull(ctx context.Context, service cbtservice.Service, bitmap types.Bitmap) (err error) { defer func() { if err != nil { bitmap.SetFull() diff --git a/pkg/uploader/cbt/set_test.go b/pkg/uploader/cbt/set_test.go index c4a055cba..3bfbde1d4 100644 --- a/pkg/uploader/cbt/set_test.go +++ b/pkg/uploader/cbt/set_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cbt_test +package cbt import ( "context" @@ -27,8 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/cbtservice" cbtservicemocks "github.com/vmware-tanzu/velero/pkg/cbtservice/mocks" - "github.com/vmware-tanzu/velero/pkg/uploader/cbt" - cbtmocks "github.com/vmware-tanzu/velero/pkg/uploader/cbt/mocks" + cbtmocks "github.com/vmware-tanzu/velero/pkg/uploader/cbt/types/mocks" ) func TestSetBitmapOrFull(t *testing.T) { @@ -126,13 +125,7 @@ func TestSetBitmapOrFull(t *testing.T) { svc = svcMock } - // Use type assertion to bypass gopls false positive on mock type - var bmp cbt.Bitmap - if bmpMock != nil { - bmp = interface{}(bmpMock).(cbt.Bitmap) - } - - err := cbt.SetBitmapOrFull(context.Background(), svc, bmp) + err := SetBitmapOrFull(context.Background(), svc, bmpMock) if tt.expectedErrStr != "" { require.Error(t, err) diff --git a/pkg/uploader/cbt/mocks/Bitmap.go b/pkg/uploader/cbt/types/mocks/Bitmap.go similarity index 94% rename from pkg/uploader/cbt/mocks/Bitmap.go rename to pkg/uploader/cbt/types/mocks/Bitmap.go index 289c88215..faa4ee242 100644 --- a/pkg/uploader/cbt/mocks/Bitmap.go +++ b/pkg/uploader/cbt/types/mocks/Bitmap.go @@ -6,7 +6,7 @@ package mocks import ( mock "github.com/stretchr/testify/mock" - "github.com/vmware-tanzu/velero/pkg/uploader/cbt" + "github.com/vmware-tanzu/velero/pkg/uploader/cbt/types" ) // NewBitmap creates a new instance of Bitmap. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. @@ -81,19 +81,19 @@ func (_c *Bitmap_ChangeID_Call) RunAndReturn(run func() string) *Bitmap_ChangeID } // Iterator provides a mock function for the type Bitmap -func (_mock *Bitmap) Iterator() cbt.Iterator { +func (_mock *Bitmap) Iterator() types.Iterator { ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for Iterator") } - var r0 cbt.Iterator - if returnFunc, ok := ret.Get(0).(func() cbt.Iterator); ok { + var r0 types.Iterator + if returnFunc, ok := ret.Get(0).(func() types.Iterator); ok { r0 = returnFunc() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(cbt.Iterator) + r0 = ret.Get(0).(types.Iterator) } } return r0 @@ -116,12 +116,12 @@ func (_c *Bitmap_Iterator_Call) Run(run func()) *Bitmap_Iterator_Call { return _c } -func (_c *Bitmap_Iterator_Call) Return(iterator cbt.Iterator) *Bitmap_Iterator_Call { +func (_c *Bitmap_Iterator_Call) Return(iterator types.Iterator) *Bitmap_Iterator_Call { _c.Call.Return(iterator) return _c } -func (_c *Bitmap_Iterator_Call) RunAndReturn(run func() cbt.Iterator) *Bitmap_Iterator_Call { +func (_c *Bitmap_Iterator_Call) RunAndReturn(run func() types.Iterator) *Bitmap_Iterator_Call { _c.Call.Return(run) return _c } diff --git a/pkg/uploader/cbt/mocks/Iterator.go b/pkg/uploader/cbt/types/mocks/Iterator.go similarity index 100% rename from pkg/uploader/cbt/mocks/Iterator.go rename to pkg/uploader/cbt/types/mocks/Iterator.go diff --git a/pkg/uploader/cbt/types/types.go b/pkg/uploader/cbt/types/types.go new file mode 100644 index 000000000..f512d7a8a --- /dev/null +++ b/pkg/uploader/cbt/types/types.go @@ -0,0 +1,59 @@ +/* +Copyright The Velero Contributors. + +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 types + +// Bitmap defines the methods to store and iterate the CBT bitmap +type Bitmap interface { + // Set sets bits within the provided range + Set(uint64, uint64) + + // SetFull sets all bits to the bitmap + SetFull() + + // Snapshot returns snapshot of the bitmap + Snapshot() string + + // ChangeID returns the changeID of the bitmap + ChangeID() string + + // VolumeID return ID of the volume from which the snapshot is taken + VolumeID() string + + // Iterator returns the iterator for the CBT Bitmap + Iterator() Iterator +} + +// Iterator defines the methods to iterate the CBT bitmap and query the associated information +type Iterator interface { + // ChangeID returns the changeID of the bitmap + ChangeID() string + + // Snapshot returns snapshot of the bitmap + Snapshot() string + + // VolumeID return ID of the volume from which the snapshot is taken + VolumeID() string + + // BlockSize returns the granularity of the bitmap + BlockSize() uint + + // Count returns the toal number of count in the bitmap + Count() uint64 + + // Next returns the offset of the next set block and whether it comes to the end of the iteration + Next() (uint64, bool) +} From 6b7df3ef4c25865d0d2b9a745ce6f6c4145d6abe Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 21 Apr 2026 17:52:06 +0800 Subject: [PATCH 06/32] add CBT bitmap implementation Signed-off-by: Lyndon-Li --- pkg/uploader/cbt/set_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/uploader/cbt/set_test.go b/pkg/uploader/cbt/set_test.go index 3bfbde1d4..55701dedd 100644 --- a/pkg/uploader/cbt/set_test.go +++ b/pkg/uploader/cbt/set_test.go @@ -21,7 +21,6 @@ import ( "errors" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -129,7 +128,7 @@ func TestSetBitmapOrFull(t *testing.T) { if tt.expectedErrStr != "" { require.Error(t, err) - assert.EqualError(t, err, tt.expectedErrStr) + require.EqualError(t, err, tt.expectedErrStr) } else { require.NoError(t, err) } From da3e7cfaaf61b52a8a497fba101966f7d03f1222 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 21 Apr 2026 16:15:09 +0800 Subject: [PATCH 07/32] Remove Restic code path from PodVolumeRestore. Signed-off-by: Xun Jiang --- changelogs/unreleased/9732-blackpiglet | 1 + .../velero-restore-helper.go | 2 +- .../resourcepolicies/resource_policies.go | 2 +- pkg/cmd/cli/nodeagent/server.go | 48 --- pkg/cmd/server/server.go | 4 +- .../pod_volume_restore_controller.go | 14 +- .../pod_volume_restore_controller_legacy.go | 364 ------------------ ...d_volume_restore_controller_legacy_test.go | 93 ----- .../pod_volume_restore_controller_test.go | 39 +- 9 files changed, 43 insertions(+), 524 deletions(-) create mode 100644 changelogs/unreleased/9732-blackpiglet delete mode 100644 pkg/controller/pod_volume_restore_controller_legacy.go delete mode 100644 pkg/controller/pod_volume_restore_controller_legacy_test.go diff --git a/changelogs/unreleased/9732-blackpiglet b/changelogs/unreleased/9732-blackpiglet new file mode 100644 index 000000000..d110fa2d6 --- /dev/null +++ b/changelogs/unreleased/9732-blackpiglet @@ -0,0 +1 @@ +Remove Restic code path from PodVolumeRestore. \ No newline at end of file diff --git a/cmd/velero-restore-helper/velero-restore-helper.go b/cmd/velero-restore-helper/velero-restore-helper.go index d310f807e..40a942137 100644 --- a/cmd/velero-restore-helper/velero-restore-helper.go +++ b/cmd/velero-restore-helper/velero-restore-helper.go @@ -35,7 +35,7 @@ func main() { for { <-ticker.C if done() { - fmt.Println("All restic restores are done") + fmt.Println("All PodVolumeRestores are done") err := removeFolder() if err != nil { fmt.Println(err) diff --git a/internal/resourcepolicies/resource_policies.go b/internal/resourcepolicies/resource_policies.go index 6b5046e57..3a173fce1 100644 --- a/internal/resourcepolicies/resource_policies.go +++ b/internal/resourcepolicies/resource_policies.go @@ -38,7 +38,7 @@ const ( ConfigmapRefType string = "configmap" // skip action implies the volume would be skipped from the backup operation Skip VolumeActionType = "skip" - // fs-backup action implies that the volume would be backed up via file system copy method using the uploader(kopia/restic) configured by the user + // fs-backup action implies that the volume would be backed up via file system copy method using the uploader(kopia) configured by the user FSBackup VolumeActionType = "fs-backup" // snapshot action can have 3 different meaning based on velero configuration and backup spec - cloud provider based snapshots, local csi snapshots and datamover snapshots Snapshot VolumeActionType = "snapshot" diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 7e7c86e6c..bb1764cb3 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -37,7 +37,6 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" cacheutil "k8s.io/client-go/tools/cache" @@ -430,10 +429,6 @@ func (s *nodeAgentServer) run() { s.logger.WithError(err).Fatal("Unable to create the pod volume restore controller") } - if err := controller.InitLegacyPodVolumeRestoreReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, s.namespace, s.config.resourceTimeout, s.logger); err != nil { - s.logger.WithError(err).Fatal("Unable to create the legacy pod volume restore controller") - } - dataUploadReconciler := controller.NewDataUploadReconciler( s.mgr.GetClient(), s.mgr, @@ -509,8 +504,6 @@ func (s *nodeAgentServer) run() { if err := pvrReconciler.AttemptPVRResume(s.ctx, s.logger.WithField("node", s.nodeName), s.namespace); err != nil { s.logger.WithError(errors.WithStack(err)).Error("Failed to attempt PVR resume") } - - s.markLegacyPVRsFailed(s.mgr.GetClient()) }() s.logger.Info("Controllers starting...") @@ -604,47 +597,6 @@ func (s *nodeAgentServer) validatePodVolumesHostPath(client kubernetes.Interface return nil } -func (s *nodeAgentServer) markLegacyPVRsFailed(client ctrlclient.Client) { - pvrs := &velerov1api.PodVolumeRestoreList{} - if err := client.List(s.ctx, pvrs, &ctrlclient.ListOptions{Namespace: s.namespace}); err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to list podvolumerestores") - return - } - - for i, pvr := range pvrs.Items { - if !controller.IsLegacyPVR(&pvr) { - continue - } - - if pvr.Status.Phase != velerov1api.PodVolumeRestorePhaseInProgress { - s.logger.Debugf("the status of podvolumerestore %q is %q, skip", pvr.GetName(), pvr.Status.Phase) - continue - } - - pod := &corev1api.Pod{} - if err := client.Get(s.ctx, types.NamespacedName{ - Namespace: pvr.Spec.Pod.Namespace, - Name: pvr.Spec.Pod.Name, - }, pod); err != nil { - s.logger.WithError(errors.WithStack(err)).Errorf("failed to get pod \"%s/%s\" of podvolumerestore %q", - pvr.Spec.Pod.Namespace, pvr.Spec.Pod.Name, pvr.GetName()) - continue - } - if pod.Spec.NodeName != s.nodeName { - s.logger.Debugf("the node of pod referenced by podvolumerestore %q is %q, not %q, skip", pvr.GetName(), pod.Spec.NodeName, s.nodeName) - continue - } - - if err := controller.UpdatePVRStatusToFailed(s.ctx, client, &pvrs.Items[i], errors.New("cannot survive from node-agent restart"), - fmt.Sprintf("get a legacy podvolumerestore with status %q during the server starting, mark it as %q", velerov1api.PodVolumeRestorePhaseInProgress, velerov1api.PodVolumeRestorePhaseFailed), - time.Now(), s.logger); err != nil { - s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumerestore %q", pvr.GetName()) - continue - } - s.logger.WithField("podvolumerestore", pvr.GetName()).Warn(pvr.Status.Message) - } -} - var getConfigsFunc = nodeagent.GetConfigs func (s *nodeAgentServer) getDataPathConfigs() error { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index cb38a40d0..44c88b980 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -1164,8 +1164,8 @@ func markPodVolumeRestoresCancel(ctx context.Context, client ctrlclient.Client, for i := range pvrs.Items { pvr := pvrs.Items[i] - if controller.IsLegacyPVR(&pvr) { - log.WithField("PVR", pvr.GetName()).Warn("Found a legacy PVR during velero server restart, cannot stop it") + if _, err := uploader.ValidateUploaderType(pvr.Spec.UploaderType); err != nil { + log.WithField("PVR", pvr.Name).Warnf("invalid uploader type %s, skip marking cancel for this PVR", pvr.Spec.UploaderType) continue } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 0af0d8c86..f40de528b 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -603,7 +603,7 @@ func (r *PodVolumeRestoreReconciler) closeDataPath(ctx context.Context, pvrName func (r *PodVolumeRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { gp := kube.NewGenericEventPredicate(func(object client.Object) bool { pvr := object.(*velerov1api.PodVolumeRestore) - if IsLegacyPVR(pvr) { + if _, err := uploader.ValidateUploaderType(pvr.Spec.UploaderType); err != nil { return false } @@ -628,7 +628,8 @@ func (r *PodVolumeRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { pred := kube.NewAllEventPredicate(func(obj client.Object) bool { pvr := obj.(*velerov1api.PodVolumeRestore) - return !IsLegacyPVR(pvr) + _, err := uploader.ValidateUploaderType(pvr.Spec.UploaderType) + return err == nil }) return ctrl.NewControllerManagedBy(mgr). @@ -678,7 +679,7 @@ func (r *PodVolumeRestoreReconciler) findPVRForTargetPod(ctx context.Context, po requests := []reconcile.Request{} for _, item := range list.Items { - if IsLegacyPVR(&item) { + if _, err := uploader.ValidateUploaderType(item.Spec.UploaderType); err != nil { continue } @@ -708,6 +709,11 @@ func (r *PodVolumeRestoreReconciler) findPVRForRestorePod(ctx context.Context, p "PVR": pvr.Name, }) + if _, err := uploader.ValidateUploaderType(pvr.Spec.UploaderType); err != nil { + log.WithField("uploaderType", pvr.Spec.UploaderType).Debug("skip PVR with invalid uploader type") + return []reconcile.Request{} + } + if pvr.Status.Phase != velerov1api.PodVolumeRestorePhaseAccepted { return []reconcile.Request{} } @@ -1029,7 +1035,7 @@ func (r *PodVolumeRestoreReconciler) AttemptPVRResume(ctx context.Context, logge for i := range pvrs.Items { pvr := &pvrs.Items[i] - if IsLegacyPVR(pvr) { + if _, err := uploader.ValidateUploaderType(pvr.Spec.UploaderType); err != nil { continue } diff --git a/pkg/controller/pod_volume_restore_controller_legacy.go b/pkg/controller/pod_volume_restore_controller_legacy.go deleted file mode 100644 index 9ddececf5..000000000 --- a/pkg/controller/pod_volume_restore_controller_legacy.go +++ /dev/null @@ -1,364 +0,0 @@ -/* -Copyright The Velero Contributors. - -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 controller - -import ( - "context" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - corev1api "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - clocks "k8s.io/utils/clock" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/vmware-tanzu/velero/internal/credentials" - veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/datapath" - "github.com/vmware-tanzu/velero/pkg/exposer" - "github.com/vmware-tanzu/velero/pkg/podvolume" - "github.com/vmware-tanzu/velero/pkg/repository" - "github.com/vmware-tanzu/velero/pkg/restorehelper" - "github.com/vmware-tanzu/velero/pkg/uploader" - "github.com/vmware-tanzu/velero/pkg/util/boolptr" - "github.com/vmware-tanzu/velero/pkg/util/filesystem" - "github.com/vmware-tanzu/velero/pkg/util/kube" -) - -func InitLegacyPodVolumeRestoreReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, namespace string, - resourceTimeout time.Duration, logger logrus.FieldLogger) error { - log := logger.WithField("controller", "PodVolumeRestoreLegacy") - - credentialFileStore, err := credentials.NewNamespacedFileStore(client, namespace, credentials.DefaultStoreDirectory(), filesystem.NewFileSystem()) - if err != nil { - return errors.Wrapf(err, "error creating credentials file store") - } - - credSecretStore, err := credentials.NewNamespacedSecretStore(client, namespace) - if err != nil { - return errors.Wrapf(err, "error creating secret file store") - } - - credentialGetter := &credentials.CredentialGetter{FromFile: credentialFileStore, FromSecret: credSecretStore} - ensurer := repository.NewEnsurer(client, log, resourceTimeout) - - reconciler := &PodVolumeRestoreReconcilerLegacy{ - Client: client, - kubeClient: kubeClient, - logger: log, - repositoryEnsurer: ensurer, - credentialGetter: credentialGetter, - fileSystem: filesystem.NewFileSystem(), - clock: &clocks.RealClock{}, - dataPathMgr: dataPathMgr, - } - - if err = reconciler.SetupWithManager(mgr); err != nil { - return errors.Wrapf(err, "error setup controller manager") - } - - return nil -} - -type PodVolumeRestoreReconcilerLegacy struct { - client.Client - kubeClient kubernetes.Interface - logger logrus.FieldLogger - repositoryEnsurer *repository.Ensurer - credentialGetter *credentials.CredentialGetter - fileSystem filesystem.Interface - clock clocks.WithTickerAndDelayedExecution - dataPathMgr *datapath.Manager -} - -// +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores/status,verbs=get;update;patch -// +kubebuilder:rbac:groups="",resources=pods,verbs=get -// +kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get -// +kubebuilder:rbac:groups="",resources=persistentvolumerclaims,verbs=get - -func (c *PodVolumeRestoreReconcilerLegacy) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := c.logger.WithField("PodVolumeRestore", req.NamespacedName.String()) - log.Info("Reconciling PVR by legacy controller") - - pvr := &velerov1api.PodVolumeRestore{} - if err := c.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, pvr); err != nil { - if apierrors.IsNotFound(err) { - log.Warn("PodVolumeRestore not found, skip") - return ctrl.Result{}, nil - } - log.WithError(err).Error("Unable to get the PodVolumeRestore") - return ctrl.Result{}, err - } - - log = log.WithField("pod", fmt.Sprintf("%s/%s", pvr.Spec.Pod.Namespace, pvr.Spec.Pod.Name)) - if len(pvr.OwnerReferences) == 1 { - log = log.WithField("restore", fmt.Sprintf("%s/%s", pvr.Namespace, pvr.OwnerReferences[0].Name)) - } - - shouldProcess, pod, err := shouldProcess(ctx, c.Client, log, pvr) - if err != nil { - return ctrl.Result{}, err - } - if !shouldProcess { - return ctrl.Result{}, nil - } - - initContainerIndex := getInitContainerIndex(pod) - if initContainerIndex > 0 { - log.Warnf(`Init containers before the %s container may cause issues - if they interfere with volumes being restored: %s index %d`, restorehelper.WaitInitContainer, restorehelper.WaitInitContainer, initContainerIndex) - } - - log.Info("Restore starting") - - callbacks := datapath.Callbacks{ - OnCompleted: c.OnDataPathCompleted, - OnFailed: c.OnDataPathFailed, - OnCancelled: c.OnDataPathCancelled, - OnProgress: c.OnDataPathProgress, - } - - fsRestore, err := c.dataPathMgr.CreateFileSystemBR(pvr.Name, pVBRRequestor, ctx, c.Client, pvr.Namespace, callbacks, log) - if err != nil { - if err == datapath.ConcurrentLimitExceed { - return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil - } else { - return c.errorOut(ctx, pvr, err, "error to create data path", log) - } - } - - original := pvr.DeepCopy() - pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress - pvr.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()} - if err = c.Patch(ctx, pvr, client.MergeFrom(original)); err != nil { - c.closeDataPath(ctx, pvr.Name) - return c.errorOut(ctx, pvr, err, "error to update status to in progress", log) - } - - volumePath, err := exposer.GetPodVolumeHostPath(ctx, pod, pvr.Spec.Volume, c.kubeClient, c.fileSystem, log) - if err != nil { - c.closeDataPath(ctx, pvr.Name) - return c.errorOut(ctx, pvr, err, "error exposing host path for pod volume", log) - } - - log.WithField("path", volumePath.ByPath).Debugf("Found host path") - - if err := fsRestore.Init(ctx, &datapath.FSBRInitParam{ - BSLName: pvr.Spec.BackupStorageLocation, - SourceNamespace: pvr.Spec.SourceNamespace, - UploaderType: pvr.Spec.UploaderType, - RepositoryType: podvolume.GetPvrRepositoryType(pvr), - RepoIdentifier: pvr.Spec.RepoIdentifier, - RepositoryEnsurer: c.repositoryEnsurer, - CredentialGetter: c.credentialGetter, - }); err != nil { - c.closeDataPath(ctx, pvr.Name) - return c.errorOut(ctx, pvr, err, "error to initialize data path", log) - } - - if err := fsRestore.StartRestore(pvr.Spec.SnapshotID, volumePath, pvr.Spec.UploaderSettings); err != nil { - c.closeDataPath(ctx, pvr.Name) - return c.errorOut(ctx, pvr, err, "error starting data path restore", log) - } - - log.WithField("path", volumePath.ByPath).Info("Async fs restore data path started") - - return ctrl.Result{}, nil -} - -func (c *PodVolumeRestoreReconcilerLegacy) errorOut(ctx context.Context, pvr *velerov1api.PodVolumeRestore, err error, msg string, log logrus.FieldLogger) (ctrl.Result, error) { - _ = UpdatePVRStatusToFailed(ctx, c.Client, pvr, err, msg, c.clock.Now(), log) - return ctrl.Result{}, err -} - -func (c *PodVolumeRestoreReconcilerLegacy) SetupWithManager(mgr ctrl.Manager) error { - // The pod may not being scheduled at the point when its PVRs are initially reconciled. - // By watching the pods, we can trigger the PVR reconciliation again once the pod is finally scheduled on the node. - pred := kube.NewAllEventPredicate(func(obj client.Object) bool { - pvr := obj.(*velerov1api.PodVolumeRestore) - return IsLegacyPVR(pvr) - }) - - return ctrl.NewControllerManagedBy(mgr).Named("podvolumerestorelegacy"). - For(&velerov1api.PodVolumeRestore{}, builder.WithPredicates(pred)). - Watches(&corev1api.Pod{}, handler.EnqueueRequestsFromMapFunc(c.findVolumeRestoresForPod)). - Complete(c) -} - -func (c *PodVolumeRestoreReconcilerLegacy) findVolumeRestoresForPod(ctx context.Context, pod client.Object) []reconcile.Request { - list := &velerov1api.PodVolumeRestoreList{} - options := &client.ListOptions{ - LabelSelector: labels.Set(map[string]string{ - velerov1api.PodUIDLabel: string(pod.GetUID()), - }).AsSelector(), - } - if err := c.Client.List(context.TODO(), list, options); err != nil { - c.logger.WithField("pod", fmt.Sprintf("%s/%s", pod.GetNamespace(), pod.GetName())).WithError(err). - Error("unable to list PodVolumeRestores") - return []reconcile.Request{} - } - - requests := []reconcile.Request{} - for _, item := range list.Items { - if !IsLegacyPVR(&item) { - continue - } - - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: item.GetNamespace(), - Name: item.GetName(), - }, - }) - } - return requests -} - -func (c *PodVolumeRestoreReconcilerLegacy) OnDataPathCompleted(ctx context.Context, namespace string, pvrName string, result datapath.Result) { - defer c.dataPathMgr.RemoveAsyncBR(pvrName) - - log := c.logger.WithField("pvr", pvrName) - - log.WithField("PVR", pvrName).Info("Async fs restore data path completed") - - var pvr velerov1api.PodVolumeRestore - if err := c.Client.Get(ctx, types.NamespacedName{Name: pvrName, Namespace: namespace}, &pvr); err != nil { - log.WithError(err).Warn("Failed to get PVR on completion") - return - } - - volumePath := result.Restore.Target.ByPath - if volumePath == "" { - _, _ = c.errorOut(ctx, &pvr, errors.New("path is empty"), "invalid restore target", log) - return - } - - // Remove the .velero directory from the restored volume (it may contain done files from previous restores - // of this volume, which we don't want to carry over). If this fails for any reason, log and continue, since - // this is non-essential cleanup (the done files are named based on restore UID and the init container looks - // for the one specific to the restore being executed). - if err := os.RemoveAll(filepath.Join(volumePath, ".velero")); err != nil { - log.WithError(err).Warnf("error removing .velero directory from directory %s", volumePath) - } - - var restoreUID types.UID - for _, owner := range pvr.OwnerReferences { - if boolptr.IsSetToTrue(owner.Controller) { - restoreUID = owner.UID - break - } - } - - // Create the .velero directory within the volume dir so we can write a done file - // for this restore. - if err := os.MkdirAll(filepath.Join(volumePath, ".velero"), 0755); err != nil { - _, _ = c.errorOut(ctx, &pvr, err, "error creating .velero directory for done file", log) - return - } - - // Write a done file with name= into the just-created .velero dir - // within the volume. The velero init container on the pod is waiting - // for this file to exist in each restored volume before completing. - if err := os.WriteFile(filepath.Join(volumePath, ".velero", string(restoreUID)), nil, 0644); err != nil { //nolint:gosec // Internal usage. No need to check. - _, _ = c.errorOut(ctx, &pvr, err, "error writing done file", log) - return - } - - original := pvr.DeepCopy() - pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted - pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()} - if err := c.Patch(ctx, &pvr, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("error updating PodVolumeRestore status") - } - - log.Info("Restore completed") -} - -func (c *PodVolumeRestoreReconcilerLegacy) OnDataPathFailed(ctx context.Context, namespace string, pvrName string, err error) { - defer c.dataPathMgr.RemoveAsyncBR(pvrName) - - log := c.logger.WithField("pvr", pvrName) - - log.WithError(err).Error("Async fs restore data path failed") - - var pvr velerov1api.PodVolumeRestore - if getErr := c.Client.Get(ctx, types.NamespacedName{Name: pvrName, Namespace: namespace}, &pvr); getErr != nil { - log.WithError(getErr).Warn("Failed to get PVR on failure") - } else { - _, _ = c.errorOut(ctx, &pvr, err, "data path restore failed", log) - } -} - -func (c *PodVolumeRestoreReconcilerLegacy) OnDataPathCancelled(ctx context.Context, namespace string, pvrName string) { - defer c.dataPathMgr.RemoveAsyncBR(pvrName) - - log := c.logger.WithField("pvr", pvrName) - - log.Warn("Async fs restore data path canceled") - - var pvr velerov1api.PodVolumeRestore - if getErr := c.Client.Get(ctx, types.NamespacedName{Name: pvrName, Namespace: namespace}, &pvr); getErr != nil { - log.WithError(getErr).Warn("Failed to get PVR on cancel") - } else { - _, _ = c.errorOut(ctx, &pvr, errors.New("PVR is canceled"), "data path restore canceled", log) - } -} - -func (c *PodVolumeRestoreReconcilerLegacy) OnDataPathProgress(ctx context.Context, namespace string, pvrName string, progress *uploader.Progress) { - log := c.logger.WithField("pvr", pvrName) - - var pvr velerov1api.PodVolumeRestore - if err := c.Client.Get(ctx, types.NamespacedName{Name: pvrName, Namespace: namespace}, &pvr); err != nil { - log.WithError(err).Warn("Failed to get PVB on progress") - return - } - - original := pvr.DeepCopy() - pvr.Status.Progress = veleroapishared.DataMoveOperationProgress{TotalBytes: progress.TotalBytes, BytesDone: progress.BytesDone} - - if err := c.Client.Patch(ctx, &pvr, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("Failed to update progress") - } -} - -func (c *PodVolumeRestoreReconcilerLegacy) closeDataPath(ctx context.Context, pvbName string) { - fsRestore := c.dataPathMgr.GetAsyncBR(pvbName) - if fsRestore != nil { - fsRestore.Close(ctx) - } - - c.dataPathMgr.RemoveAsyncBR(pvbName) -} - -func IsLegacyPVR(pvr *velerov1api.PodVolumeRestore) bool { - return pvr.Spec.UploaderType == "restic" -} diff --git a/pkg/controller/pod_volume_restore_controller_legacy_test.go b/pkg/controller/pod_volume_restore_controller_legacy_test.go deleted file mode 100644 index a107603e0..000000000 --- a/pkg/controller/pod_volume_restore_controller_legacy_test.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright The Velero Contributors. - -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 controller - -import ( - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - corev1api "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" -) - -func TestFindVolumeRestoresForPodLegacy(t *testing.T) { - pod := &corev1api.Pod{} - pod.UID = "uid" - - scheme := runtime.NewScheme() - scheme.AddKnownTypes(velerov1api.SchemeGroupVersion, &velerov1api.PodVolumeRestore{}, &velerov1api.PodVolumeRestoreList{}) - clientBuilder := fake.NewClientBuilder().WithScheme(scheme) - - // no matching PVR - reconciler := &PodVolumeRestoreReconcilerLegacy{ - Client: clientBuilder.Build(), - logger: logrus.New(), - } - requests := reconciler.findVolumeRestoresForPod(t.Context(), pod) - assert.Empty(t, requests) - - // contain one matching PVR - reconciler.Client = clientBuilder.WithLists(&velerov1api.PodVolumeRestoreList{ - Items: []velerov1api.PodVolumeRestore{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "pvr1", - Labels: map[string]string{ - velerov1api.PodUIDLabel: string(pod.GetUID()), - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "pvr2", - Labels: map[string]string{ - velerov1api.PodUIDLabel: "non-matching-uid", - }, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "pvr3", - Labels: map[string]string{ - velerov1api.PodUIDLabel: string(pod.GetUID()), - }, - }, - Spec: velerov1api.PodVolumeRestoreSpec{ - UploaderType: "kopia", - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "pvr4", - Labels: map[string]string{ - velerov1api.PodUIDLabel: string(pod.GetUID()), - }, - }, - Spec: velerov1api.PodVolumeRestoreSpec{ - UploaderType: "restic", - }, - }, - }, - }).Build() - requests = reconciler.findVolumeRestoresForPod(t.Context(), pod) - assert.Len(t, requests, 1) -} diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 9f2fe7a7f..8999543d7 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -526,6 +526,7 @@ func TestFindPVRForTargetPod(t *testing.T) { velerov1api.PodUIDLabel: string(pod.GetUID()), }, }, + Spec: velerov1api.PodVolumeRestoreSpec{UploaderType: uploader.KopiaType}, }, { ObjectMeta: metav1.ObjectMeta{ @@ -688,6 +689,7 @@ func TestPodVolumeRestoreReconcile(t *testing.T) { mockClose bool needExclusiveUpdateError error constrained bool + preserveEmptyUploader bool expected *velerov1api.PodVolumeRestore expectDeleted bool expectCancelRecord bool @@ -939,6 +941,13 @@ func TestPodVolumeRestoreReconcile(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + if !test.preserveEmptyUploader && test.pvr != nil && test.pvr.Spec.UploaderType == "" { + test.pvr.Spec.UploaderType = uploader.KopiaType + } + if !test.preserveEmptyUploader && test.expected != nil && test.expected.Spec.UploaderType == "" { + test.expected.Spec.UploaderType = uploader.KopiaType + } + objs := []runtime.Object{daemonSet, node} ctlObj := []client.Object{} @@ -1396,7 +1405,7 @@ func TestFindPVBForRestorePod(t *testing.T) { }{ { name: "find pvr for pod", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), pod: builder.ForPod(velerov1api.DefaultNamespace, pvrName).Labels(map[string]string{velerov1api.PVRLabel: pvrName}).Status(corev1api.PodStatus{Phase: corev1api.PodRunning}).Result(), checkFunc: func(pvr *velerov1api.PodVolumeRestore, requests []reconcile.Request) { // Assert that the function returns a single request @@ -1407,7 +1416,7 @@ func TestFindPVBForRestorePod(t *testing.T) { }, }, { name: "no selected label found for pod", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), pod: builder.ForPod(velerov1api.DefaultNamespace, pvrName).Result(), checkFunc: func(pvr *velerov1api.PodVolumeRestore, requests []reconcile.Request) { // Assert that the function returns a single request @@ -1415,7 +1424,7 @@ func TestFindPVBForRestorePod(t *testing.T) { }, }, { name: "no matched pod", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), pod: builder.ForPod(velerov1api.DefaultNamespace, pvrName).Labels(map[string]string{velerov1api.PVRLabel: "non-existing-pvr"}).Result(), checkFunc: func(pvr *velerov1api.PodVolumeRestore, requests []reconcile.Request) { assert.Empty(t, requests) @@ -1423,12 +1432,20 @@ func TestFindPVBForRestorePod(t *testing.T) { }, { name: "pvr not accept", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseInProgress).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseInProgress).Result(), pod: builder.ForPod(velerov1api.DefaultNamespace, pvrName).Labels(map[string]string{velerov1api.PVRLabel: pvrName}).Result(), checkFunc: func(pvr *velerov1api.PodVolumeRestore, requests []reconcile.Request) { assert.Empty(t, requests) }, }, + { + name: "invalid uploader type", + pvr: pvrBuilder().UploaderType("restic").Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), + pod: builder.ForPod(velerov1api.DefaultNamespace, pvrName).Labels(map[string]string{velerov1api.PVRLabel: pvrName}).Status(corev1api.PodStatus{Phase: corev1api.PodRunning}).Result(), + checkFunc: func(pvr *velerov1api.PodVolumeRestore, requests []reconcile.Request) { + assert.Empty(t, requests) + }, + }, } for _, test := range tests { ctx := t.Context() @@ -1613,32 +1630,32 @@ func TestAttemptPVRResume(t *testing.T) { }{ { name: "Other pvr", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhasePrepared).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhasePrepared).Result(), }, { name: "Other pvr", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseAccepted).Result(), }, { name: "InProgress pvr, not the current node", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseInProgress).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseInProgress).Result(), inProgressPvrs: []string{pvrName}, }, { name: "InProgress pvr, no resume error", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), inProgressPvrs: []string{pvrName}, }, { name: "InProgress pvr, resume error, cancel error", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), resumeErr: errors.New("fake-resume-error"), needErrs: []bool{false, false, true, false, false, false}, inProgressPvrs: []string{pvrName}, }, { name: "InProgress pvr, resume error, cancel succeed", - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhaseInProgress).Node("node-1").Result(), resumeErr: errors.New("fake-resume-error"), cancelledPvrs: []string{pvrName}, inProgressPvrs: []string{pvrName}, @@ -1646,7 +1663,7 @@ func TestAttemptPVRResume(t *testing.T) { { name: "Error", needErrs: []bool{false, false, false, false, false, true}, - pvr: pvrBuilder().Phase(velerov1api.PodVolumeRestorePhasePrepared).Result(), + pvr: pvrBuilder().UploaderType(uploader.KopiaType).Phase(velerov1api.PodVolumeRestorePhasePrepared).Result(), expectedError: "error to list PVRs: List error", }, } From 0fa1910afba37c4d367c4679355a2f672cec5003 Mon Sep 17 00:00:00 2001 From: Pierluigi Lenoci Date: Wed, 22 Apr 2026 09:44:47 +0200 Subject: [PATCH 08/32] fix: honor -stderrthreshold when -logtostderr is true (default) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit klog v2 defaults -logtostderr to true, which silently ignores the -stderrthreshold flag — all log levels are unconditionally sent to stderr. This makes it impossible for log-aggregation systems to filter by severity. Bump klog to v2.140.0 and opt into the fixed behavior by setting legacy_stderr_threshold_behavior=false and stderrthreshold=INFO (which preserves current output while letting users override via CLI flags). Ref: kubernetes/klog#212, kubernetes/klog#432 Signed-off-by: Pierluigi Lenoci --- go.mod | 2 +- go.sum | 4 ++-- pkg/cmd/velero/velero.go | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e80bfbcf9..d277791db 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( k8s.io/apimachinery v0.33.3 k8s.io/cli-runtime v0.33.3 k8s.io/client-go v0.33.3 - k8s.io/klog/v2 v2.130.1 + k8s.io/klog/v2 v2.140.0 k8s.io/kube-aggregator v0.33.3 k8s.io/metrics v0.33.3 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 diff --git a/go.sum b/go.sum index d8ee0cab0..4acfe045a 100644 --- a/go.sum +++ b/go.sum @@ -1232,8 +1232,8 @@ k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-aggregator v0.33.3 h1:Pa6hQpKJMX0p0D2wwcxXJgu02++gYcGWXoW1z1ZJDfo= k8s.io/kube-aggregator v0.33.3/go.mod h1:hwvkUoQ8q6gv0+SgNnlmQ3eUue1zHhJKTHsX7BwxwSE= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index a74c68fbc..d5392a92f 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -132,6 +132,11 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre // init and add the klog flags klog.InitFlags(flag.CommandLine) + // Opt into the new klog behavior so that -stderrthreshold is honored even + // when -logtostderr=true (the default). + // Ref: kubernetes/klog#212, kubernetes/klog#432 + flag.CommandLine.Set("legacy_stderr_threshold_behavior", "false") //nolint:errcheck + flag.CommandLine.Set("stderrthreshold", "INFO") //nolint:errcheck c.PersistentFlags().AddGoFlagSet(flag.CommandLine) return c From 4d23571c180d35524678b70698157133bd05bda6 Mon Sep 17 00:00:00 2001 From: Pierluigi Lenoci Date: Wed, 22 Apr 2026 16:10:46 +0200 Subject: [PATCH 09/32] fix: add changelog and nolint explanation for CI Add missing changelog entry for PR 9654 (fixes Changelog Check). Add explanation to //nolint:errcheck directives (fixes nolintlint). Signed-off-by: Pierluigi Lenoci --- changelogs/unreleased/9654-pierluigilenoci | 1 + pkg/cmd/velero/velero.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/9654-pierluigilenoci diff --git a/changelogs/unreleased/9654-pierluigilenoci b/changelogs/unreleased/9654-pierluigilenoci new file mode 100644 index 000000000..d31a76427 --- /dev/null +++ b/changelogs/unreleased/9654-pierluigilenoci @@ -0,0 +1 @@ +Fix issue #9658, Honor --stderrthreshold when --logtostderr is enabled diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index d5392a92f..42f07b03a 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -135,8 +135,8 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre // Opt into the new klog behavior so that -stderrthreshold is honored even // when -logtostderr=true (the default). // Ref: kubernetes/klog#212, kubernetes/klog#432 - flag.CommandLine.Set("legacy_stderr_threshold_behavior", "false") //nolint:errcheck - flag.CommandLine.Set("stderrthreshold", "INFO") //nolint:errcheck + flag.CommandLine.Set("legacy_stderr_threshold_behavior", "false") //nolint:errcheck // flag is registered by klog.InitFlags above + flag.CommandLine.Set("stderrthreshold", "INFO") //nolint:errcheck // flag is registered by klog.InitFlags above c.PersistentFlags().AddGoFlagSet(flag.CommandLine) return c From 3b34fb8effcd12ae0fd42cef0e29a0a64c7a9972 Mon Sep 17 00:00:00 2001 From: Joseph Antony Vaikath Date: Wed, 22 Apr 2026 16:24:22 -0400 Subject: [PATCH 10/32] Fix wildcard expansion when includes is empty and excludes has wildcards (#9684) * Fix wildcard expansion when includes is empty and excludes has wildcards When a Backup CR is applied via kubectl with empty includedNamespaces and a wildcard in excludedNamespaces, ShouldExpandWildcards triggers expansion. The empty includes expands to nil, but wildcardExpanded is set to true, causing ShouldInclude to return false for all namespaces. Populate expanded includes with all active namespaces when the original includes was empty (meaning "include all") so that the wildcardExpanded check does not falsely reject everything. Signed-off-by: Joseph * Changelog Signed-off-by: Joseph * Normalize empty includes to * instead of active namespaces list This ensures consistent behavior between CLI and kubectl-apply paths for Namespace CR inclusion when excludes contain wildcards. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Move empty includes normalization to backup controller Instead of normalizing empty IncludedNamespaces to ["*"] in the collections layer's ExpandIncludesExcludes, do it earlier in prepareBackupRequest. This ensures the spec is correct before any downstream processing. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Update TestProcessBackupCompletions for wildcard normalization Add IncludedNamespaces: []string{"*"} to all expected BackupSpec structs, reflecting the new prepareBackupRequest normalization. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Add checks around empty includenamespaces Signed-off-by: Joseph * gofmt Signed-off-by: Joseph --------- Signed-off-by: Joseph Co-authored-by: Claude Opus 4.6 (1M context) --- changelogs/unreleased/9684-Joeavaikath | 1 + pkg/controller/backup_controller.go | 7 ++++ pkg/controller/backup_controller_test.go | 47 ++++++++++++++++++++++++ pkg/util/wildcard/expand.go | 5 +++ pkg/util/wildcard/expand_test.go | 6 +++ 5 files changed, 66 insertions(+) create mode 100644 changelogs/unreleased/9684-Joeavaikath diff --git a/changelogs/unreleased/9684-Joeavaikath b/changelogs/unreleased/9684-Joeavaikath new file mode 100644 index 000000000..d5f5d6e76 --- /dev/null +++ b/changelogs/unreleased/9684-Joeavaikath @@ -0,0 +1 @@ +Fix wildcard expansion when includes is empty and excludes has wildcards diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 496308a6e..496875bbf 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -570,6 +570,13 @@ func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *vel } } + // Empty IncludedNamespaces means "include all namespaces". Normalize + // to ["*"] so that downstream wildcard expansion does not collapse + // an empty-includes + wildcard-excludes combination into "back up nothing". + if len(request.Spec.IncludedNamespaces) == 0 { + request.Spec.IncludedNamespaces = []string{"*"} + } + // validate the included/excluded namespaces for _, err := range collections.ValidateNamespaceIncludesExcludes(request.Spec.IncludedNamespaces, request.Spec.ExcludedNamespaces) { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err)) diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 386498900..c65f1d15d 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -320,6 +320,34 @@ func TestBackupLocationLabel(t *testing.T) { } } +func TestPrepareBackupRequest_EmptyIncludedNamespacesNormalizedToWildcard(t *testing.T) { + formatFlag := logging.FormatText + logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag) + + apiServer := velerotest.NewAPIServer(t) + discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) + require.NoError(t, err) + + backupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() + fakeClient := velerotest.NewFakeControllerRuntimeClient(t, backupLocation) + + c := &backupReconciler{ + discoveryHelper: discoveryHelper, + kbClient: fakeClient, + defaultBackupLocation: backupLocation.Name, + clock: &clock.RealClock{}, + formatFlag: formatFlag, + } + + backup := defaultBackup().Result() + backup.Spec.IncludedNamespaces = nil + + res := c.prepareBackupRequest(ctx, backup, logger) + defer res.WorkerPool.Stop() + + assert.Equal(t, []string{"*"}, res.Spec.IncludedNamespaces) +} + func Test_prepareBackupRequest_BackupStorageLocation(t *testing.T) { var ( defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour} @@ -709,6 +737,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -748,6 +777,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: "alt-loc", + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -791,6 +821,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: "read-write", + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -831,6 +862,7 @@ func TestProcessBackupCompletions(t *testing.T) { Spec: velerov1api.BackupSpec{ TTL: metav1.Duration{Duration: 10 * time.Minute}, StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -871,6 +903,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -912,6 +945,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -953,6 +987,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -994,6 +1029,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1035,6 +1071,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1077,6 +1114,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1119,6 +1157,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.True(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1161,6 +1200,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.True(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1204,6 +1244,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1247,6 +1288,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1290,6 +1332,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.True(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1334,6 +1377,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.False(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1377,6 +1421,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.True(), ExcludedClusterScopedResources: autoExcludeClusterScopedResources, @@ -1424,6 +1469,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.True(), IncludedClusterScopedResources: []string{"storageclasses"}, @@ -1473,6 +1519,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + IncludedNamespaces: []string{"*"}, DefaultVolumesToFsBackup: boolptr.False(), SnapshotMoveData: boolptr.True(), IncludedClusterScopedResources: []string{"storageclasses"}, diff --git a/pkg/util/wildcard/expand.go b/pkg/util/wildcard/expand.go index 8767c8ed3..4a88f1464 100644 --- a/pkg/util/wildcard/expand.go +++ b/pkg/util/wildcard/expand.go @@ -9,6 +9,11 @@ import ( ) func ShouldExpandWildcards(includes []string, excludes []string) bool { + // Empty includes is equivalent to * (match all) - don't expand + if len(includes) == 0 { + return false + } + wildcardFound := false for _, include := range includes { // Special case: "*" alone means "match all" - don't expand diff --git a/pkg/util/wildcard/expand_test.go b/pkg/util/wildcard/expand_test.go index 317020648..5730956cf 100644 --- a/pkg/util/wildcard/expand_test.go +++ b/pkg/util/wildcard/expand_test.go @@ -68,6 +68,12 @@ func TestShouldExpandWildcards(t *testing.T) { excludes: []string{}, expected: false, }, + { + name: "empty includes with wildcard excludes - should not expand", + includes: []string{}, + excludes: []string{"ns*"}, + expected: false, + }, { name: "complex wildcard patterns", includes: []string{"*-prod"}, From 7549408e210529a7dc81dd2c097734ee8cf257ba Mon Sep 17 00:00:00 2001 From: peter woodman Date: Thu, 23 Apr 2026 00:20:05 -0400 Subject: [PATCH 11/32] update hashicorp/go-hclog and go-plugin to current version (#9613) Signed-off-by: Peter Woodman --- changelogs/unreleased/9613-pjjw | 1 + go.mod | 9 ++--- go.sum | 39 ++++++++++--------- .../clientmgmt/process/logrus_adapter.go | 18 +++++++++ 4 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 changelogs/unreleased/9613-pjjw diff --git a/changelogs/unreleased/9613-pjjw b/changelogs/unreleased/9613-pjjw new file mode 100644 index 000000000..3f8c6faae --- /dev/null +++ b/changelogs/unreleased/9613-pjjw @@ -0,0 +1 @@ +update go-hclog to current version diff --git a/go.mod b/go.mod index e80bfbcf9..fe23ce9ab 100644 --- a/go.mod +++ b/go.mod @@ -22,8 +22,8 @@ require ( github.com/gobwas/glob v0.2.3 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/hashicorp/go-hclog v0.14.1 - github.com/hashicorp/go-plugin v1.6.0 + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-plugin v1.7.0 github.com/joho/godotenv v1.3.0 github.com/kopia/kopia v0.16.0 github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0 @@ -125,7 +125,7 @@ require ( github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/hashicorp/cronexpr v1.1.3 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -143,7 +143,6 @@ require ( github.com/minio/crc64nvme v1.1.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.97 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -153,7 +152,7 @@ require ( github.com/mxk/go-vss v1.2.0 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect diff --git a/go.sum b/go.sum index d8ee0cab0..808fe5215 100644 --- a/go.sum +++ b/go.sum @@ -174,8 +174,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZA82TQ= github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= -github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= -github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -242,6 +242,7 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -428,13 +429,13 @@ github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7 github.com/hashicorp/cronexpr v1.1.3/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= -github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -448,8 +449,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -458,8 +459,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= -github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -523,12 +524,13 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -542,7 +544,6 @@ github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohw github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -576,8 +577,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -696,6 +697,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -914,7 +916,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -926,12 +927,12 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -965,7 +966,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/plugin/clientmgmt/process/logrus_adapter.go b/pkg/plugin/clientmgmt/process/logrus_adapter.go index da573284a..8a790aace 100644 --- a/pkg/plugin/clientmgmt/process/logrus_adapter.go +++ b/pkg/plugin/clientmgmt/process/logrus_adapter.go @@ -196,3 +196,21 @@ func (l *logrusAdapter) Name() string { func (l *logrusAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { panic("not implemented") } + +// GetLevel returns the current level +func (l *logrusAdapter) GetLevel() hclog.Level { + switch l.level { + case logrus.TraceLevel: + return hclog.Trace + case logrus.DebugLevel: + return hclog.Debug + case logrus.InfoLevel: + return hclog.Info + case logrus.WarnLevel: + return hclog.Warn + case logrus.ErrorLevel: + return hclog.Error + default: + return hclog.NoLevel + } +} From f494e1064ec9af446cc1078614f94a4c59bf3ac4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 20:52:14 +0000 Subject: [PATCH 12/32] Bump go.opentelemetry.io/otel from 1.40.0 to 1.41.0 Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.40.0 to 1.41.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...v1.41.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-version: 1.41.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 23d141382..f5c6769e1 100644 --- a/go.mod +++ b/go.mod @@ -171,11 +171,11 @@ require ( go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect go.opentelemetry.io/otel/sdk v1.40.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect diff --git a/go.sum b/go.sum index 48111a58c..718349506 100644 --- a/go.sum +++ b/go.sum @@ -750,18 +750,18 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= From b3c178b9a1bddbe7b83fea11ba012c2fc9d49707 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 27 Apr 2026 15:47:17 +0800 Subject: [PATCH 13/32] Fix error in auto-request-review action Per action.yml of the action, the token is required. https://github.com/necojackarc/auto-request-review/blob/e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424/action.yml#L8 Signed-off-by: Daniel Jiang --- .github/workflows/auto_request_review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml index ba64d151d..47844bc6c 100644 --- a/.github/workflows/auto_request_review.yml +++ b/.github/workflows/auto_request_review.yml @@ -18,3 +18,4 @@ jobs: uses: necojackarc/auto-request-review@v0.13.0 with: config: .github/auto-assignees.yml + token: ${{ secrets.GITHUB_TOKEN }} From 16ca7739ceec92e8f6b530052f7290d208311cfd Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Apr 2026 17:15:52 +0800 Subject: [PATCH 14/32] add concurrency limit to go-releaser Signed-off-by: Lyndon-Li --- hack/release-tools/goreleaser.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hack/release-tools/goreleaser.sh b/hack/release-tools/goreleaser.sh index 79f9a4fc5..b8a180754 100755 --- a/hack/release-tools/goreleaser.sh +++ b/hack/release-tools/goreleaser.sh @@ -51,11 +51,13 @@ if [[ "${PUBLISH:-}" != "TRUE" ]]; then echo "Not set to publish" goreleaser release \ --clean \ + --parallelism 2 \ --release-notes="${RELEASE_NOTES_FILE}" \ --snapshot # Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts (implies --skip-publish, --skip-announce and --skip-validate) else echo "Getting ready to publish" goreleaser release \ --clean \ + --parallelism 2 \ --release-notes="${RELEASE_NOTES_FILE}" fi From 36dde0f88e182815e8cebba9110124dfdf8d295e Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Apr 2026 18:23:39 +0800 Subject: [PATCH 15/32] fix go-releaser upload error Signed-off-by: Lyndon-Li --- .goreleaser.yml | 2 +- hack/release-tools/tag-release.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 0ff87e36a..f206d3f9c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -55,7 +55,7 @@ checksum: name_template: 'CHECKSUM' release: github: - owner: vmware-tanzu + owner: velero-io name: velero draft: true prerelease: auto diff --git a/hack/release-tools/tag-release.sh b/hack/release-tools/tag-release.sh index 4fbfa933e..7a993c03b 100755 --- a/hack/release-tools/tag-release.sh +++ b/hack/release-tools/tag-release.sh @@ -24,7 +24,7 @@ # The following variables are needed: -# - $VELERO_VERSION: defines the tag of Velero that any https://github.com/vmware-tanzu/velero/... +# - $VELERO_VERSION: defines the tag of Velero that any https://github.com/velero-io/velero/... # links in the docs should redirect to. # - $REMOTE: defines the remote that should be used when pushing tags and branches. Defaults to "upstream" # - $publish: TRUE/FALSE value where FALSE (or not including it) will indicate a dry-run, and TRUE, or simply adding 'publish', From 79faa5ff967eb92e17f98e00e528ce95a921d36e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:58:24 -0400 Subject: [PATCH 16/32] Bump go.opentelemetry.io/otel/sdk from 1.40.0 to 1.43.0 (#9692) Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.40.0 to 1.43.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...v1.43.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/sdk dependency-version: 1.43.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index f5c6769e1..13e456d67 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( go.uber.org/zap v1.27.1 golang.org/x/mod v0.30.0 golang.org/x/oauth2 v0.34.0 - golang.org/x/sys v0.40.0 + golang.org/x/sys v0.42.0 golang.org/x/text v0.32.0 google.golang.org/api v0.256.0 google.golang.org/grpc v1.79.3 @@ -171,11 +171,11 @@ require ( go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.41.0 // indirect - go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.41.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect diff --git a/go.sum b/go.sum index 718349506..80d6ea3ac 100644 --- a/go.sum +++ b/go.sum @@ -750,18 +750,18 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= -go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= -go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= -go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= -go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= @@ -972,8 +972,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 76197d78923d472241df3b529b83bfd7740d838d Mon Sep 17 00:00:00 2001 From: Nolan Emirot Date: Tue, 28 Apr 2026 03:11:49 -0400 Subject: [PATCH 17/32] fix: lint permission issue (#9740) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: lint permission issue Signed-off-by: emirot * fix: lint permission issue Signed-off-by: emirot * Set permissions to the actions This commit update the actions "Auto Assign Author", "Auto Label PRs", and "Auto Request Review" Signed-off-by: Daniel Jiang Signed-off-by: emirot * Fix wildcard expansion when includes is empty and excludes has wildcards (#9684) * Fix wildcard expansion when includes is empty and excludes has wildcards When a Backup CR is applied via kubectl with empty includedNamespaces and a wildcard in excludedNamespaces, ShouldExpandWildcards triggers expansion. The empty includes expands to nil, but wildcardExpanded is set to true, causing ShouldInclude to return false for all namespaces. Populate expanded includes with all active namespaces when the original includes was empty (meaning "include all") so that the wildcardExpanded check does not falsely reject everything. Signed-off-by: Joseph * Changelog Signed-off-by: Joseph * Normalize empty includes to * instead of active namespaces list This ensures consistent behavior between CLI and kubectl-apply paths for Namespace CR inclusion when excludes contain wildcards. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Move empty includes normalization to backup controller Instead of normalizing empty IncludedNamespaces to ["*"] in the collections layer's ExpandIncludesExcludes, do it earlier in prepareBackupRequest. This ensures the spec is correct before any downstream processing. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Update TestProcessBackupCompletions for wildcard normalization Add IncludedNamespaces: []string{"*"} to all expected BackupSpec structs, reflecting the new prepareBackupRequest normalization. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Joseph * Add checks around empty includenamespaces Signed-off-by: Joseph * gofmt Signed-off-by: Joseph --------- Signed-off-by: Joseph Co-authored-by: Claude Opus 4.6 (1M context) Signed-off-by: emirot * update hashicorp/go-hclog and go-plugin to current version (#9613) Signed-off-by: Peter Woodman Signed-off-by: emirot * fix: honor -stderrthreshold when -logtostderr is true (default) klog v2 defaults -logtostderr to true, which silently ignores the -stderrthreshold flag — all log levels are unconditionally sent to stderr. This makes it impossible for log-aggregation systems to filter by severity. Bump klog to v2.140.0 and opt into the fixed behavior by setting legacy_stderr_threshold_behavior=false and stderrthreshold=INFO (which preserves current output while letting users override via CLI flags). Ref: kubernetes/klog#212, kubernetes/klog#432 Signed-off-by: Pierluigi Lenoci Signed-off-by: emirot * fix: add changelog and nolint explanation for CI Add missing changelog entry for PR 9654 (fixes Changelog Check). Add explanation to //nolint:errcheck directives (fixes nolintlint). Signed-off-by: Pierluigi Lenoci Signed-off-by: emirot * Remove Restic code path from PodVolumeRestore. Signed-off-by: Xun Jiang Signed-off-by: emirot * Bump go.opentelemetry.io/otel from 1.40.0 to 1.41.0 Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.40.0 to 1.41.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...v1.41.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-version: 1.41.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: emirot * Fix error in auto-request-review action Per action.yml of the action, the token is required. https://github.com/necojackarc/auto-request-review/blob/e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424/action.yml#L8 Signed-off-by: Daniel Jiang Signed-off-by: emirot * fix go-releaser upload error Signed-off-by: Lyndon-Li Signed-off-by: emirot * add concurrency limit to go-releaser Signed-off-by: Lyndon-Li Signed-off-by: emirot * Bump go.opentelemetry.io/otel/sdk from 1.40.0 to 1.43.0 (#9692) Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.40.0 to 1.43.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.40.0...v1.43.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/sdk dependency-version: 1.43.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: emirot * fix(lint): fix lint local Signed-off-by: emirot * Apply suggestion from @blackpiglet https://github.com/velero-io/velero/pull/9740/changes#r3151366281 Signed-off-by: Tiger Kaovilai --------- Signed-off-by: emirot Signed-off-by: Daniel Jiang Signed-off-by: Joseph Signed-off-by: Peter Woodman Signed-off-by: Pierluigi Lenoci Signed-off-by: Xun Jiang Signed-off-by: dependabot[bot] Signed-off-by: Lyndon-Li Signed-off-by: Tiger Kaovilai Co-authored-by: Daniel Jiang Co-authored-by: Joseph Antony Vaikath Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: peter woodman Co-authored-by: Pierluigi Lenoci Co-authored-by: Xun Jiang Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Lyndon-Li Co-authored-by: Tiger Kaovilai --- Makefile | 3 ++- changelogs/unreleased/9740-emirot | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/9740-emirot diff --git a/Makefile b/Makefile index 59f767afc..80db72e3a 100644 --- a/Makefile +++ b/Makefile @@ -211,6 +211,7 @@ shell: build-dirs build-env -v "$$(pwd)/.go/std/$(GOOS)/$(GOARCH):/usr/local/go/pkg/$(GOOS)_$(GOARCH)_static:delegated" \ -v "$$(pwd)/.go/go-build:/.cache/go-build:delegated" \ -v "$$(pwd)/.go/golangci-lint:/.cache/golangci-lint:delegated" \ + -v "$$(pwd)/.go/goimports:/.cache/goimports:delegated" \ -w /github.com/vmware-tanzu/velero \ $(BUILDER_IMAGE) \ /bin/sh $(CMD) @@ -342,7 +343,7 @@ update-crd: build-dirs: @mkdir -p _output/bin/$(GOOS)/$(GOARCH) - @mkdir -p .go/src/$(PKG) .go/pkg .go/bin .go/std/$(GOOS)/$(GOARCH) .go/go-build .go/golangci-lint + @mkdir -p .go/src/$(PKG) .go/pkg .go/bin .go/std/$(GOOS)/$(GOARCH) .go/go-build .go/golangci-lint .go/goimports build-env: @# if we have overridden the value for the build-image Dockerfile, diff --git a/changelogs/unreleased/9740-emirot b/changelogs/unreleased/9740-emirot new file mode 100644 index 000000000..dee2611c1 --- /dev/null +++ b/changelogs/unreleased/9740-emirot @@ -0,0 +1 @@ +fix: lint permission issue From f72c57b185280b1ddb196831a65a085e338e1aab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:39:18 -0400 Subject: [PATCH 18/32] Bump github.com/moby/spdystream from 0.5.0 to 0.5.1 (#9734) * Bump github.com/moby/spdystream from 0.5.0 to 0.5.1 Bumps [github.com/moby/spdystream](https://github.com/moby/spdystream) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/moby/spdystream/releases) - [Commits](https://github.com/moby/spdystream/compare/v0.5.0...v0.5.1) --- updated-dependencies: - dependency-name: github.com/moby/spdystream dependency-version: 0.5.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] * fix: run go mod tidy to update module files Agent-Logs-Url: https://github.com/velero-io/velero/sessions/3537c5cb-5e31-405c-a79f-878bd146efa8 Co-authored-by: blackpiglet <59276555+blackpiglet@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Daniel Jiang Co-authored-by: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 13e456d67..5bcbc11ef 100644 --- a/go.mod +++ b/go.mod @@ -143,7 +143,7 @@ require ( github.com/minio/crc64nvme v1.1.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.97 // indirect - github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/spdystream v0.5.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 80d6ea3ac..bbdb66567 100644 --- a/go.sum +++ b/go.sum @@ -551,8 +551,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= -github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= +github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= From 7918cb35dd9ade02ed57d36a7addbad7407e01a6 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 30 Apr 2026 14:17:42 +0800 Subject: [PATCH 19/32] fix docker hub push error Signed-off-by: Lyndon-Li --- .github/workflows/pr-containers.yml | 2 +- .github/workflows/pr-filepath-check.yml | 2 +- .github/workflows/pr-goreleaser.yml | 2 +- .github/workflows/push-builder.yml | 2 +- .github/workflows/push.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-containers.yml b/.github/workflows/pr-containers.yml index 6f839a166..2df7e0fab 100644 --- a/.github/workflows/pr-containers.yml +++ b/.github/workflows/pr-containers.yml @@ -32,6 +32,6 @@ jobs: # by push, so BRANCH and TAG are empty by default. docker-push.sh will # only build Velero image without pushing. - name: Make Velero container without pushing to registry. - if: github.repository == 'vmware-tanzu/velero' + if: github.repository == 'velero-io/velero' run: | ./hack/docker-push.sh \ No newline at end of file diff --git a/.github/workflows/pr-filepath-check.yml b/.github/workflows/pr-filepath-check.yml index 2e4b3d6ea..260a09dc4 100644 --- a/.github/workflows/pr-filepath-check.yml +++ b/.github/workflows/pr-filepath-check.yml @@ -84,7 +84,7 @@ jobs: else: print(f' # {original} — cannot auto-suggest rename (non-UTF-8)') print() - print('See https://github.com/vmware-tanzu/velero/pull/9552 for context.') + print('See https://github.com/velero-io/velero/pull/9552 for context.') sys.exit(1) else: print('All file paths are valid for Go module zip.') diff --git a/.github/workflows/pr-goreleaser.yml b/.github/workflows/pr-goreleaser.yml index 5215a5bfb..802080cb5 100644 --- a/.github/workflows/pr-goreleaser.yml +++ b/.github/workflows/pr-goreleaser.yml @@ -18,7 +18,7 @@ jobs: name: Checkout - name: Verify .goreleaser.yml and try a dryrun release. - if: github.repository == 'vmware-tanzu/velero' + if: github.repository == 'velero-io/velero' run: | CHANGELOG=$(ls changelogs | sort -V -r | head -n 1) GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ diff --git a/.github/workflows/push-builder.yml b/.github/workflows/push-builder.yml index 2272ee3f1..8e3e59c15 100644 --- a/.github/workflows/push-builder.yml +++ b/.github/workflows/push-builder.yml @@ -28,7 +28,7 @@ jobs: # Only try to publish the container image from the root repo; forks don't have permission to do so and will always get failures. - name: Publish container image - if: github.repository == 'vmware-tanzu/velero' + if: github.repository == 'velero-io/velero' run: | docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e0c32e189..10b191630 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -52,7 +52,7 @@ jobs: verbose: true # Only try to publish the container image from the root repo; forks don't have permission to do so and will always get failures. - name: Publish container image - if: github.repository == 'vmware-tanzu/velero' + if: github.repository == 'velero-io/velero' run: | sudo swapoff -a sudo rm -f /mnt/swapfile From 360095a2e76105e33b45aecd7f6834169c8e70aa Mon Sep 17 00:00:00 2001 From: Joseph Antony Vaikath Date: Wed, 6 May 2026 04:30:36 -0400 Subject: [PATCH 20/32] Merge pull request #9558 from Joeavaikath/wildcard-ns-bugfix Wildcard namespaces: Log warning on empty resolution --- changelogs/unreleased/9558-Joeavaikath | 1 + pkg/backup/backup.go | 41 ++++++++++++++----- pkg/util/collections/includes_excludes.go | 1 - .../docs/main/namespace-glob-patterns.md | 2 + 4 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/9558-Joeavaikath diff --git a/changelogs/unreleased/9558-Joeavaikath b/changelogs/unreleased/9558-Joeavaikath new file mode 100644 index 000000000..c9be4839e --- /dev/null +++ b/changelogs/unreleased/9558-Joeavaikath @@ -0,0 +1 @@ +Wildcard namespaces: Log warning on empty resolution diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 1a1c54247..9edaf6a85 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -167,15 +167,15 @@ func NewKubernetesBackupper( }, nil } -// getNamespaceIncludesExcludesAndArgoCDNamespaces returns an IncludesExcludes list containing which namespaces to -// include and exclude from the backup and a list of namespaces managed by ArgoCD. -func getNamespaceIncludesExcludesAndArgoCDNamespaces(backup *velerov1api.Backup, kbClient kbclient.Client) (*collections.NamespaceIncludesExcludes, []string, error) { +// getNamespaceIncludesExcludes returns an IncludesExcludes list containing which namespaces to +// include and exclude from the backup. +func getNamespaceIncludesExcludes(backup *velerov1api.Backup, kbClient kbclient.Client) (*collections.NamespaceIncludesExcludes, error) { nsList := corev1api.NamespaceList{} - activeNamespaces := []string{} - nsManagedByArgoCD := []string{} if err := kbClient.List(context.Background(), &nsList); err != nil { - return nil, nsManagedByArgoCD, err + return nil, err } + + activeNamespaces := []string{} for _, ns := range nsList.Items { activeNamespaces = append(activeNamespaces, ns.Name) } @@ -188,10 +188,20 @@ func getNamespaceIncludesExcludesAndArgoCDNamespaces(backup *velerov1api.Backup, // Expand wildcards if needed if err := includesExcludes.ExpandIncludesExcludes(); err != nil { - return nil, []string{}, err + return nil, err } - // Check for ArgoCD managed namespaces in the namespaces that will be included + return includesExcludes, nil +} + +// getArgoCDManagedNamespaces returns a list of namespaces managed by ArgoCD that should be included in the backup. +func getArgoCDManagedNamespaces(kbClient kbclient.Client, includesExcludes *collections.NamespaceIncludesExcludes) ([]string, error) { + nsList := corev1api.NamespaceList{} + if err := kbClient.List(context.Background(), &nsList); err != nil { + return nil, err + } + + nsManagedByArgoCD := []string{} for _, ns := range nsList.Items { nsLabels := ns.GetLabels() if len(nsLabels[ArgoCDManagedByNamespaceLabel]) > 0 && includesExcludes.ShouldInclude(ns.Name) { @@ -199,7 +209,7 @@ func getNamespaceIncludesExcludesAndArgoCDNamespaces(backup *velerov1api.Backup, } } - return includesExcludes, nsManagedByArgoCD, nil + return nsManagedByArgoCD, nil } func getResourceHooks(hookSpecs []velerov1api.BackupResourceHookSpec, discoveryHelper discovery.Helper) ([]hook.ResourceHook, error) { @@ -274,13 +284,18 @@ func (kb *kubernetesBackupper) BackupWithResolvers( return errors.WithStack(err) } var err error - var nsManagedByArgoCD []string - backupRequest.NamespaceIncludesExcludes, nsManagedByArgoCD, err = getNamespaceIncludesExcludesAndArgoCDNamespaces(backupRequest.Backup, kb.kbClient) + backupRequest.NamespaceIncludesExcludes, err = getNamespaceIncludesExcludes(backupRequest.Backup, kb.kbClient) if err != nil { log.WithError(err).Errorf("error getting namespace includes/excludes") return err } + nsManagedByArgoCD, err := getArgoCDManagedNamespaces(kb.kbClient, backupRequest.NamespaceIncludesExcludes) + if err != nil { + log.WithError(err).Errorf("error getting ArgoCD managed namespaces") + return err + } + if backupRequest.NamespaceIncludesExcludes.IsWildcardExpanded() { expandedIncludes := backupRequest.NamespaceIncludesExcludes.GetIncludes() expandedExcludes := backupRequest.NamespaceIncludesExcludes.GetExcludes() @@ -292,6 +307,10 @@ func (kb *kubernetesBackupper) BackupWithResolvers( return err } + if len(wildcardResult) == 0 { + log.Warnf("no namespaces matched the resolution of wildcard patterns ") + } + log.WithFields(logrus.Fields{ "expandedIncludes": expandedIncludes, "expandedExcludes": expandedExcludes, diff --git a/pkg/util/collections/includes_excludes.go b/pkg/util/collections/includes_excludes.go index ab63eaa72..f326a4124 100644 --- a/pkg/util/collections/includes_excludes.go +++ b/pkg/util/collections/includes_excludes.go @@ -173,7 +173,6 @@ func (nie *NamespaceIncludesExcludes) ExpandIncludesExcludes() error { } // ResolveNamespaceList returns a list of all namespaces which will be backed up. -// The second return value indicates whether wildcard expansion was performed. func (nie *NamespaceIncludesExcludes) ResolveNamespaceList() ([]string, error) { // Check if this is being called by non-backup processing e.g. backup queue controller if !nie.wildcardExpanded { diff --git a/site/content/docs/main/namespace-glob-patterns.md b/site/content/docs/main/namespace-glob-patterns.md index 4695124ea..c4cea9195 100644 --- a/site/content/docs/main/namespace-glob-patterns.md +++ b/site/content/docs/main/namespace-glob-patterns.md @@ -5,6 +5,8 @@ layout: docs When using `--include-namespaces` and `--exclude-namespaces` flags with backup and restore commands, you can use glob patterns to match multiple namespaces. +Note: If the resolution of namespace patterns results in no namespaces, the backup will succeed with a warning. + ## Supported Patterns Velero supports the following glob pattern characters: From e6d928cdfe06635347c564a7b03a5f5bf1156a57 Mon Sep 17 00:00:00 2001 From: Xun Jiang/Bruce Jiang <59276555+blackpiglet@users.noreply.github.com> Date: Thu, 7 May 2026 00:10:23 +0800 Subject: [PATCH 21/32] Some updates for actions on main branch. (#9775) * Add Windows dockerfile in the Dockerfile change action. * Update Bitnami MinIO Dockerfile directory. Signed-off-by: Xun Jiang --- .github/workflows/e2e-test-kind.yaml | 2 +- .github/workflows/pr-containers.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 033e93370..37bf72092 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -71,7 +71,7 @@ jobs: run: | echo "Building MinIO image from Bitnami Dockerfile..." git clone --depth 1 https://github.com/bitnami/containers.git /tmp/bitnami-containers - cd /tmp/bitnami-containers/bitnami/minio/2025/debian-12 + cd /tmp/bitnami-containers/bitnami/minio/2026/debian-12 docker build -t bitnami/minio:local . docker save bitnami/minio:local > ${{ github.workspace }}/minio-image.tar # Create json of k8s versions to test diff --git a/.github/workflows/pr-containers.yml b/.github/workflows/pr-containers.yml index 2df7e0fab..c2fea1386 100644 --- a/.github/workflows/pr-containers.yml +++ b/.github/workflows/pr-containers.yml @@ -7,6 +7,7 @@ on: - 'release-**' paths: - 'Dockerfile' + - 'Dockerfile-Windows' jobs: build: From 7314eab5820521248eb622935fd5ffbc3778eadc Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Fri, 8 May 2026 01:34:24 +0800 Subject: [PATCH 22/32] Update CODE OF CONDUCT for CNCF compliance (#9779) Signed-off-by: Daniel Jiang --- CODE_OF_CONDUCT.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 146cf4141..fe6ec8c6f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,23 @@ +# Velero Code of Conduct + +Velero is a [Cloud Native Computing Foundation](https://www.cncf.io/) sandbox +project. As a CNCF project, the Velero community follows the +[**CNCF Code of Conduct**](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). + +The text below is the project's adopted Code of Conduct, based on the +[Contributor Covenant](https://www.contributor-covenant.org/), and is +substantively aligned with the CNCF Code of Conduct. Where any conflict exists, +the CNCF Code of Conduct prevails. + +Instances of unacceptable behavior may be reported to the CNCF Code of +Conduct Committee at [conduct@cncf.io](mailto:conduct@cncf.io). For more +detailed instructions on how to submit a report, including how to submit a +report anonymously, please see the CNCF +[Incident Resolution Procedures](https://github.com/cncf/foundation/blob/main/code-of-conduct/coc-incident-resolution-procedures.md). +You can expect a response within three business days. + +--- + # Contributor Covenant Code of Conduct ## Our Pledge @@ -59,7 +79,8 @@ representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at oss-coc@vmware.com. +reported by contacting the CNCF Code of Conduct Committee at +[conduct@cncf.io](mailto:conduct@cncf.io). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the From 5afe5df1220411894cb1451a35c36c827eb67787 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 7 May 2026 18:57:34 +0800 Subject: [PATCH 23/32] Bump Velero dependencies to latest version. * Fix UT failures caused by client-go version bump. * Some modifications to enhance the UT stability. * Fix UT errors: non-constant format string in call to ... * Fix linter issues. Signed-off-by: Xun Jiang --- .github/workflows/get-go-version.yaml | 2 +- .github/workflows/pr-linter-check.yml | 2 +- .golangci.yaml | 5 +- Dockerfile | 2 +- Dockerfile-Windows | 2 +- Tiltfile | 2 +- .../velero-restore-helper.go | 1 + go.mod | 111 ++++---- go.sum | 257 ++++++++++-------- hack/build-image/Dockerfile | 2 +- hack/issue-template-gen/main.go | 1 + hack/test.sh | 4 +- .../restartabletest/restartable_delegate.go | 2 +- pkg/backup/actions/csi/pvc_action_test.go | 4 +- pkg/controller/backup_controller_test.go | 3 +- .../pod_volume_restore_controller_test.go | 5 +- pkg/install/daemonset.go | 3 +- pkg/install/deployment.go | 3 +- pkg/plugin/framework/server.go | 2 +- pkg/podvolume/backupper_test.go | 94 +++++-- pkg/podvolume/restorer_test.go | 23 +- pkg/repository/backup_repo_op_test.go | 5 +- pkg/restore/actions/csi/pvc_action_test.go | 4 +- pkg/restore/actions/service_action.go | 4 +- pkg/test/comparisons.go | 2 +- pkg/util/csi/volume_snapshot_test.go | 2 +- pkg/util/kube/mocks/Client.go | 39 ++- .../kube/periodical_enqueue_source_test.go | 27 +- 28 files changed, 342 insertions(+), 271 deletions(-) diff --git a/.github/workflows/get-go-version.yaml b/.github/workflows/get-go-version.yaml index 4f14a352c..7a74fd845 100644 --- a/.github/workflows/get-go-version.yaml +++ b/.github/workflows/get-go-version.yaml @@ -25,7 +25,7 @@ jobs: version=$(grep '^go ' go.mod | awk '{print $2}' | cut -d. -f1-2) else goDirectiveVersion=$(grep '^go ' go.mod | awk '{print $2}') - toolChainVersion=$(grep '^toolchain ' go.mod | awk '{print $2}') + toolChainVersion=$(grep '^toolchain ' go.mod | awk '{print $2}' | sed 's/^go//') version=$(printf "%s\n%s\n" "$goDirectiveVersion" "$toolChainVersion" | sort -V | tail -n1) fi diff --git a/.github/workflows/pr-linter-check.yml b/.github/workflows/pr-linter-check.yml index 79d7918b2..6ed7f073d 100644 --- a/.github/workflows/pr-linter-check.yml +++ b/.github/workflows/pr-linter-check.yml @@ -28,5 +28,5 @@ jobs: - name: Linter check uses: golangci/golangci-lint-action@v9 with: - version: v2.5.0 + version: v2.12.0 args: --verbose diff --git a/.golangci.yaml b/.golangci.yaml index 7ab8bdeaf..72db2986b 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -317,7 +317,7 @@ linters: - errchkjson - exptostd - ginkgolinter - - goconst + #- goconst # Disable goconst for now, as it reports a lot of false positives. We can enable it later after refactoring the codebase to reduce the number of string literals. - goheader - goprintffuncname - gosec @@ -383,6 +383,9 @@ linters: linters: - dupword text: "bucket" + - text: "non-constant format string" + linters: + - govet generated: lax presets: diff --git a/Dockerfile b/Dockerfile index 9b1e04a20..dfd95683f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. # Velero binary build section -FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS velero-builder +FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS velero-builder ARG GOPROXY ARG BIN diff --git a/Dockerfile-Windows b/Dockerfile-Windows index 757da8f80..df588def5 100644 --- a/Dockerfile-Windows +++ b/Dockerfile-Windows @@ -15,7 +15,7 @@ ARG OS_VERSION=1809 # Velero binary build section -FROM --platform=$BUILDPLATFORM golang:1.25-trixie AS velero-builder +FROM --platform=$BUILDPLATFORM golang:1.26-trixie AS velero-builder ARG GOPROXY ARG BIN diff --git a/Tiltfile b/Tiltfile index 8bfa2ac2f..de60b544f 100644 --- a/Tiltfile +++ b/Tiltfile @@ -52,7 +52,7 @@ git_sha = str(local("git rev-parse HEAD", quiet = True, echo_off = True)).strip( tilt_helper_dockerfile_header = """ # Tilt image -FROM golang:1.25 as tilt-helper +FROM golang:1.26 as tilt-helper # Support live reloading with Tilt RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \ diff --git a/cmd/velero-restore-helper/velero-restore-helper.go b/cmd/velero-restore-helper/velero-restore-helper.go index 40a942137..549bbedad 100644 --- a/cmd/velero-restore-helper/velero-restore-helper.go +++ b/cmd/velero-restore-helper/velero-restore-helper.go @@ -65,6 +65,7 @@ func done() bool { doneFile := filepath.Join("/restores", child.Name(), ".velero", os.Args[1]) + // #nosec G304,G703 -- doneFile is generated from internal logic and not user-controllable. if _, err := os.Stat(doneFile); os.IsNotExist(err) { fmt.Printf("The filesystem restore done file %s is not found yet. Retry later.\n", doneFile) return false diff --git a/go.mod b/go.mod index 5bcbc11ef..81bf85c8d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/vmware-tanzu/velero -go 1.25.0 +go 1.26.0 require ( cloud.google.com/go/storage v1.57.2 @@ -27,8 +27,8 @@ require ( github.com/joho/godotenv v1.3.0 github.com/kopia/kopia v0.16.0 github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0 - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.1 + github.com/onsi/ginkgo/v2 v2.28.3 + github.com/onsi/gomega v1.40.0 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.23.2 @@ -36,47 +36,48 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.10.0 - github.com/spf13/cobra v1.8.1 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.10.2 + github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 github.com/vmware-tanzu/crash-diagnostics v0.3.7 go.uber.org/zap v1.27.1 - golang.org/x/mod v0.30.0 - golang.org/x/oauth2 v0.34.0 - golang.org/x/sys v0.42.0 - golang.org/x/text v0.32.0 - google.golang.org/api v0.256.0 - google.golang.org/grpc v1.79.3 - google.golang.org/protobuf v1.36.10 + golang.org/x/mod v0.35.0 + golang.org/x/oauth2 v0.36.0 + golang.org/x/sys v0.43.0 + golang.org/x/text v0.36.0 + google.golang.org/api v0.277.0 + google.golang.org/grpc v1.80.0 + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.33.3 - k8s.io/apiextensions-apiserver v0.33.3 - k8s.io/apimachinery v0.33.3 - k8s.io/cli-runtime v0.33.3 - k8s.io/client-go v0.33.3 + k8s.io/api v0.36.0 + k8s.io/apiextensions-apiserver v0.36.0 + k8s.io/apimachinery v0.36.0 + k8s.io/cli-runtime v0.36.0 + k8s.io/client-go v0.36.0 k8s.io/klog/v2 v2.140.0 - k8s.io/kube-aggregator v0.33.3 - k8s.io/metrics v0.33.3 - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 - sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 - sigs.k8s.io/yaml v1.4.0 + k8s.io/kube-aggregator v0.36.0 + k8s.io/metrics v0.36.0 + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 + sigs.k8s.io/controller-runtime v0.24.0 + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 + sigs.k8s.io/yaml v1.6.0 ) require ( cel.dev/expr v0.25.1 // indirect cloud.google.com/go v0.121.6 // indirect - cloud.google.com/go/auth v0.17.0 // indirect + cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.5.2 // indirect - cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/monitoring v1.24.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect @@ -91,19 +92,18 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -114,15 +114,13 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gofrs/flock v0.13.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.1.3 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/hashicorp/cronexpr v1.1.3 // indirect github.com/hashicorp/yamux v0.1.2 // indirect @@ -146,9 +144,8 @@ require ( github.com/moby/spdystream v0.5.1 // indirect github.com/moby/term v0.5.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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-vss v1.2.0 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect @@ -158,8 +155,8 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/common v0.67.4 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/rs/xid v1.6.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -169,8 +166,8 @@ require ( github.com/zeebo/blake3 v0.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/sdk v1.43.0 // indirect @@ -179,22 +176,24 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.46.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.48.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/term v0.38.0 // indirect - golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.39.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/term v0.42.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.44.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect + k8s.io/streaming v0.36.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20251230033609-d946b1e75197 diff --git a/go.sum b/go.sum index bbdb66567..f7f6521df 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -41,14 +41,14 @@ cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCB cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= -cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= -cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= -cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= -cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= -cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= -cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA= +cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -61,8 +61,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.57.2 h1:sVlym3cHGYhrp6XZKkKb+92I1V42ks2qKKpB0CF5Mb4= cloud.google.com/go/storage v1.57.2/go.mod h1:n5ijg4yiRXXpCu0sJTD6k+eMf7GRrJmPyr9YxLXGHOk= -cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= -cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= @@ -101,14 +101,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -170,8 +172,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZA82TQ= github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= @@ -200,7 +200,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -219,8 +219,8 @@ github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84= github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -254,11 +254,17 @@ github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -300,6 +306,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -307,7 +315,6 @@ github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -349,10 +356,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -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/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -365,7 +370,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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= @@ -390,8 +394,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -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/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= @@ -400,12 +404,12 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= +github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -470,6 +474,8 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -523,6 +529,8 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -534,6 +542,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= @@ -560,14 +570,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mxk/go-vss v1.2.0 h1:JpdOPc/P6B3XyRoddn0iMiG/ADBi3AuEsv8RlTb+JeE= github.com/mxk/go-vss v1.2.0/go.mod h1:ZQ4yFxCG54vqPnCd+p2IxAe5jwZdz56wSjbwzBXiFd8= @@ -585,13 +595,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4= +github.com/onsi/ginkgo/v2 v2.28.3/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc= +github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -629,12 +639,12 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= -github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= @@ -669,14 +679,16 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -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.9/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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= @@ -705,6 +717,14 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tg123/go-htpasswd v1.2.4 h1:HgH8KKCjdmo7jjXWN9k1nefPBd7Be3tFCTjc2jPraPU= github.com/tg123/go-htpasswd v1.2.4/go.mod h1:EKThQok9xHkun6NBMynNv6Jmu24A33XdZzzl4Q7H1+0= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -746,10 +766,10 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= @@ -780,6 +800,8 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -792,8 +814,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -804,8 +826,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -831,8 +853,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -878,8 +900,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -893,8 +915,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -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/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -906,8 +928,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -972,14 +994,14 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -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/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -989,14 +1011,14 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -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/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1050,16 +1072,16 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= 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= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1082,8 +1104,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= -google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= +google.golang.org/api v0.277.0 h1:HJfyJUiNeBBUMai7ez8u14wkp/gH/I4wpGbbO9o+cSk= +google.golang.org/api v0.277.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1135,12 +1157,12 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= -google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1162,8 +1184,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1177,8 +1199,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1187,8 +1209,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1219,50 +1241,51 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= -k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= -k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= -k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs= -k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8= +k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80= +k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34= +k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0= +k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= -k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ= +k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc= k8s.io/cli-runtime v0.22.2/go.mod h1:tkm2YeORFpbgQHEK/igqttvPTRIHFRz5kATlw53zlMI= -k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA= -k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo= +k8s.io/cli-runtime v0.36.0 h1:HNxciQpQMMOKS0/GiUXcKDyA6J2FDILJj9NmP2BZrTg= +k8s.io/cli-runtime v0.36.0/go.mod h1:KObkknK9Ro5LYX+1RdiKc7C8CvGg4aX+V/Zv+E8WPHA= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= -k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= -k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c= +k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= -k8s.io/kube-aggregator v0.33.3 h1:Pa6hQpKJMX0p0D2wwcxXJgu02++gYcGWXoW1z1ZJDfo= -k8s.io/kube-aggregator v0.33.3/go.mod h1:hwvkUoQ8q6gv0+SgNnlmQ3eUue1zHhJKTHsX7BwxwSE= +k8s.io/kube-aggregator v0.36.0 h1:yrR+aw41p4/Wur55FCcfozPSBHH70HIs5j+J6ZphExg= +k8s.io/kube-aggregator v0.36.0/go.mod h1:2CkdUvPZjEbKnlhn+wxj6z3yity7H4xsTrFX+M/t1UE= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/metrics v0.33.3 h1:9CcqBz15JZfISqwca33gdHS8I6XfsK1vA8WUdEnG70g= -k8s.io/metrics v0.33.3/go.mod h1:Aw+cdg4AYHw0HvUY+lCyq40FOO84awrqvJRTw0cmXDs= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= +k8s.io/metrics v0.36.0 h1:VF41Mv9ZWKKQ4jEiJ0n3Tp6jdyO+oM6dbKcJn6Y/DVg= +k8s.io/metrics v0.36.0/go.mod h1:FY1dgPJZqnSfnOYbVdBEdRNUdy0n1nUCU6yxSMUrVG4= +k8s.io/streaming v0.36.0 h1:agnTxU+NFulUrtYzXUGKO3ndEa8jKwht1Kwn9nu9x+4= +k8s.io/streaming v0.36.0/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/controller-runtime v0.24.0 h1:Ck6N2LdS8Lovy1o25BB4r1xjvLEKUl1s2o9kU+KWDE4= +sigs.k8s.io/controller-runtime v0.24.0/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw= +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/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 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/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -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/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/hack/build-image/Dockerfile b/hack/build-image/Dockerfile index 25a162a82..eb1d728f5 100644 --- a/hack/build-image/Dockerfile +++ b/hack/build-image/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM --platform=$TARGETPLATFORM golang:1.25-trixie +FROM --platform=$TARGETPLATFORM golang:1.26-trixie ARG GOPROXY diff --git a/hack/issue-template-gen/main.go b/hack/issue-template-gen/main.go index 66a5ac916..dcdcf782e 100644 --- a/hack/issue-template-gen/main.go +++ b/hack/issue-template-gen/main.go @@ -29,6 +29,7 @@ import ( func main() { outTemplateFilename := os.Args[1] + // #nosec G304,G703 -- outTemplateFilename is generated from internal logic and not user-controllable outFile, err := os.OpenFile(outTemplateFilename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) diff --git a/hack/test.sh b/hack/test.sh index 7b70493e1..61756b12e 100755 --- a/hack/test.sh +++ b/hack/test.sh @@ -41,5 +41,7 @@ fi # but the user and group don't exist inside the container, when the code(https://github.com/kubernetes-sigs/controller-runtime/blob/v0.10.2/pkg/internal/testing/addr/manager.go#L44) # tries to get the cache directory, it gets the directory "/" and then get the permission error when trying to create directory under "/". # Specifying the cache directory by environment variable "XDG_CACHE_HOME" to workaround it -XDG_CACHE_HOME=/tmp/ go test -installsuffix "static" -short -timeout 1200s -coverprofile=coverage.out "${TARGETS[@]}" +# Add -vet parameter to filter out `printf` rule to avoid the false positive report for `non-constant format string in call to ...`. +# https://pkg.go.dev/cmd/go#hdr-Test_packages:~:text=Only%20a%20high%2Dconfidence%20subset%20of%20the%20default%20go%20vet%20checks%20are%20used.%20That%20subset%20is%3A%20atomic%2C%20bool%2C%20buildtags%2C%20directive%2C%20errorsas%2C%20ifaceassert%2C%20nilfunc%2C%20printf%2C%20stringintconv%2C%20and%20tests. +XDG_CACHE_HOME=/tmp/ go test -vet="atomic,bool,buildtags,directive,errorsas,ifaceassert,nilfunc,stringintconv,tests" -installsuffix "static" -short -timeout 1200s -coverprofile=coverage.out "${TARGETS[@]}" echo "Success!" diff --git a/internal/restartabletest/restartable_delegate.go b/internal/restartabletest/restartable_delegate.go index 75cf31c3a..41d56cf31 100644 --- a/internal/restartabletest/restartable_delegate.go +++ b/internal/restartabletest/restartable_delegate.go @@ -122,7 +122,7 @@ func RunRestartableDelegateTests( // If Function returns nil as struct return type, we cannot just // compare the interface to nil as its type will not be nil, // only the value will be - if expected[i] == nil && reflect.ValueOf(a).Kind() == reflect.Ptr { + if expected[i] == nil && reflect.ValueOf(a).Kind() == reflect.Pointer { assert.True(t, reflect.ValueOf(a).IsNil()) continue } diff --git a/pkg/backup/actions/csi/pvc_action_test.go b/pkg/backup/actions/csi/pvc_action_test.go index 9ffe20be5..274371db3 100644 --- a/pkg/backup/actions/csi/pvc_action_test.go +++ b/pkg/backup/actions/csi/pvc_action_test.go @@ -279,7 +279,7 @@ func TestExecute(t *testing.T) { err := crClient.List(t.Context(), dataUploadList, &crclient.ListOptions{LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: tc.backup.Name})}) require.NoError(t, err) require.Len(t, dataUploadList.Items, 1) - require.True(t, cmp.Equal(tc.expectedDataUpload, &dataUploadList.Items[0], cmpopts.IgnoreFields(velerov2alpha1.DataUpload{}, "ResourceVersion", "Name", "Spec.CSISnapshot.VolumeSnapshot"))) + require.Empty(t, cmp.Diff(tc.expectedDataUpload, &dataUploadList.Items[0], cmpopts.IgnoreFields(velerov2alpha1.DataUpload{}, "TypeMeta", "ResourceVersion", "Name", "Spec.CSISnapshot.VolumeSnapshot"))) } if tc.expectedPVC != nil { @@ -488,7 +488,7 @@ func TestCancel(t *testing.T) { err = crClient.Get(t.Context(), crclient.ObjectKey{Namespace: tc.dataUpload.Namespace, Name: tc.dataUpload.Name}, du) require.NoError(t, err) - require.True(t, cmp.Equal(tc.expectedDataUpload, *du, cmpopts.IgnoreFields(velerov2alpha1.DataUpload{}, "ResourceVersion"))) + require.Empty(t, cmp.Diff(tc.expectedDataUpload, *du, cmpopts.IgnoreFields(velerov2alpha1.DataUpload{}, "TypeMeta", "ResourceVersion"))) } }) } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index c65f1d15d..7b646edcf 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -27,6 +27,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -1656,7 +1657,7 @@ func TestProcessBackupCompletions(t *testing.T) { err = c.kbClient.Get(t.Context(), kbclient.ObjectKey{Namespace: test.backup.Namespace, Name: test.backup.Name}, res) require.NoError(t, err) res.ResourceVersion = "" - assert.Equal(t, test.expectedResult, res) + assert.Empty(t, cmp.Diff(test.expectedResult, res, cmpopts.IgnoreFields(velerov1api.Backup{}, "TypeMeta"))) // reset defaultBackupLocation resourceVersion defaultBackupLocation.ObjectMeta.ResourceVersion = "" }) diff --git a/pkg/controller/pod_volume_restore_controller_test.go b/pkg/controller/pod_volume_restore_controller_test.go index 8999543d7..1819cbc32 100644 --- a/pkg/controller/pod_volume_restore_controller_test.go +++ b/pkg/controller/pod_volume_restore_controller_test.go @@ -506,18 +506,17 @@ func TestFindPVRForTargetPod(t *testing.T) { scheme := runtime.NewScheme() scheme.AddKnownTypes(velerov1api.SchemeGroupVersion, &velerov1api.PodVolumeRestore{}, &velerov1api.PodVolumeRestoreList{}) - clientBuilder := fake.NewClientBuilder().WithScheme(scheme) // no matching PVR reconciler := &PodVolumeRestoreReconciler{ - client: clientBuilder.Build(), + client: fake.NewClientBuilder().WithScheme(scheme).Build(), logger: logrus.New(), } requests := reconciler.findPVRForTargetPod(t.Context(), pod) assert.Empty(t, requests) // contain one matching PVR - reconciler.client = clientBuilder.WithLists(&velerov1api.PodVolumeRestoreList{ + reconciler.client = fake.NewClientBuilder().WithScheme(scheme).WithLists(&velerov1api.PodVolumeRestoreList{ Items: []velerov1api.PodVolumeRestore{ { ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/install/daemonset.go b/pkg/install/daemonset.go index 771114e82..10d2764e6 100644 --- a/pkg/install/daemonset.go +++ b/pkg/install/daemonset.go @@ -198,7 +198,8 @@ func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1api.DaemonSet Secret: &corev1api.SecretVolumeSource{ // read-only for Owner, Group, Public DefaultMode: ptr.To(int32(0444)), - SecretName: "cloud-credentials", + // #nosec G101 -- This is a reference to a Secret resource name, not a credential + SecretName: "cloud-credentials", }, }, }, diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 04ea40e04..7af17bc53 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -454,7 +454,8 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1api.Deployme Secret: &corev1api.SecretVolumeSource{ // read-only for Owner, Group, Public DefaultMode: ptr.To(int32(0444)), - SecretName: "cloud-credentials", + // #nosec G101 -- This is a reference to a Secret resource name, not a credential + SecretName: "cloud-credentials", }, }, }, diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index 0231a5122..923210d55 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -140,7 +140,7 @@ func NewServer() Server { func (s *server) BindFlags(flags *pflag.FlagSet) Server { s.flagSet = flags s.config.BindFlags(flags) - s.flagSet.ParseErrorsWhitelist.UnknownFlags = true // Velero.io word list : ignore + s.flagSet.ParseErrorsAllowlist.UnknownFlags = true // Velero.io word list : ignore return s } diff --git a/pkg/podvolume/backupper_test.go b/pkg/podvolume/backupper_test.go index f7686978a..fba789664 100644 --- a/pkg/podvolume/backupper_test.go +++ b/pkg/podvolume/backupper_test.go @@ -29,6 +29,7 @@ import ( corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -40,9 +41,61 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/repository" velerotest "github.com/vmware-tanzu/velero/pkg/test" - "github.com/vmware-tanzu/velero/pkg/util/kube" ) +type fakeInformerRegistration struct{} + +func (fakeInformerRegistration) HasSynced() bool { return true } + +func (fakeInformerRegistration) HasSyncedChecker() cache.DoneChecker { + return fakeDoneChecker{name: "fakeInformerRegistration"} +} + +type fakeDoneChecker struct { + name string +} + +func (f fakeDoneChecker) Name() string { return f.name } + +func (f fakeDoneChecker) Done() <-chan struct{} { + ch := make(chan struct{}) + close(ch) + return ch +} + +type fakeInformer struct { + handler cache.ResourceEventHandler +} + +func (f *fakeInformer) AddEventHandler(handler cache.ResourceEventHandler) (cache.ResourceEventHandlerRegistration, error) { + f.handler = handler + return fakeInformerRegistration{}, nil +} + +func (f *fakeInformer) AddEventHandlerWithResyncPeriod(handler cache.ResourceEventHandler, _ time.Duration) (cache.ResourceEventHandlerRegistration, error) { + return f.AddEventHandler(handler) +} + +func (f *fakeInformer) AddEventHandlerWithOptions(handler cache.ResourceEventHandler, _ cache.HandlerOptions) (cache.ResourceEventHandlerRegistration, error) { + return f.AddEventHandler(handler) +} + +func (f *fakeInformer) RemoveEventHandler(_ cache.ResourceEventHandlerRegistration) error { + return nil +} + +func (f *fakeInformer) AddIndexers(_ cache.Indexers) error { return nil } + +func (f *fakeInformer) HasSynced() bool { return true } + +func (f *fakeInformer) HasSyncedChecker() cache.DoneChecker { + return fakeDoneChecker{name: "fakeInformer"} +} + +func (f *fakeInformer) IsStopped() bool { return false } + +var _ ctrlcache.Informer = (*fakeInformer)(nil) + func TestIsHostPathVolume(t *testing.T) { // hostPath pod volume vol := &corev1api.Volume{ @@ -557,27 +610,16 @@ func TestBackupPodVolumes(t *testing.T) { objList = append(objList, test.kubeClientObj...) fakeCtrlClient := fakeClientBuilder.WithRuntimeObjects(objList...).Build() - fakeCRWatchClient := velerotest.NewFakeControllerRuntimeWatchClient(t, test.kubeClientObj...) - lw := kube.InternalLW{ - Client: fakeCRWatchClient, - Namespace: velerov1api.DefaultNamespace, - ObjectList: new(velerov1api.PodVolumeBackupList), - } - - pvbInformer := cache.NewSharedIndexInformer(&lw, &velerov1api.PodVolumeBackup{}, 0, cache.Indexers{}) - - go pvbInformer.Run(ctx.Done()) - require.True(t, cache.WaitForCacheSync(ctx.Done(), pvbInformer.HasSynced)) + // This test validates creation-time behavior only, so we don't need + // informer sync/watch to be running. + pvbInformer := cache.NewSharedIndexInformer(&cache.ListWatch{}, &velerov1api.PodVolumeBackup{}, 0, cache.Indexers{}) ensurer := repository.NewEnsurer(fakeCtrlClient, velerotest.NewLogger(), time.Millisecond) backupObj := builder.ForBackup(velerov1api.DefaultNamespace, "fake-backup").Result() backupObj.Spec.StorageLocation = test.bsl - factory := NewBackupperFactory(repository.NewRepoLocker(), ensurer, fakeCtrlClient, pvbInformer, velerotest.NewLogger()) - bp, err := factory.NewBackupper(ctx, log, backupObj, test.uploaderType) - - require.NoError(t, err) + bp := newBackupper(ctx, log, repository.NewRepoLocker(), ensurer, pvbInformer, fakeCtrlClient, test.uploaderType, backupObj) if test.mockGetRepositoryType { funcGetRepositoryType = func() string { return "" } @@ -587,9 +629,7 @@ func TestBackupPodVolumes(t *testing.T) { pvbs, _, errs := bp.BackupPodVolumes(backupObj, test.sourcePod, test.volumes, nil, velerotest.NewLogger()) - if test.errs == nil { - require.NoError(t, err) - } else { + if test.errs != nil { for i := 0; i < len(errs); i++ { require.EqualError(t, errs[i], test.errs[i]) } @@ -760,17 +800,7 @@ func TestWaitAllPodVolumesProcessed(t *testing.T) { velerov1api.AddToScheme(scheme) client := ctrlfake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build() - lw := kube.InternalLW{ - Client: client, - Namespace: velerov1api.DefaultNamespace, - ObjectList: new(velerov1api.PodVolumeBackupList), - } - - informer := cache.NewSharedIndexInformer(&lw, &velerov1api.PodVolumeBackup{}, 0, cache.Indexers{}) - - ctx := t.Context() - go informer.Run(ctx.Done()) - require.True(t, cache.WaitForCacheSync(ctx.Done(), informer.HasSynced)) + informer := &fakeInformer{} logger := logrus.New() logHook := &logHook{} @@ -787,9 +817,13 @@ func TestWaitAllPodVolumesProcessed(t *testing.T) { err := client.Get(t.Context(), ctrlclient.ObjectKey{Namespace: c.pvb.Namespace, Name: c.pvb.Name}, pvb) require.NoError(t, err) + oldPVB := pvb.DeepCopy() pvb.Status = *c.statusToBeUpdated err = client.Update(t.Context(), pvb) require.NoError(t, err) + + require.NotNil(t, informer.handler) + informer.handler.OnUpdate(oldPVB, pvb) } pvbs := backuper.WaitAllPodVolumesProcessed(logger) diff --git a/pkg/podvolume/restorer_test.go b/pkg/podvolume/restorer_test.go index e10146578..bd67e8480 100644 --- a/pkg/podvolume/restorer_test.go +++ b/pkg/podvolume/restorer_test.go @@ -25,7 +25,6 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" appsv1api "k8s.io/api/apps/v1" corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -39,7 +38,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/repository" velerotest "github.com/vmware-tanzu/velero/pkg/test" - "github.com/vmware-tanzu/velero/pkg/util/kube" ) func TestGetVolumesRepositoryType(t *testing.T) { @@ -359,27 +357,14 @@ func TestRestorePodVolumes(t *testing.T) { fakeKubeClient := kubefake.NewSimpleClientset(test.kubeClientObj...) var kubeClient kubernetes.Interface = fakeKubeClient - fakeCRWatchClient := velerotest.NewFakeControllerRuntimeWatchClient(t, test.kubeClientObj...) - lw := kube.InternalLW{ - Client: fakeCRWatchClient, - Namespace: velerov1api.DefaultNamespace, - ObjectList: new(velerov1api.PodVolumeRestoreList), - } - - pvrInformer := cache.NewSharedIndexInformer(&lw, &velerov1api.PodVolumeBackup{}, 0, cache.Indexers{}) - - go pvrInformer.Run(ctx.Done()) - require.True(t, cache.WaitForCacheSync(ctx.Done(), pvrInformer.HasSynced)) + // This test verifies restore behavior itself, not informer sync/watch. + pvrInformer := cache.NewSharedIndexInformer(&cache.ListWatch{}, &velerov1api.PodVolumeRestore{}, 0, cache.Indexers{}) ensurer := repository.NewEnsurer(fakeCRClient, velerotest.NewLogger(), time.Millisecond) restoreObj := builder.ForRestore(velerov1api.DefaultNamespace, "fake-restore").Result() - factory := NewRestorerFactory(repository.NewRepoLocker(), ensurer, kubeClient, - fakeCRClient, pvrInformer, velerotest.NewLogger()) - rs, err := factory.NewRestorer(ctx, restoreObj) - - require.NoError(t, err) + rs := newRestorer(ctx, repository.NewRepoLocker(), ensurer, pvrInformer, kubeClient, fakeCRClient, restoreObj, velerotest.NewLogger()) go func() { if test.ctx != nil { @@ -388,7 +373,7 @@ func TestRestorePodVolumes(t *testing.T) { } else if test.retPVRs != nil { time.Sleep(time.Second) for _, pvr := range test.retPVRs { - rs.(*restorer).results[resultsKey(test.restoredPod.Namespace, test.restoredPod.Name)] <- pvr + rs.results[resultsKey(test.restoredPod.Namespace, test.restoredPod.Name)] <- pvr } } }() diff --git a/pkg/repository/backup_repo_op_test.go b/pkg/repository/backup_repo_op_test.go index a106a6602..de53cfbe7 100644 --- a/pkg/repository/backup_repo_op_test.go +++ b/pkg/repository/backup_repo_op_test.go @@ -19,6 +19,9 @@ package repository import ( "fmt" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" "testing" @@ -153,7 +156,7 @@ func TestGetBackupRepository(t *testing.T) { if backupRepo != nil && tc.expected != nil { backupRepo.ResourceVersion = tc.expected.ResourceVersion - require.Equal(t, *tc.expected, *backupRepo) + require.Empty(t, cmp.Diff(*tc.expected, *backupRepo, cmpopts.IgnoreFields(velerov1api.BackupRepository{}, "TypeMeta"))) } else { require.Equal(t, tc.expected, backupRepo) } diff --git a/pkg/restore/actions/csi/pvc_action_test.go b/pkg/restore/actions/csi/pvc_action_test.go index bc7f66d89..4da3bec50 100644 --- a/pkg/restore/actions/csi/pvc_action_test.go +++ b/pkg/restore/actions/csi/pvc_action_test.go @@ -359,7 +359,7 @@ func TestCancel(t *testing.T) { err = pvcRIA.crClient.Get(t.Context(), crclient.ObjectKey{Namespace: tc.dataDownload.Namespace, Name: tc.dataDownload.Name}, resultDataDownload) require.NoError(t, err) - require.True(t, cmp.Equal(tc.expectedDataDownload, *resultDataDownload, cmpopts.IgnoreFields(velerov2alpha1.DataDownload{}, "ResourceVersion", "Name"))) + require.Empty(t, cmp.Diff(tc.expectedDataDownload, *resultDataDownload, cmpopts.IgnoreFields(velerov2alpha1.DataDownload{}, "TypeMeta", "ResourceVersion", "Name"))) }) } } @@ -523,7 +523,7 @@ func TestExecute(t *testing.T) { LabelSelector: labels.SelectorFromSet(tc.expectedDataDownload.Labels), }) require.NoError(t, err) - require.True(t, cmp.Equal(tc.expectedDataDownload, &dataDownloadList.Items[0], cmpopts.IgnoreFields(velerov2alpha1.DataDownload{}, "ResourceVersion", "Name"))) + require.Empty(t, cmp.Diff(tc.expectedDataDownload, &dataDownloadList.Items[0], cmpopts.IgnoreFields(velerov2alpha1.DataDownload{}, "TypeMeta", "ResourceVersion", "Name"))) } }) } diff --git a/pkg/restore/actions/service_action.go b/pkg/restore/actions/service_action.go index 34ba85e93..47147a31f 100644 --- a/pkg/restore/actions/service_action.go +++ b/pkg/restore/actions/service_action.go @@ -117,7 +117,7 @@ func deleteHealthCheckNodePort(service *corev1api.Service) error { continue } fields := new(map[string]any) - if err := json.Unmarshal(entry.FieldsV1.Raw, fields); err != nil { + if err := json.Unmarshal(entry.FieldsV1.GetRawBytes(), fields); err != nil { return errors.WithStack(err) } @@ -222,7 +222,7 @@ func deleteNodePorts(service *corev1api.Service) error { continue } fields := new(map[string]any) - if err := json.Unmarshal(entry.FieldsV1.Raw, fields); err != nil { + if err := json.Unmarshal(entry.FieldsV1.GetRawBytes(), fields); err != nil { return errors.WithStack(err) } diff --git a/pkg/test/comparisons.go b/pkg/test/comparisons.go index 670cf3cf0..d66646886 100644 --- a/pkg/test/comparisons.go +++ b/pkg/test/comparisons.go @@ -107,7 +107,7 @@ func AssertDeepEqual(t *testing.T, expected, actual any) bool { } if !equality.Semantic.DeepEqual(expected, actual) { - s := diff.ObjectDiff(expected, actual) + s := diff.Diff(expected, actual) return assert.Fail(t, fmt.Sprintf("Objects not equal:\n\n%s", s)) } diff --git a/pkg/util/csi/volume_snapshot_test.go b/pkg/util/csi/volume_snapshot_test.go index 2f735559c..67a07d135 100644 --- a/pkg/util/csi/volume_snapshot_test.go +++ b/pkg/util/csi/volume_snapshot_test.go @@ -2004,7 +2004,7 @@ func TestGetVSCForVS(t *testing.T) { } if tc.expectedVSC != nil { - require.True(t, cmp.Equal(tc.expectedVSC, vsc, cmpopts.IgnoreFields(snapshotv1api.VolumeSnapshotContent{}, "ResourceVersion"))) + require.Empty(t, cmp.Diff(tc.expectedVSC, vsc, cmpopts.IgnoreFields(snapshotv1api.VolumeSnapshotContent{}, "TypeMeta", "ResourceVersion"))) } }) } diff --git a/pkg/util/kube/mocks/Client.go b/pkg/util/kube/mocks/Client.go index cb3b8d77f..e4067b9a5 100644 --- a/pkg/util/kube/mocks/Client.go +++ b/pkg/util/kube/mocks/Client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.53.5. DO NOT EDIT. package mocks @@ -7,14 +7,10 @@ import ( client "sigs.k8s.io/controller-runtime/pkg/client" - meta "k8s.io/apimachinery/pkg/api/meta" - mock "github.com/stretchr/testify/mock" - + meta "k8s.io/apimachinery/pkg/api/meta" runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" ) @@ -23,6 +19,31 @@ type Client struct { mock.Mock } +// Apply provides a mock function with given fields: ctx, obj, opts +func (_m *Client) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, obj) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Apply") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, runtime.ApplyConfiguration, ...client.ApplyOption) error); ok { + r0 = rf(ctx, obj, opts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Create provides a mock function with given fields: ctx, obj, opts func (_m *Client) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { _va := make([]interface{}, len(opts)) @@ -229,7 +250,7 @@ func (_m *Client) Patch(ctx context.Context, obj client.Object, patch client.Pat return r0 } -// RESTMapper provides a mock function with given fields: +// RESTMapper provides a mock function with no fields func (_m *Client) RESTMapper() meta.RESTMapper { ret := _m.Called() @@ -249,7 +270,7 @@ func (_m *Client) RESTMapper() meta.RESTMapper { return r0 } -// Scheme provides a mock function with given fields: +// Scheme provides a mock function with no fields func (_m *Client) Scheme() *runtime.Scheme { ret := _m.Called() @@ -269,7 +290,7 @@ func (_m *Client) Scheme() *runtime.Scheme { return r0 } -// Status provides a mock function with given fields: +// Status provides a mock function with no fields func (_m *Client) Status() client.SubResourceWriter { ret := _m.Called() diff --git a/pkg/util/kube/periodical_enqueue_source_test.go b/pkg/util/kube/periodical_enqueue_source_test.go index ee892b54b..b5e97321a 100644 --- a/pkg/util/kube/periodical_enqueue_source_test.go +++ b/pkg/util/kube/periodical_enqueue_source_test.go @@ -17,6 +17,7 @@ limitations under the License. package kube import ( + "sort" "testing" "time" @@ -24,9 +25,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/util/workqueue" crclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -91,8 +90,6 @@ func TestPredicate(t *testing.T) { }, ) - require.NoError(t, source.Start(ctx, queue)) - // Should not patch a backup storage location object status phase // if the location's validation frequency is specifically set to zero require.NoError(t, client.Create(ctx, &velerov1.BackupStorageLocation{ @@ -107,6 +104,8 @@ func TestPredicate(t *testing.T) { LastValidationTime: &metav1.Time{Time: time.Now()}, }, })) + + require.NoError(t, source.Start(ctx, queue)) time.Sleep(2 * time.Second) require.Equal(t, 0, queue.Len()) @@ -127,24 +126,20 @@ func TestOrder(t *testing.T) { 1*time.Second, PeriodicalEnqueueSourceOption{ OrderFunc: func(objList crclient.ObjectList) crclient.ObjectList { - locationList := &velerov1.BackupStorageLocationList{} - objArray := make([]runtime.Object, 0) + locationList, ok := objList.(*velerov1.BackupStorageLocationList) + if !ok { + return objList + } - // Generate BSL array. - locations, _ := meta.ExtractList(objList) - // Move default BSL to tail of array. - objArray = append(objArray, locations[1]) - objArray = append(objArray, locations[0]) - - meta.SetList(locationList, objArray) + sort.SliceStable(locationList.Items, func(i, j int) bool { + return locationList.Items[i].Spec.Default && !locationList.Items[j].Spec.Default + }) return locationList }, }, ) - require.NoError(t, source.Start(ctx, queue)) - // Should not patch a backup storage location object status phase // if the location's validation frequency is specifically set to zero require.NoError(t, client.Create(ctx, &velerov1.BackupStorageLocation{ @@ -172,6 +167,8 @@ func TestOrder(t *testing.T) { LastValidationTime: &metav1.Time{Time: time.Now()}, }, })) + + require.NoError(t, source.Start(ctx, queue)) time.Sleep(2 * time.Second) first, _ := queue.Get() From 038d78057668bb010a9cc1552982d4321b3c7ce5 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Fri, 8 May 2026 20:25:43 -0700 Subject: [PATCH 24/32] Merge pull request #9776 from shubham-pampattiwar/fix-prow-action-permissions Fix prow-action workflow permissions for issue commands --- .github/workflows/prow-action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/prow-action.yml b/.github/workflows/prow-action.yml index a38953ed8..e247590fe 100644 --- a/.github/workflows/prow-action.yml +++ b/.github/workflows/prow-action.yml @@ -5,6 +5,10 @@ on: issue_comment: types: [created] +permissions: + issues: write + pull-requests: write + jobs: execute: runs-on: ubuntu-latest From 35e1e1f988998d19c674db3e6e6f48be7fe99993 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 11 May 2026 11:33:08 +0800 Subject: [PATCH 25/32] Fix unstable UT in pvc_action_test.go's TestExectue(). * Move the error assert out of goroutine. * Create the VSC before patching VS status to ReadyToUse. Signed-off-by: Xun Jiang --- pkg/backup/actions/csi/pvc_action_test.go | 36 ++++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/pkg/backup/actions/csi/pvc_action_test.go b/pkg/backup/actions/csi/pvc_action_test.go index 274371db3..629c5e9cd 100644 --- a/pkg/backup/actions/csi/pvc_action_test.go +++ b/pkg/backup/actions/csi/pvc_action_test.go @@ -227,33 +227,43 @@ func TestExecute(t *testing.T) { pvcMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.pvc) require.NoError(t, err) + var reconcileErrCh chan error if tc.pvc != nil && !tc.failVSCreate && !tc.skipVSReadyUpdate { + reconcileErrCh = make(chan error, 1) go func() { var vsList snapshotv1api.VolumeSnapshotList err := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(ctx context.Context) (bool, error) { - err = pvcBIA.crClient.List(ctx, &vsList, &crclient.ListOptions{Namespace: tc.pvc.Namespace}) - - require.NoError(t, err) - if err != nil || len(vsList.Items) == 0 { + if err := pvcBIA.crClient.List(ctx, &vsList, &crclient.ListOptions{Namespace: tc.pvc.Namespace}); err != nil { return false, err } + if len(vsList.Items) == 0 { + return false, nil + } + return true, nil }) - require.NoError(t, err) + if err != nil { + reconcileErrCh <- err + return + } + vscName := "testVSC" + handleName := "testHandle" + vsc := builder.ForVolumeSnapshotContent("testVSC").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: &handleName}).Result() + err = pvcBIA.crClient.Create(t.Context(), vsc) + if err != nil { + reconcileErrCh <- err + return + } + readyToUse := true vsList.Items[0].Status = &snapshotv1api.VolumeSnapshotStatus{ BoundVolumeSnapshotContentName: &vscName, ReadyToUse: &readyToUse, } err = pvcBIA.crClient.Update(t.Context(), &vsList.Items[0]) - require.NoError(t, err) - - handleName := "testHandle" - vsc := builder.ForVolumeSnapshotContent("testVSC").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: &handleName}).Result() - err = pvcBIA.crClient.Create(t.Context(), vsc) - require.NoError(t, err) + reconcileErrCh <- err }() } @@ -274,6 +284,10 @@ func TestExecute(t *testing.T) { require.NoError(t, err) } + if reconcileErrCh != nil { + require.NoError(t, <-reconcileErrCh) + } + if tc.expectedDataUpload != nil { dataUploadList := new(velerov2alpha1.DataUploadList) err := crClient.List(t.Context(), dataUploadList, &crclient.ListOptions{LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: tc.backup.Name})}) From e78ec188704d1355ba407fb367f1faabfe5c5cc8 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 12 May 2026 11:31:40 +0800 Subject: [PATCH 26/32] bump up kopia 1.23.0 Signed-off-by: Lyndon-Li --- go.mod | 48 ++++++++++---------- go.sum | 139 +++++++++++++++++++++++++++------------------------------ 2 files changed, 90 insertions(+), 97 deletions(-) diff --git a/go.mod b/go.mod index 81bf85c8d..114e9f652 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/vmware-tanzu/velero go 1.26.0 require ( - cloud.google.com/go/storage v1.57.2 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 + cloud.google.com/go/storage v1.62.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3 + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 github.com/aws/aws-sdk-go-v2 v1.24.1 github.com/aws/aws-sdk-go-v2/config v1.26.3 github.com/aws/aws-sdk-go-v2/credentials v1.16.14 @@ -18,7 +18,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/bombsimon/logrusr/v3 v3.0.0 github.com/evanphx/json-patch/v5 v5.9.11 - github.com/fatih/color v1.18.0 + github.com/fatih/color v1.19.0 github.com/gobwas/glob v0.2.3 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 @@ -40,7 +40,7 @@ require ( github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 github.com/vmware-tanzu/crash-diagnostics v0.3.7 - go.uber.org/zap v1.27.1 + go.uber.org/zap v1.28.0 golang.org/x/mod v0.35.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sys v0.43.0 @@ -65,18 +65,18 @@ require ( require ( cel.dev/expr v0.25.1 // indirect - cloud.google.com/go v0.121.6 // indirect + cloud.google.com/go v0.123.0 // indirect cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/iam v1.7.0 // indirect cloud.google.com/go/monitoring v1.24.3 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect @@ -94,7 +94,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect - github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect @@ -105,7 +105,7 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -114,7 +114,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gofrs/flock v0.13.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect @@ -128,30 +128,29 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/klauspost/reedsolomon v1.12.6 // indirect + github.com/klauspost/reedsolomon v1.14.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/minio/crc64nvme v1.1.0 // indirect + github.com/mattn/go-isatty v0.0.21 // indirect + github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/minio-go/v7 v7.0.97 // indirect + github.com/minio/minio-go/v7 v7.1.0 // indirect github.com/moby/spdystream v0.5.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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/mxk/go-vss v1.2.0 // indirect + github.com/mxk/go-vss v1.2.1 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/run v1.1.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -160,10 +159,11 @@ require ( github.com/rs/xid v1.6.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/tinylib/msgp v1.3.0 // indirect + github.com/tinylib/msgp v1.6.1 // indirect github.com/vladimirvivien/gexe v0.1.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect + github.com/zeebo/xxh3 v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect @@ -178,7 +178,7 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.50.0 // indirect - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/term v0.42.0 // indirect @@ -186,7 +186,7 @@ require ( golang.org/x/tools v0.44.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -196,4 +196,4 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) -replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20251230033609-d946b1e75197 +replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20260512025144-908c5c098101 diff --git a/go.sum b/go.sum index f7f6521df..3d8e58fcc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= -al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -24,8 +22,8 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= -cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= @@ -41,12 +39,12 @@ cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCB cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= -cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/iam v1.7.0 h1:JD3zh0C6LHl16aCn5Akff0+GELdp1+4hmh6ndoFLl8U= +cloud.google.com/go/iam v1.7.0/go.mod h1:tetWZW1PD/m6vcuY2Zj/aU0eCHNPuxedbnbRTyKXvdY= cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA= cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= -cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= -cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= +cloud.google.com/go/longrunning v0.9.0 h1:0EzbDEGsAvOZNbqXopgniY0w0a1phvu5IdUFq8grmqY= +cloud.google.com/go/longrunning v0.9.0/go.mod h1:pkTz846W7bF4o2SzdWJ40Hu0Re+UoNT6Q5t+igIcb8E= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -59,19 +57,19 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.57.2 h1:sVlym3cHGYhrp6XZKkKb+92I1V42ks2qKKpB0CF5Mb4= -cloud.google.com/go/storage v1.57.2/go.mod h1:n5ijg4yiRXXpCu0sJTD6k+eMf7GRrJmPyr9YxLXGHOk= +cloud.google.com/go/storage v1.62.1 h1:Os0G3XbUbjZumkpDUf2Y0rLoXJTCF1kU2kWUujKYXD8= +cloud.google.com/go/storage v1.62.1/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA= cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0 h1:ui3YNbxfW7J3tTFIZMH6LIGRjCngp+J+nIFlnizfNTE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0/go.mod h1:gZmgV+qBqygoznvqo2J9oKZAFziqhLZ2xE/WVUmzkHA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= @@ -82,8 +80,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1. github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3 h1:ZJJNFaQ86GVKQ9ehwqyAFE6pIfyicpuJ8IkVaPBc6/4= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3/go.mod h1:URuDvhmATVKqHBH9/0nOiNKk0+YcwfQ3WkK5PqHKxc8= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -103,12 +101,12 @@ github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcv github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -189,8 +187,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= -github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -204,8 +202,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= -github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= +github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= 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= @@ -243,14 +241,12 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8= -github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -271,8 +267,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -309,17 +305,15 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= +github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -425,8 +419,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hanwen/go-fuse/v2 v2.9.0 h1:0AOGUkHtbOVeyGLr0tXupiid1Vg7QB7M6YUcdmVdC58= -github.com/hanwen/go-fuse/v2 v2.9.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI= +github.com/hanwen/go-fuse/v2 v2.10.1 h1:QAqZuc9+aBtTou+OPruU/hkYQYCkgPtQd2QaepHkTTs= +github.com/hanwen/go-fuse/v2 v2.10.1/go.mod h1:aU7NkGYZUmuJrZapoI3mEcNve7PZTySUOLBuch/vR6U= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7NA4= @@ -489,8 +483,8 @@ github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXw github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= @@ -498,11 +492,11 @@ github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/klauspost/reedsolomon v1.12.6 h1:8pqE9aECQG/ZFitiUD1xK/E83zwosBAZtE3UbuZM8TQ= -github.com/klauspost/reedsolomon v1.12.6/go.mod h1:ggJT9lc71Vu+cSOPBlxGvBN6TfAS77qB4fp8vJ05NSA= +github.com/klauspost/reedsolomon v1.14.0 h1:5YSZeclzSYg5nl349+GDG/agDtQ6MZiwUYXvVKN1Jx0= +github.com/klauspost/reedsolomon v1.14.0/go.mod h1:yjqqjgMTQkBUHSG97/rm4zipffCNbCiZcB3kTqr++sQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kopia/htmluibuild v0.0.1-0.20251125011029-7f1c3f84f29d h1:U3VB/cDMsPW4zB4JRFbVRDzIpPytt889rJUKAG40NPA= -github.com/kopia/htmluibuild v0.0.1-0.20251125011029-7f1c3f84f29d/go.mod h1:h53A5JM3t2qiwxqxusBe+PFgGcgZdS+DWCQvG5PTlto= +github.com/kopia/htmluibuild v0.0.1-0.20260502040510-a4505d4145ae h1:igSzPZDDs3icBsXWC/2zRFBRlzelXcBSODpxpORf6s8= +github.com/kopia/htmluibuild v0.0.1-0.20260502040510-a4505d4145ae/go.mod h1:h53A5JM3t2qiwxqxusBe+PFgGcgZdS+DWCQvG5PTlto= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -539,18 +533,18 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= +github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= -github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= +github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ= -github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk= +github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8= +github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -579,8 +573,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/mxk/go-vss v1.2.0 h1:JpdOPc/P6B3XyRoddn0iMiG/ADBi3AuEsv8RlTb+JeE= -github.com/mxk/go-vss v1.2.0/go.mod h1:ZQ4yFxCG54vqPnCd+p2IxAe5jwZdz56wSjbwzBXiFd8= +github.com/mxk/go-vss v1.2.1 h1:shspH0qgqZ9l5sfIRsXS5BgZXz25/BY+ZQsW0HlD0fM= +github.com/mxk/go-vss v1.2.1/go.mod h1:ZQ4yFxCG54vqPnCd+p2IxAe5jwZdz56wSjbwzBXiFd8= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -610,8 +604,6 @@ github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2u github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -626,8 +618,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/project-velero/kopia v0.0.0-20251230033609-d946b1e75197 h1:iGkfuELGvFCqW+zcrhf2GsOwNH1nWYBsC69IOc57KJk= -github.com/project-velero/kopia v0.0.0-20251230033609-d946b1e75197/go.mod h1:RL4KehCNKEIDNltN7oruSa3ldwBNVPmQbwmN3Schbjc= +github.com/project-velero/kopia v0.0.0-20260512025144-908c5c098101 h1:bTzkHkWqMM2Zp942BqDQ3TMrfAKZ6tRTG6vcRBlObps= +github.com/project-velero/kopia v0.0.0-20260512025144-908c5c098101/go.mod h1:VxeLQ3AfxPMOraxoEqzbsOrHPSohTc6CWGs5PgMvtDs= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -725,8 +717,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= -github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= +github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/vladimirvivien/gexe v0.1.1 h1:2A0SBaOSKH+cwLVdt6H+KkHZotZWRNLlWygANGw5DxE= @@ -743,14 +735,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= -github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= -github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= -github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= +github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= @@ -772,8 +766,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:Oyrsyzu go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= @@ -796,8 +790,8 @@ 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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= 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= @@ -826,8 +820,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -993,7 +987,6 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1159,8 +1152,8 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= -google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI= -google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM= google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= From 68fa6f4ee9edadb82ccd1d5899d9320d95d7e9f7 Mon Sep 17 00:00:00 2001 From: Adam Zhang Date: Fri, 24 Apr 2026 15:15:56 +0800 Subject: [PATCH 27/32] backup filter enhancement enhance backup filter with resource policies, extend resource policies with fine-grained control for backup resources, both cluster scoped resources and namespace scoped resources, with labels, names include/exclude support with glob patterns. Signed-off-by: Adam Zhang --- .../fine-grained-backup-filters-design.md | 834 ++++++++++++++++++ 1 file changed, 834 insertions(+) create mode 100644 design/backup-filter-enhancement/fine-grained-backup-filters-design.md diff --git a/design/backup-filter-enhancement/fine-grained-backup-filters-design.md b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md new file mode 100644 index 000000000..284ded16d --- /dev/null +++ b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md @@ -0,0 +1,834 @@ +# Fine Grained Backup Filters via Resource Policies + +## Glossary & Abbreviation + +**Backup Filter**: The mechanism in Velero that determines which Kubernetes resources are collected from the cluster and written into the backup archive. Backup filters currently operate on four dimensions: namespace, resource type, label, and cluster scope. +**Global Filter**: A filter that applies uniformly across all namespaces in a backup. All existing Velero backup filters are global filters. +**Namespace-Scoped Filter**: A filter that applies only within specific namespaces, overriding the global filter for those namespaces. This is the capability introduced by this design. +**FineGrainedGlobalFilterPolicy**: A global filter for cluster-scoped resources that allows per-kind label selectors and name patterns, functioning similarly to `NamespacedFilterPolicy` but applied to cluster-scoped resources globally. +**Resource Filter**: A filter rule that pairs one or more resource kinds with their own label selector and/or name patterns. Multiple resource filters within a namespace-scoped policy allow different filtering criteria for different resource types. +**Resource Name Filter**: A filter that matches individual resource instances by their metadata.name, using glob patterns. This is a new filter dimension introduced by this design. +**Resource Policy**: An existing Velero mechanism where backup behavior rules are defined in a ConfigMap and referenced from `BackupSpec.ResourcePolicy`. Currently used for volume policies and global include/exclude policies. + +## Background + +Velero's backup filter system allows users to specify which resources to include or exclude from a backup. The filters operate on three dimensions: + +1. **Namespace** — `IncludedNamespaces`/`ExcludedNamespaces` select which namespaces to back up +2. **Resource Type** — `IncludedResources`/`ExcludedResources` (or the newer scoped variants `Included/ExcludedClusterScopedResources`, `Included/ExcludedNamespaceScopedResources`) select which Kubernetes resource types to back up +3. **Labels** — `LabelSelector`/`OrLabelSelectors` filter individual objects by their labels + +All three dimensions are applied **globally** — the same resource type filter, the same label selector, and the same namespace list apply uniformly throughout the entire backup operation. Specifically: + +- In `item_collector.go`, the `ResourceIncludesExcludes.ShouldInclude()` check is a single global check applied to every resource type across all namespaces. +- In `listResourceByLabelsPerNamespace()`, the same `LabelSelector` is passed to every Kubernetes API list call regardless of namespace. +- There is no mechanism to filter resources by their individual `metadata.name`. + +This creates three critical gaps for common backup scenarios: + +**Gap 1: Different resource needs per namespace.** When multiple applications share the same cluster, different namespaces often require different backup strategies. For example, a namespace running a database workload may need all resource types backed up, while a namespace running a stateless frontend may only need Deployments, ConfigMaps, and Services. Setting `IncludedResources: [configmaps]` means *all* ConfigMaps in *all* included namespaces — you cannot say "only ConfigMaps in namespace-a but everything in namespace-b." + +**Gap 2: Same resource type, different workloads.** Resources of the same type (e.g., ConfigMaps or Secrets) in the same namespace may belong to different workloads. For instance, a namespace may contain `app-config`, `app-secret`, `monitoring-config`, and `monitoring-secret`. Without name-based filtering, you cannot selectively back up only the `app-*` resources — the only option is label-based selection, which requires workloads to have been pre-labeled appropriately. + +**Gap 3: Different kinds need different selectors.** Within a single namespace, different resource types may belong to different workloads with different labels. For example, Deployments labeled `app=workload-1` and StatefulSets labeled `app=workload-2` in the same namespace. The current single-label-selector-per-namespace model cannot express this — the label selector applies identically to all resource types. + +## Goals + +- Extend the `ResourcePolicies` ConfigMap format with a `namespacedFilterPolicies` section that allows per-namespace, per-kind resource filtering with independent label selectors and name patterns for each resource type +- Extend the `ResourcePolicies` ConfigMap format with a `fineGrainedGlobalFilterPolicy` section that allows per-kind resource filtering with independent label selectors and name patterns for cluster-scoped resources globally +- Support resource name filtering by glob patterns using the same `gobwas/glob` library that Velero uses for namespace patterns, ensuring consistency across the codebase +- Support per-kind label selectors, so that different resource types within the same namespace can be filtered with different labels +- Maintain full backward compatibility — existing backups with no `namespacedFilterPolicies` behave exactly as they do today +- Define clear precedence rules for how per-namespace filters interact with global filters +- Add corresponding validation within the Resource Policies validation pipeline using existing Velero wildcard validation functions +- Update `velero backup describe` output to display per-namespace filter information when present +- Ensure the restore process works correctly with backups produced by namespace-scoped filters, without requiring restore-side code changes in the initial phase + +## Non-Goals + +- Adding namespace-scoped filters to `RestoreSpec` or the restore pipeline is not part of the initial implementation. Restore from a namespace-filtered backup works automatically because the restore process reads whatever is in the backup archive. Restore-side namespace filters will be addressed in a follow-up. +- Changing existing `BackupSpec` fields (`IncludedResources`, `LabelSelector`, etc.) or adding new CRD fields is explicitly avoided by this design. +- Supporting regex patterns for resource names is not included. Glob patterns (already used throughout Velero) are sufficient and consistent. +- Modifying the plugin `ResourceSelector` system (`AppliesTo()` / `resolvedAction.ShouldUse()`) is not part of this design. +- CLI flags for inline specification of namespace-scoped filters are not part of the initial implementation. The configuration is expressed in the ResourcePolicy ConfigMap YAML. + +## Architecture of Namespace-Scoped Filters + +### Configuration Model + +The namespace-scoped filters and fine-grained global filters are defined in the same ResourcePolicy ConfigMap that is already referenced by `BackupSpec.ResourcePolicy`. The YAML format is extended with two new top-level keys: + +```yaml +version: v1 +volumePolicies: + # existing volume policies (unchanged) + - conditions: + capacity: "0,100Gi" + action: + type: skip +includeExcludePolicy: + # existing global include/exclude policy (unchanged) + includedNamespaceScopedResources: + - configmaps +fineGrainedGlobalFilterPolicy: + # NEW: global overrides for cluster-scoped resources + resourceFilters: + - kinds: [ClusterRole, ClusterRoleBinding] + names: ["my-app-*"] + - kinds: [CustomResourceDefinition] + labelSelector: + app: my-app +namespacedFilterPolicies: + # NEW: per-namespace filter overrides + - namespaces: + - ns-a + resourceFilters: + - kinds: [ConfigMap, Secret, Deployment] + labelSelector: + app: my-app + - namespaces: + - ns-b + resourceFilters: + - kinds: [Deployment] + names: [app-1, app-2] + - kinds: [ConfigMap] + labelSelector: + app: my-service +``` + +All four sections coexist in the same ConfigMap. They are independent — `volumePolicies` handles volume backup strategy, `includeExcludePolicy` handles global resource type filtering, `fineGrainedGlobalFilterPolicy` handles cluster-scoped resource filtering by kind/name/label, and `namespacedFilterPolicies` handles per-namespace, per-kind overrides. + +### The `resourceFilters` Model + +Each `namespacedFilterPolicies` entry targets one or more namespaces and contains a `resourceFilters` array. Each entry in `resourceFilters` pairs one or more resource kinds with their own label selector and name patterns: + +```yaml +namespacedFilterPolicies: + - namespaces: [ns-a] + resourceFilters: + - kinds: [ConfigMap, Secret] # these kinds share a selector + labelSelector: {app: my-app} + names: ["app-*"] + - kinds: [Deployment] # this kind has its own selector + names: [workload-1, workload-2] + - kinds: [StatefulSet] # this kind has no extra filtering +``` + +This model has one way to express filters — there is no ambiguity about how to structure the configuration. Only resource kinds listed in `resourceFilters` entries are included in the backup for the matched namespaces; unlisted kinds are implicitly excluded. + +#### Catch-All Resource Filter (Empty `kinds` or `["*"]`) + +A `ResourceFilter` entry with an empty (or omitted) `kinds` field, or a field explicitly set to `["*"]`, acts as a **catch-all**. Its `labelSelector` or `orLabelSelectors` (if provided) is applied to **all resource types in the namespace that are not already matched by a kind-specific filter entry**. If no selectors are provided, all unlisted resources are included. Using `["*"]` is highly recommended as it makes the catch-all intention explicit and self-documenting. + +**Rules for catch-all entries:** +- At most **one** catch-all entry is allowed per `NamespacedFilterPolicy`. +- `names` and `excludedNames` are **not** supported on catch-all entries. Name patterns are kind-specific by nature and cannot be applied across arbitrary kinds; use kind-specific entries for name-based filtering. +- The catch-all applies to kinds that are **not listed in any other `resourceFilters` entry** in the same policy. Kind-specific entries take precedence over the catch-all. + +**Evaluation order within a namespace filter policy:** +1. For each resource kind encountered during backup, the system first checks whether a kind-specific `resourceFilters` entry exists for that kind. +2. If a kind-specific entry exists, it is used exclusively (its label selectors and name patterns apply). +3. If no kind-specific entry exists but a catch-all entry is present, the catch-all's `labelSelector`/`orLabelSelectors` is applied to that kind. +4. If neither a kind-specific entry nor a catch-all entry exists, the kind is excluded from the backup for that namespace. + +### Filter Precedence Model + +The namespace-scoped filter system and fine-grained global filter system layer on top of the existing global filter system. They intentionally behave differently: +- **`namespacedFilterPolicies`** acts as an **exclusive allowlist (boundary)**. Only kinds explicitly listed (or matched by a catch-all) are backed up from that namespace. This gives namespace owners complete and isolated control over their namespace's backup contents, preventing unexpected data spillage from global fallbacks. +- **`fineGrainedGlobalFilterPolicy`** acts as a **refinement overlay (tweak)**. Unlisted cluster-scoped kinds fall back to the standard global filters. This allows administrators to selectively adjust filtering for a few specific cluster-scoped kinds without rewriting the entire global inclusion list. + +**For Namespace-Scoped Resources:** + +The evaluation order is: + +1. **Global namespace filter** (`BackupSpec.IncludedNamespaces`/`ExcludedNamespaces`) is checked first. A namespace must pass this filter to be considered at all. `namespacedFilterPolicies` cannot override namespace exclusion — if a namespace is excluded globally, no filter policy entry can bring it back. + +2. **Per-namespace filter lookup.** For each namespace that passes the global namespace filter, the system checks whether any `namespacedFilterPolicies` entry matches (by namespace name or glob pattern). If a match is found, the `resourceFilters` array determines what gets backed up for that namespace: + - Only resource kinds listed in `resourceFilters[].kinds` are included + - Each kind uses its own `labelSelector`/`orLabelSelectors` (if specified) + - Each kind uses its own `names`/`excludedNames` patterns (if specified) + +3. **Namespaces without a matching filter policy** continue to use the global filters (`BackupSpec.IncludedResources`, `BackupSpec.LabelSelector`, etc., combined with `includeExcludePolicy`) exactly as they do today. + +4. **If multiple filter policy entries could match the same namespace** (e.g., `team-*` and `team-frontend-*` both matching `team-frontend-prod`), the **first matching policy in the list** is used. **Important: Place more specific patterns before broader patterns** to achieve the intended filtering behavior. + +5. **The `velero.io/exclude-from-backup=true` label** always takes precedence over all filters, regardless of whether the item matches global or per-namespace filters. + +6. **Interaction with `includeExcludePolicy`**: `namespacedFilterPolicies` is a **refinement** of the global resource filter system, not a replacement. Global exclusions defined in `includeExcludePolicy` (e.g., `excludedNamespaceScopedResources: [secrets]`) are applied first at the resource-type level before per-namespace filter policies are consulted. A namespace-scoped filter policy cannot re-include a resource kind that has been globally excluded by `includeExcludePolicy`. For example, if `secrets` is listed under `excludedNamespaceScopedResources`, no `Secret` resources will be backed up from any namespace, even if a `namespacedFilterPolicies` entry explicitly lists `Secret` for that namespace. Users who need per-namespace secret selection must remove `secrets` from the global exclusion list. + +**For Cluster-Scoped Resources:** + +1. If `fineGrainedGlobalFilterPolicy` is present, it acts as a **refinement overlay** over the existing global filters for cluster-scoped resources. It is NOT an exclusive allowlist. + - To back up cluster-scoped resources in a namespace-filtered backup, you must still explicitly include them via `BackupSpec.IncludedClusterScopedResources`. + - If a cluster-scoped kind is listed in its `resourceFilters`, its specific `labelSelector`/`orLabelSelectors` and `names`/`excludedNames` patterns are applied. + - If a cluster-scoped kind is **not listed**, it falls back to the standard global filters (`BackupSpec.LabelSelector`, etc.) and is included in the backup. + +2. If `fineGrainedGlobalFilterPolicy` is absent, Velero falls back to the existing global filters (`IncludedClusterScopedResources`, `IncludedResources`, `LabelSelector`, etc.) for cluster-scoped resources. + +3. **The `velero.io/exclude-from-backup=true` label** always takes precedence over all filters. + +```mermaid +flowchart TD + A["BackupSpec Global
IncludedNamespaces / ExcludedNamespaces"] + B{Namespace passes
global filter?} + C[Namespace excluded
from backup] + D{namespacedFilterPolicies
lookup by namespace} + E{"For each resource kind:
is kind in resourceFilters?"} + F["Apply kind-specific filters:
- labelSelector / orLabelSelectors
- names / excludedNames"] + G[Kind skipped for
this namespace] + H["Use global filters:
- BackupSpec IncludedResources
- BackupSpec LabelSelector
- includeExcludePolicy"] + + I{"Is resource
cluster-scoped?"} + J{"Is fineGrainedGlobalFilterPolicy
present?"} + K{"Is kind in resourceFilters?"} + L["Apply kind-specific filters:
- labelSelector / orLabelSelectors
- names / excludedNames"] + + I -- Yes --> J + J -- Yes --> K + K -- Yes --> L + K -- No --> H + J -- No --> H + + I -- No --> A + A --> B + B -- No --> C + B -- Yes --> D + D -- Match found --> E + E -- Yes --> F + E -- No --> G + D -- No match found --> H +``` + +### Data Flow in the Backup Pipeline + +The existing backup pipeline has two stages: item collection and item backup. Namespace-scoped filters and fine-grained global filters are applied at both stages: + +**Stage 1 — Item Collection (`item_collector.go`).** Resources are listed from the Kubernetes API. + +- **Resource type check** in `getResourceItems()`: Before iterating namespaces, the global resource type check still applies. + - **For Cluster-Scoped Resources:** The global resource type check (`ShouldInclude`) determines if the kind is collected. `FineGrainedGlobalFilterPolicy` does not skip unlisted cluster-scoped kinds at this stage. + - **For Namespace-Scoped Resources:** Within the namespace loop, a per-namespace resource type check is added. If a filter policy matches the current namespace, only resource kinds listed in `resourceFilters[].kinds` are included — if the current resource type is not listed, it is skipped for that namespace. +- **Label selector** in `listResourceByLabelsPerNamespace()` and `listResourceByLabelsGlobally()`: The function looks up the filter policy (either the namespace-specific one or the fine-grained global one). If found, it retrieves the `ResourceFilter` entry for the current resource kind and uses that entry's `labelSelector`/`orLabelSelectors` for the Kubernetes API list call. If no filter policy is found, the global selectors are used as before. + +**Stage 2 — Item Backup (`item_backupper.go`).** Collected items are validated and written to the archive. + +- **Name pattern check** in `itemInclusionChecks()`: After the existing namespace and resource type re-validation, the item's `metadata.name` is checked against the `ResourceFilter` entry's `names`/`excludedNames` glob patterns for the item's kind (checking the cluster-scoped map for cluster resources and namespace map for namespace resources). If the name doesn't match, the item is excluded. + - **Important:** If the item's kind is not listed in the namespace filter map **and** there is no catch-all entry, the item passes through Stage 2 without a name check. This is intentional — see [Plugin AdditionalItems and Auto-Backed Up CRDs](#edge-cases-and-behavior-documentation) below. + +### Impact on Restore + +The restore process (`pkg/restore/restore.go`) is **not modified** in this design. The reason: + +- Restore reads the backup archive as-is. Items excluded by namespace-scoped filter policies during backup are simply absent from the archive. The restore process iterates what's in the tarball and applies `RestoreSpec` filters on top. No items excluded during backup will appear during restore. +- Restore plugins that request "additional items" (via `RestoreItemAction`) may reference items excluded from the backup. These items won't be in the archive, so the restore will skip them silently. This is the same behavior that occurs today with any incomplete backup — no new risk is introduced. +- Users can still use `RestoreSpec.IncludedNamespaces` to selectively restore from a namespace-filtered backup. + +A follow-up design will add namespace-scoped filters to the restore pipeline. + +### Edge Cases and Behavior Documentation + +This section documents the system behavior in edge cases and error conditions: + +**Plugin AdditionalItems and Auto-Backed Up CRDs:** +Cluster-scoped resources injected dynamically (such as `VolumeSnapshotClass` from the CSI plugin or `CustomResourceDefinition` from Velero's auto-backup loop) do not require hardcoded exceptions. In `itemInclusionChecks()`, Velero natively allows unlisted cluster-scoped resources to pass through unless explicitly excluded by the user. `FineGrainedGlobalFilterPolicy` preserves this permissive behavior: if a dynamically injected cluster-scoped resource is NOT listed in the policy, it passes through untouched. If it IS listed, its specific `names` and `excludedNames` filters are strictly enforced. + +**Plugin-injected namespace-scoped additional items** follow the same permissive principle. When a `BackupItemAction` returns additional items whose kind is not listed in the matched `namespacedFilterPolicies` entry and there is no catch-all entry, those items still pass through Stage 2 (`itemInclusionChecks`). This is intentional: blocking plugin-injected items at Stage 2 would break backup completeness — for example, a CSI plugin may inject a `VolumeSnapshotContent` that is required for a correct restore even when the user's filter policy only lists application resource types. + +The kind-level exclusion that makes `namespacedFilterPolicies` an exclusive allowlist applies only during **Stage 1** (the primary collection pass in `item_collector.go`). At Stage 2, `itemInclusionChecks` enforces only: +- The `velero.io/exclude-from-backup=true` label (always takes precedence). +- The `names`/`excludedNames` patterns for **listed** kinds. + +Plugin-injected items of unlisted kinds are therefore included as long as they are not explicitly excluded by label. Users who need to suppress a specific plugin-injected kind should apply the `velero.io/exclude-from-backup=true` label to those resources. + +**Multiple Glob Patterns Matching Same Namespace (Incorrect Order):** +```yaml +namespacedFilterPolicies: + - namespaces: ["team-*"] # Broader pattern listed first + resourceFilters: + - kinds: [Deployment, Service] + - namespaces: ["team-frontend-*"] # More specific pattern listed second + resourceFilters: + - kinds: [ConfigMap, Secret, Deployment, Service] +``` +**Behavior:** For namespace `team-frontend-prod`, the broader `team-*` pattern matches first, so only `Deployment` and `Service` are backed up. The more specific `team-frontend-*` rule is never reached. + +**Multiple Glob Patterns Matching Same Namespace (Correct Order):** +```yaml +namespacedFilterPolicies: + - namespaces: ["team-frontend-*"] # More specific pattern listed first + resourceFilters: + - kinds: [ConfigMap, Secret, Deployment, Service] + - namespaces: ["team-*"] # Broader pattern listed second + resourceFilters: + - kinds: [Deployment, Service] +``` +**Behavior:** For namespace `team-frontend-prod`, the specific `team-frontend-*` pattern matches first, backing up all specified resources. For `team-backend-dev`, the broader `team-*` pattern matches, backing up only `Deployment` and `Service`. This achieves the intended behavior. + +**Namespace Included Globally But No Matching Filter Policy:** +```yaml +# BackupSpec includes "production" namespace +# ResourcePolicy has no namespacedFilterPolicies entry for "production" +``` +**Behavior:** The namespace uses global filters exactly as it does today. This is the backward compatibility behavior — only namespaces with explicit filter policies get namespace-scoped filtering. + + +**Empty ResourceFilters Array:** +```yaml +namespacedFilterPolicies: + - namespaces: ["test-namespace"] + resourceFilters: [] # empty array +``` +**Behavior:** Validation error during backup creation: +``` +namespacedFilterPolicies[0]: at least one resourceFilter must be specified +``` + +**Namespace Pattern with No Matches:** +```yaml +namespacedFilterPolicies: + - namespaces: ["nonexistent-*"] + resourceFilters: [...] +``` +**Behavior:** No error. The filter policy is loaded but never applied since no namespaces match the pattern. This allows for conditional filtering based on namespace existence. + +**Resource Kind Not Present in Target Namespaces:** +```yaml +resourceFilters: + - kinds: ["StatefulSet"] # namespace has no StatefulSets + names: ["workload-1"] +``` +**Behavior:** No error. The filter is applied but finds no matching resources. Empty result set is valid. + +**Conflicting Name Patterns:** +```yaml +resourceFilters: + - kinds: ["ConfigMap"] + names: ["app-*"] + excludedNames: ["app-config"] # conflicts with names pattern +``` +**Behavior:** The `excludedNames` takes precedence. Resources matching `app-*` are included, then `app-config` is excluded. Net result: includes `app-secret`, `app-data`, etc., but excludes `app-config`. + +**Invalid Label Selector Syntax:** +```yaml +resourceFilters: + - kinds: ["Pod"] + labelSelector: + "invalid label key!": "value" # invalid key syntax +``` +**Behavior:** Validation error during backup creation when `labels.SelectorFromSet()` fails: +``` +namespacedFilterPolicies[0].resourceFilters[0]: invalid label selector: "invalid label key!" is not a valid label key +``` + +**Out-of-Scope Kinds in Filter Entries:** +A user may accidentally list a cluster-scoped kind (e.g., `ClusterRole`) inside a `namespacedFilterPolicies` entry, or a namespace-scoped kind (e.g., `ConfigMap`) inside `fineGrainedGlobalFilterPolicy`. The system silently ignores such entries at the Kubernetes API level — namespace-scoped items are never listed globally, and cluster-scoped items are never listed per-namespace, so no matching resources will ever be found. A warning is logged at backup start to help the user detect the misconfiguration. No validation error is raised — the entry is harmless but ineffective. + +**Discovery Helper Unavailable:** +If the discovery helper is completely unavailable during backup initialization, the backup fails with: +``` +failed to resolve namespace filter policies: discovery client unavailable +``` +This is consistent with how other discovery-dependent features handle this error condition. + +# Detailed Design + +## ResourceFilter Field Notes + +**`labelSelector`** supports equality-based selectors only (`key=value`). Set-based requirements (e.g., `environment in (prod, staging)`) are not supported. To match resources with any of several label combinations, use `orLabelSelectors` with multiple maps — each map is AND-evaluated internally, and the maps are OR-evaluated across the list. `labelSelector` and `orLabelSelectors` cannot co-exist in the same entry. + +**`names` / `excludedNames`** accept exact resource names or glob patterns. If `names` is empty, all resource names are included (subject to label filters). `excludedNames` takes precedence over `names` when a name matches both. + +## Validation + +**Validation functions for `namespacedFilterPolicies`:** + +1. **Each filter policy must specify at least one namespace:** + ``` + namespacedFilterPolicies[N]: at least one namespace must be specified + ``` + +2. **Each filter policy must specify at least one resource filter:** + ``` + namespacedFilterPolicies[N]: at least one resourceFilter must be specified + ``` + +3. **Each resource filter without kinds can only be defined once, and cannot specify names/excludedNames. Additionally, it must specify either labelSelector or orLabelSelectors:** + ``` + namespacedFilterPolicies[N]: only one resource filter with empty kinds is allowed + namespacedFilterPolicies[N].resourceFilters[M]: names or excludedNames cannot be specified when kinds is empty + namespacedFilterPolicies[N].resourceFilters[M]: labelSelector or orLabelSelectors must be specified when kinds is empty + ``` + +4. **No duplicate kinds across resource filter entries** within the same namespace filter: + ``` + namespacedFilterPolicies[N]: kind "Pod" appears in both resourceFilters[0] and resourceFilters[2] + ``` + +5. **`labelSelector` and `orLabelSelectors` mutual exclusion** within each resource filter: + ``` + namespacedFilterPolicies[N].resourceFilters[M]: labelSelector and orLabelSelectors cannot co-exist + ``` + +6. **No duplicate namespace patterns across filter policies.** This validates only exact duplicates - runtime behavior handles overlapping patterns. + + **Rationale:** Detecting all possible pattern overlaps (like `team-*` vs `team-frontend-*`) is computationally complex and may reject valid configurations. Instead, the runtime uses first-match semantics - the first matching filter policy in the list is applied. This allows users flexibility while preventing obvious configuration errors. + +7. **Namespace patterns must be valid globs.** + +8. **Resource name patterns must be valid globs.** + +9. **Resource kind validation with discovery helper** (performed during backup initialization). + +**Validation functions for `fineGrainedGlobalFilterPolicy`:** + +1. **At least one resourceFilter must be specified.** + ``` + fineGrainedGlobalFilterPolicy: at least one resourceFilter must be specified + ``` + +2. **No duplicate kinds across resource filters.** + +3. **`labelSelector` and `orLabelSelectors` mutual exclusion.** + +4. **Resource name patterns must be valid globs.** + +Additionally, in `backup_controller.go`, a validation check ensures that `namespacedFilterPolicies` and `fineGrainedGlobalFilterPolicy` are not used with old-style resource filters (`IncludedResources`/`ExcludedResources`/`IncludeClusterResources`), similar to the existing check for `includeExcludePolicy`. + +## ConfigMap Examples + +### Per-Namespace Resource Type Filtering + +Back up only ConfigMaps, Secrets, and Deployments (with label `app=my-app`) from `ns-a`, but everything from `ns-b`: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: backup-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - ns-a + resourceFilters: + - kinds: [ConfigMap, Secret, Deployment] + labelSelector: + app: my-app + # ns-b has no filter policy entry, so global filters apply (include everything) +``` + +Backup CR referencing it: + +```yaml +apiVersion: velero.io/v1 +kind: Backup +metadata: + name: selective-backup + namespace: velero +spec: + includedNamespaces: + - ns-a + - ns-b + resourcePolicy: + kind: configmap + name: backup-filter-policy + storageLocation: default + ttl: 720h0m0s +``` + +### Per-Kind Label Selectors (Different Labels per Kind) + +Back up Deployments with one label and StatefulSets with a different label from the same namespace: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: vm-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - target-namespace + resourceFilters: + - kinds: [Deployment] + labelSelector: + app: production-workload-1 + - kinds: [StatefulSet] + labelSelector: + app: production-workload-2 +``` + +### Per-Kind Exact Names + +Back up specific Deployments, Configmaps, and Secrets by name: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: named-resource-filter + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - target-namespace + resourceFilters: + - kinds: [Deployment] + names: [workload-1, workload-2] + - kinds: [ConfigMap] + names: [p1, p2] + - kinds: [Secret] + names: [c1, c2] +``` + +### Name Pattern Filtering with Exclusion + +Back up only `app-*` ConfigMaps and Secrets from `production`, excluding temporary and debug resources: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - production + resourceFilters: + - kinds: [ConfigMap, Secret] + names: ["app-*"] + excludedNames: ["*-tmp", "*-debug"] +``` + +### Catch-All with No Label Selector (Override-Only) + +A user may want to use the global configuration for 99% of resources in a namespace, but only apply a specific name filter to a single kind. To achieve this without explicitly listing all other kinds or adding dummy labels, a catch-all filter without a label selector can be used: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: override-only-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - ns-a + resourceFilters: + - kinds: [Secret] + names: [my-secret] # Specific override for Secrets + - kinds: ["*"] # Catch-all: NO label selector + # Includes all other kinds unconditionally +``` + +**Result:** +- `Secret` resources: only `my-secret` is backed up. +- All other resource types: backed up unconditionally (acting like a global fallback). + +### Catch-All Label Selector (Back Up Everything with a Specific Label) + +When a user wants to back up any resource type in a namespace that carries a particular label — without enumerating every kind — the catch-all entry (empty `kinds` or `["*"]`) achieves this with a single rule: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: label-based-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - production + resourceFilters: + - kinds: ["*"] # catch-all: applies to every kind not listed below + labelSelector: + backup: "true" # back up any resource carrying this label +``` + +**Result:** Every resource type in `production` that has the label `backup=true` is backed up. Resources without that label are excluded. No kind enumeration is required. + +### Catch-All with Per-Kind Name Overrides + +A more advanced pattern: use exact names for specific kinds and fall back to a label selector for all remaining kinds. Kind-specific entries take precedence over the catch-all: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: mixed-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + - namespaces: + - production + resourceFilters: + - kinds: [Deployment] + names: [api-server, worker] # these exact Deployments by name + - kinds: [Secret] + names: [db-credentials, tls-cert] # these exact Secrets by name + - kinds: ["*"] # catch-all for all other kinds + labelSelector: + backup: "true" # back up by label +``` + +**Result:** +- `Deployment` resources: only `api-server` and `worker` are backed up (name filter; the catch-all does not apply). +- `Secret` resources: only `db-credentials` and `tls-cert` are backed up (name filter; the catch-all does not apply). +- All other resource types (ConfigMap, StatefulSet, Service, etc.): backed up only if they carry `backup=true`. + +This pattern is useful when certain high-value resources need precise name-based selection, while the rest of the namespace is covered by a label convention. + +### Glob Namespace Patterns and Ordering + +Apply filters to namespaces matching patterns. **Critical: Order patterns from most specific to least specific:** + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: team-filter-policy + namespace: velero +data: + policy: | + version: v1 + namespacedFilterPolicies: + # More specific patterns first + - namespaces: + - "team-frontend-prod" # Most specific (exact match) + resourceFilters: + - kinds: [Deployment, Service, ConfigMap, Secret, PVC] + - namespaces: + - "team-frontend-*" # Less specific (pattern match) + resourceFilters: + - kinds: [Deployment, Service, ConfigMap] + - namespaces: + - "team-*" # Least specific (broad pattern) + resourceFilters: + - kinds: [Deployment, Service] +``` + +**Pattern Matching Results:** +- `team-frontend-prod` → Uses exact match policy (backs up 5 resource types) +- `team-frontend-dev` → Uses `team-frontend-*` policy (backs up 3 resource types) +- `team-backend-test` → Uses `team-*` policy (backs up 2 resource types) +- `app-namespace` → No match, uses global filters + +### Combined with Volume Policies + +Both volume policies and namespace-scoped filters in the same ConfigMap: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: combined-policy + namespace: velero +data: + policy: | + version: v1 + volumePolicies: + - conditions: + capacity: "0,10Gi" + storageClass: + - standard + action: + type: fs-backup + - conditions: + capacity: "10Gi,100Gi" + action: + type: snapshot + namespacedFilterPolicies: + - namespaces: + - production + resourceFilters: + - kinds: [Deployment] + names: [workload-1, workload-2] + - kinds: [StatefulSet] + labelSelector: + app: my-app + - kinds: [ConfigMap, Secret] + names: ["app-*"] + excludedNames: ["*-tmp", "*-debug"] +``` + +### Backup CR — No ResourcePolicy (backward compatible) + +Existing backups continue to work exactly as before: + +```yaml +apiVersion: velero.io/v1 +kind: Backup +metadata: + name: full-backup + namespace: velero +spec: + includedNamespaces: + - "*" + includedResources: + - "*" + labelSelector: + matchLabels: + backup: "true" + storageLocation: default +``` + +## CLI + +### `velero backup describe` + +The output is extended to display namespace-scoped filter policies when present in the ResourcePolicy ConfigMap: + +``` +Name: selective-backup +Namespace: velero +Labels: +Annotations: + +Phase: Completed + +Errors: 0 +Warnings: 0 + +Namespaces: + Included: ns-a, ns-b + Excluded: + +Resources: + Included: * + Excluded: + Cluster-scoped: auto + +Label selector: + +Resource Policy: backup-filter-policy + +Namespace-Scoped Filter Policies: + ns-a: + Resource Filters: + ConfigMap, Secret, Deployment: + Label selector: app=my-app + Included names: + Excluded names: + target-namespace: + Resource Filters: + Deployment: + Label selector: app=production-workload-1 + Included names: + Excluded names: + StatefulSet: + Label selector: app=production-workload-2 + Included names: + Excluded names: + production: + Resource Filters: + Deployment: + Label selector: + Included names: [api-server, worker] + Excluded names: + (all other kinds): + Label selector: backup=true + Included names: + Excluded names: + +Fine-Grained Global Filter Policy: + Resource Filters: + ClusterRole, ClusterRoleBinding: + Label selector: + Included names: [my-app-*] + Excluded names: + CustomResourceDefinition: + Label selector: app=my-app + Included names: + Excluded names: + +Storage Location: default + +... +``` + +### `velero backup create` + +No new CLI flags are added. The namespace-scoped filter policies are specified in the ResourcePolicy ConfigMap, which is already referenced via the existing `--resource-policies-configmap` flag: + +```bash +velero backup create selective-backup \ + --include-namespaces ns-a,ns-b \ + --resource-policies-configmap backup-filter-policy +``` + +The `--help` output for `velero backup create` is updated to clarify the interaction between global and namespace-scoped filters: + +``` +Backup Filtering Options: + --include-namespaces stringArray namespaces to include in the backup (use '*' for all namespaces) + --exclude-namespaces stringArray namespaces to exclude from the backup + --include-resources stringArray resources to include in the backup, formatted as resource.group + --exclude-resources stringArray resources to exclude from the backup, formatted as resource.group + --include-cluster-resources optionalBool[=true] include cluster-scoped resources + --exclude-cluster-resources exclude cluster-scoped resources + --selector labelSelector only back up resources matching this label selector + --or-selector labelSelector back up resources matching any of the label selectors (can be repeated) + --resource-policies-configmap string reference to a configmap containing resource policies for volume snapshots and namespace-scoped filtering + +Notes: +- Global filters (--include-resources, --selector, etc.) apply to all included namespaces +- Namespace-scoped filters defined in --resource-policies-configmap override global filters for matching namespaces +- Fine-grained global filter policies defined in --resource-policies-configmap override global filters for cluster-scoped resources +- Use 'velero backup describe' to view resolved filter policies after backup creation +``` + +### CLI Integration Points + +**Backup Creation Workflow:** +1. User creates ResourcePolicy ConfigMap with `namespacedFilterPolicies` +2. User references ConfigMap via `--resource-policies-configmap` flag +3. Backup controller validates policies during backup initialization +4. Validation errors are reported immediately with specific line/field references + +**Help and Discovery:** +- `velero backup create --help` includes updated filtering documentation +- `velero backup describe` shows resolved filter policies for troubleshooting +- Validation errors include ConfigMap field references for easy debugging + +**Configuration Discovery:** +- `velero backup create --help` includes namespace-scoped filtering documentation +- `velero backup describe` shows resolved filter policies for verification + +## User Perspective + +This design provides fine-grained, per-namespace, per-kind control over backup filtering. Key user-facing aspects: + +- **For users not using namespace-scoped filter policies**: Zero changes. All existing backups and workflows continue to work identically. The new YAML key is optional. +- **For users adopting namespace-scoped filter policies**: Create a ConfigMap with the `namespacedFilterPolicies` section and reference it via `BackupSpec.ResourcePolicy` (or the existing `--resource-policies-configmap` flag). The backup will selectively include/exclude resources per namespace based on the filter rules. +- **For users already using ResourcePolicy for volume policies**: Add the `namespacedFilterPolicies` section to the same ConfigMap. Both volume policies and namespace-scoped filters coexist. +- **For restore from a namespace-filtered backup**: No changes to restore workflow. Restore processes whatever is in the archive. Users can use existing `RestoreSpec.IncludedNamespaces` for additional filtering at restore time. +- **`velero backup describe` output**: Extended to show per-namespace, per-kind filter details when the ResourcePolicy ConfigMap contains `namespacedFilterPolicies`. +- **Validation errors**: Reported at backup start when the ResourcePolicy ConfigMap contains invalid `namespacedFilterPolicies` configurations. Consistent with how volume policy validation errors are reported today. + +## Alternatives Considered + +1. **CRD-Based `NamespacedFilters` Field**: Add `NamespacedFilters []NamespaceFilter` directly to `BackupSpec`. Rejected for this iteration due to heavy CRD change overhead. The ResourcePolicy approach achieves the same functionality with less API surface change. + +2. **Flat Fields on NamespacedFilterPolicy (No Per-Kind Selectors)**: Use flat fields (`includedResources`, `labelSelector`, `includedResourceNames`) shared across all kinds within a namespace. Rejected because it cannot express per-kind label selectors or per-kind name lists — a critical requirement for workloads where different resource types have different labels or naming conventions. + +3. **Scoped Label Selectors Only**: Augment existing label selectors with an optional namespace scope. Rejected because it only addresses label-scoped filtering and does not support per-namespace resource type filtering or name filtering. + +4. **Global Name Filter Only**: Add only global name filter fields. Rejected because it only addresses name filtering globally and does not address namespace-scoped or kind-scoped filtering. + +5. **Separate ConfigMap for Namespace Filters**: Use a new `BackupSpec` field pointing to a different ConfigMap (separate from volume policies). Rejected because it adds a new CRD field (which has similar reasons with #1) and splits configuration across multiple ConfigMaps. From 8ef7e36054b745f9040120b4d1689c3b0d2872e6 Mon Sep 17 00:00:00 2001 From: Adam Zhang Date: Fri, 15 May 2026 23:34:17 +0800 Subject: [PATCH 28/32] Update design/backup-filter-enhancement/fine-grained-backup-filters-design.md Co-authored-by: Tiger Kaovilai Signed-off-by: Adam Zhang --- .../fine-grained-backup-filters-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/backup-filter-enhancement/fine-grained-backup-filters-design.md b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md index 284ded16d..a54663a1f 100644 --- a/design/backup-filter-enhancement/fine-grained-backup-filters-design.md +++ b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md @@ -56,7 +56,7 @@ This creates three critical gaps for common backup scenarios: ### Configuration Model -The namespace-scoped filters and fine-grained global filters are defined in the same ResourcePolicy ConfigMap that is already referenced by `BackupSpec.ResourcePolicy`. The YAML format is extended with two new top-level keys: +The namespace-scoped filters and fine-grained global filters are defined in the same ResourcePolicy ConfigMap that is already referenced by `BackupSpec.ResourcePolicy`. The YAML format is extended with two new top-level keys: `fineGrainedGlobalFilterPolicy` and `namespacedFilterPolicies` ```yaml version: v1 From 174d76c197a456bfcfa5c78310ba7817654355f4 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 18 May 2026 17:42:02 +0800 Subject: [PATCH 29/32] Update the Linux's base image from Ubuntu jammy to noble. Signed-off-by: Xun Jiang --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index dfd95683f..12306fad2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,9 +49,9 @@ RUN mkdir -p /output/usr/bin && \ go clean -modcache -cache # Velero image packing section -FROM paketobuildpacks/run-jammy-tiny:latest +FROM paketobuildpacks/ubuntu-noble-run-tiny:latest -LABEL maintainer="Xun Jiang " +LABEL maintainer="Xun Jiang " COPY --from=velero-builder /output / From bbbff59eedd51f4f4c40eb5778f685ae2deeaeb8 Mon Sep 17 00:00:00 2001 From: Adam Zhang Date: Tue, 19 May 2026 00:02:17 +0800 Subject: [PATCH 30/32] address review comments - rename FineGrainedGlobalFilterPolicy to ClusterScopedFilterPolicy - add warning message in several places to help debug - cleanup conflicting validations Signed-off-by: Adam Zhang --- .../fine-grained-backup-filters-design.md | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/design/backup-filter-enhancement/fine-grained-backup-filters-design.md b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md index a54663a1f..0bb52ffd5 100644 --- a/design/backup-filter-enhancement/fine-grained-backup-filters-design.md +++ b/design/backup-filter-enhancement/fine-grained-backup-filters-design.md @@ -5,7 +5,7 @@ **Backup Filter**: The mechanism in Velero that determines which Kubernetes resources are collected from the cluster and written into the backup archive. Backup filters currently operate on four dimensions: namespace, resource type, label, and cluster scope. **Global Filter**: A filter that applies uniformly across all namespaces in a backup. All existing Velero backup filters are global filters. **Namespace-Scoped Filter**: A filter that applies only within specific namespaces, overriding the global filter for those namespaces. This is the capability introduced by this design. -**FineGrainedGlobalFilterPolicy**: A global filter for cluster-scoped resources that allows per-kind label selectors and name patterns, functioning similarly to `NamespacedFilterPolicy` but applied to cluster-scoped resources globally. +**ClusterScopedFilterPolicy**: A global filter for cluster-scoped resources that allows per-kind label selectors and name patterns, functioning similarly to `NamespacedFilterPolicy` but applied to cluster-scoped resources globally. **Resource Filter**: A filter rule that pairs one or more resource kinds with their own label selector and/or name patterns. Multiple resource filters within a namespace-scoped policy allow different filtering criteria for different resource types. **Resource Name Filter**: A filter that matches individual resource instances by their metadata.name, using glob patterns. This is a new filter dimension introduced by this design. **Resource Policy**: An existing Velero mechanism where backup behavior rules are defined in a ConfigMap and referenced from `BackupSpec.ResourcePolicy`. Currently used for volume policies and global include/exclude policies. @@ -35,7 +35,7 @@ This creates three critical gaps for common backup scenarios: ## Goals - Extend the `ResourcePolicies` ConfigMap format with a `namespacedFilterPolicies` section that allows per-namespace, per-kind resource filtering with independent label selectors and name patterns for each resource type -- Extend the `ResourcePolicies` ConfigMap format with a `fineGrainedGlobalFilterPolicy` section that allows per-kind resource filtering with independent label selectors and name patterns for cluster-scoped resources globally +- Extend the `ResourcePolicies` ConfigMap format with a `clusterScopedFilterPolicy` section that allows per-kind resource filtering with independent label selectors and name patterns for cluster-scoped resources globally - Support resource name filtering by glob patterns using the same `gobwas/glob` library that Velero uses for namespace patterns, ensuring consistency across the codebase - Support per-kind label selectors, so that different resource types within the same namespace can be filtered with different labels - Maintain full backward compatibility — existing backups with no `namespacedFilterPolicies` behave exactly as they do today @@ -56,7 +56,7 @@ This creates three critical gaps for common backup scenarios: ### Configuration Model -The namespace-scoped filters and fine-grained global filters are defined in the same ResourcePolicy ConfigMap that is already referenced by `BackupSpec.ResourcePolicy`. The YAML format is extended with two new top-level keys: `fineGrainedGlobalFilterPolicy` and `namespacedFilterPolicies` +The namespace-scoped filters and fine-grained global filters are defined in the same ResourcePolicy ConfigMap that is already referenced by `BackupSpec.ResourcePolicy`. The YAML format is extended with two new top-level keys: `clusterScopedFilterPolicy` and `namespacedFilterPolicies` ```yaml version: v1 @@ -70,7 +70,7 @@ includeExcludePolicy: # existing global include/exclude policy (unchanged) includedNamespaceScopedResources: - configmaps -fineGrainedGlobalFilterPolicy: +clusterScopedFilterPolicy: # NEW: global overrides for cluster-scoped resources resourceFilters: - kinds: [ClusterRole, ClusterRoleBinding] @@ -96,7 +96,7 @@ namespacedFilterPolicies: app: my-service ``` -All four sections coexist in the same ConfigMap. They are independent — `volumePolicies` handles volume backup strategy, `includeExcludePolicy` handles global resource type filtering, `fineGrainedGlobalFilterPolicy` handles cluster-scoped resource filtering by kind/name/label, and `namespacedFilterPolicies` handles per-namespace, per-kind overrides. +All four sections coexist in the same ConfigMap. They are independent — `volumePolicies` handles volume backup strategy, `includeExcludePolicy` handles global resource type filtering, `clusterScopedFilterPolicy` handles cluster-scoped resource filtering by kind/name/label, and `namespacedFilterPolicies` handles per-namespace, per-kind overrides. ### The `resourceFilters` Model @@ -124,6 +124,7 @@ A `ResourceFilter` entry with an empty (or omitted) `kinds` field, or a field ex - At most **one** catch-all entry is allowed per `NamespacedFilterPolicy`. - `names` and `excludedNames` are **not** supported on catch-all entries. Name patterns are kind-specific by nature and cannot be applied across arbitrary kinds; use kind-specific entries for name-based filtering. - The catch-all applies to kinds that are **not listed in any other `resourceFilters` entry** in the same policy. Kind-specific entries take precedence over the catch-all. +- A catch-all entry **does not inherit or fall back to `BackupSpec.LabelSelector`**. If a catch-all entry has no `labelSelector`/`orLabelSelectors`, all unlisted resource kinds in the namespace are included with **no label filtering** — the global label selector is not applied. Define a catch-all with an explicit `labelSelector` if label-based filtering is desired for unlisted kinds. **Evaluation order within a namespace filter policy:** 1. For each resource kind encountered during backup, the system first checks whether a kind-specific `resourceFilters` entry exists for that kind. @@ -135,7 +136,7 @@ A `ResourceFilter` entry with an empty (or omitted) `kinds` field, or a field ex The namespace-scoped filter system and fine-grained global filter system layer on top of the existing global filter system. They intentionally behave differently: - **`namespacedFilterPolicies`** acts as an **exclusive allowlist (boundary)**. Only kinds explicitly listed (or matched by a catch-all) are backed up from that namespace. This gives namespace owners complete and isolated control over their namespace's backup contents, preventing unexpected data spillage from global fallbacks. -- **`fineGrainedGlobalFilterPolicy`** acts as a **refinement overlay (tweak)**. Unlisted cluster-scoped kinds fall back to the standard global filters. This allows administrators to selectively adjust filtering for a few specific cluster-scoped kinds without rewriting the entire global inclusion list. +- **`clusterScopedFilterPolicy`** acts as a **refinement overlay (tweak)**. Unlisted cluster-scoped kinds fall back to the standard global filters. This allows administrators to selectively adjust filtering for a few specific cluster-scoped kinds without rewriting the entire global inclusion list. **For Namespace-Scoped Resources:** @@ -156,14 +157,19 @@ The evaluation order is: 6. **Interaction with `includeExcludePolicy`**: `namespacedFilterPolicies` is a **refinement** of the global resource filter system, not a replacement. Global exclusions defined in `includeExcludePolicy` (e.g., `excludedNamespaceScopedResources: [secrets]`) are applied first at the resource-type level before per-namespace filter policies are consulted. A namespace-scoped filter policy cannot re-include a resource kind that has been globally excluded by `includeExcludePolicy`. For example, if `secrets` is listed under `excludedNamespaceScopedResources`, no `Secret` resources will be backed up from any namespace, even if a `namespacedFilterPolicies` entry explicitly lists `Secret` for that namespace. Users who need per-namespace secret selection must remove `secrets` from the global exclusion list. + To help users catch this misconfiguration early, Velero logs a warning at backup start when a `namespacedFilterPolicies` entry lists a kind that is globally excluded by `includeExcludePolicy`: + ``` + level=warn msg="namespacedFilterPolicies entry lists a kind that is globally excluded by includeExcludePolicy; the per-namespace filter entry has no effect" kind="secrets" namespacePattern="ns-a" + ``` + **For Cluster-Scoped Resources:** -1. If `fineGrainedGlobalFilterPolicy` is present, it acts as a **refinement overlay** over the existing global filters for cluster-scoped resources. It is NOT an exclusive allowlist. +1. If `clusterScopedFilterPolicy` is present, it acts as a **refinement overlay** over the existing global filters for cluster-scoped resources. It is NOT an exclusive allowlist. - To back up cluster-scoped resources in a namespace-filtered backup, you must still explicitly include them via `BackupSpec.IncludedClusterScopedResources`. - If a cluster-scoped kind is listed in its `resourceFilters`, its specific `labelSelector`/`orLabelSelectors` and `names`/`excludedNames` patterns are applied. - If a cluster-scoped kind is **not listed**, it falls back to the standard global filters (`BackupSpec.LabelSelector`, etc.) and is included in the backup. -2. If `fineGrainedGlobalFilterPolicy` is absent, Velero falls back to the existing global filters (`IncludedClusterScopedResources`, `IncludedResources`, `LabelSelector`, etc.) for cluster-scoped resources. +2. If `clusterScopedFilterPolicy` is absent, Velero falls back to the existing global filters (`IncludedClusterScopedResources`, `IncludedResources`, `LabelSelector`, etc.) for cluster-scoped resources. 3. **The `velero.io/exclude-from-backup=true` label** always takes precedence over all filters. @@ -179,7 +185,7 @@ flowchart TD H["Use global filters:
- BackupSpec IncludedResources
- BackupSpec LabelSelector
- includeExcludePolicy"] I{"Is resource
cluster-scoped?"} - J{"Is fineGrainedGlobalFilterPolicy
present?"} + J{"Is clusterScopedFilterPolicy
present?"} K{"Is kind in resourceFilters?"} L["Apply kind-specific filters:
- labelSelector / orLabelSelectors
- names / excludedNames"] @@ -206,7 +212,7 @@ The existing backup pipeline has two stages: item collection and item backup. Na **Stage 1 — Item Collection (`item_collector.go`).** Resources are listed from the Kubernetes API. - **Resource type check** in `getResourceItems()`: Before iterating namespaces, the global resource type check still applies. - - **For Cluster-Scoped Resources:** The global resource type check (`ShouldInclude`) determines if the kind is collected. `FineGrainedGlobalFilterPolicy` does not skip unlisted cluster-scoped kinds at this stage. + - **For Cluster-Scoped Resources:** The global resource type check (`ShouldInclude`) determines if the kind is collected. `ClusterScopedFilterPolicy` does not skip unlisted cluster-scoped kinds at this stage. - **For Namespace-Scoped Resources:** Within the namespace loop, a per-namespace resource type check is added. If a filter policy matches the current namespace, only resource kinds listed in `resourceFilters[].kinds` are included — if the current resource type is not listed, it is skipped for that namespace. - **Label selector** in `listResourceByLabelsPerNamespace()` and `listResourceByLabelsGlobally()`: The function looks up the filter policy (either the namespace-specific one or the fine-grained global one). If found, it retrieves the `ResourceFilter` entry for the current resource kind and uses that entry's `labelSelector`/`orLabelSelectors` for the Kubernetes API list call. If no filter policy is found, the global selectors are used as before. @@ -230,7 +236,7 @@ A follow-up design will add namespace-scoped filters to the restore pipeline. This section documents the system behavior in edge cases and error conditions: **Plugin AdditionalItems and Auto-Backed Up CRDs:** -Cluster-scoped resources injected dynamically (such as `VolumeSnapshotClass` from the CSI plugin or `CustomResourceDefinition` from Velero's auto-backup loop) do not require hardcoded exceptions. In `itemInclusionChecks()`, Velero natively allows unlisted cluster-scoped resources to pass through unless explicitly excluded by the user. `FineGrainedGlobalFilterPolicy` preserves this permissive behavior: if a dynamically injected cluster-scoped resource is NOT listed in the policy, it passes through untouched. If it IS listed, its specific `names` and `excludedNames` filters are strictly enforced. +Cluster-scoped resources injected dynamically (such as `VolumeSnapshotClass` from the CSI plugin or `CustomResourceDefinition` from Velero's auto-backup loop) do not require hardcoded exceptions. In `itemInclusionChecks()`, Velero natively allows unlisted cluster-scoped resources to pass through unless explicitly excluded by the user. `ClusterScopedFilterPolicy` preserves this permissive behavior: if a dynamically injected cluster-scoped resource is NOT listed in the policy, it passes through untouched. If it IS listed, its specific `names` and `excludedNames` filters are strictly enforced. **Plugin-injected namespace-scoped additional items** follow the same permissive principle. When a `BackupItemAction` returns additional items whose kind is not listed in the matched `namespacedFilterPolicies` entry and there is no catch-all entry, those items still pass through Stage 2 (`itemInclusionChecks`). This is intentional: blocking plugin-injected items at Stage 2 would break backup completeness — for example, a CSI plugin may inject a `VolumeSnapshotContent` that is required for a correct restore even when the user's filter policy only lists application resource types. @@ -321,7 +327,7 @@ namespacedFilterPolicies[0].resourceFilters[0]: invalid label selector: "invalid ``` **Out-of-Scope Kinds in Filter Entries:** -A user may accidentally list a cluster-scoped kind (e.g., `ClusterRole`) inside a `namespacedFilterPolicies` entry, or a namespace-scoped kind (e.g., `ConfigMap`) inside `fineGrainedGlobalFilterPolicy`. The system silently ignores such entries at the Kubernetes API level — namespace-scoped items are never listed globally, and cluster-scoped items are never listed per-namespace, so no matching resources will ever be found. A warning is logged at backup start to help the user detect the misconfiguration. No validation error is raised — the entry is harmless but ineffective. +A user may accidentally list a cluster-scoped kind (e.g., `ClusterRole`) inside a `namespacedFilterPolicies` entry, or a namespace-scoped kind (e.g., `ConfigMap`) inside `clusterScopedFilterPolicy`. The system silently ignores such entries at the Kubernetes API level — namespace-scoped items are never listed globally, and cluster-scoped items are never listed per-namespace, so no matching resources will ever be found. A warning is logged at backup start to help the user detect the misconfiguration. No validation error is raised — the entry is harmless but ineffective. **Discovery Helper Unavailable:** If the discovery helper is completely unavailable during backup initialization, the backup fails with: @@ -352,11 +358,10 @@ This is consistent with how other discovery-dependent features handle this error namespacedFilterPolicies[N]: at least one resourceFilter must be specified ``` -3. **Each resource filter without kinds can only be defined once, and cannot specify names/excludedNames. Additionally, it must specify either labelSelector or orLabelSelectors:** +3. **Each resource filter without kinds can only be defined once, and cannot specify names/excludedNames.** ``` namespacedFilterPolicies[N]: only one resource filter with empty kinds is allowed namespacedFilterPolicies[N].resourceFilters[M]: names or excludedNames cannot be specified when kinds is empty - namespacedFilterPolicies[N].resourceFilters[M]: labelSelector or orLabelSelectors must be specified when kinds is empty ``` 4. **No duplicate kinds across resource filter entries** within the same namespace filter: @@ -379,11 +384,11 @@ This is consistent with how other discovery-dependent features handle this error 9. **Resource kind validation with discovery helper** (performed during backup initialization). -**Validation functions for `fineGrainedGlobalFilterPolicy`:** +**Validation functions for `clusterScopedFilterPolicy`:** 1. **At least one resourceFilter must be specified.** ``` - fineGrainedGlobalFilterPolicy: at least one resourceFilter must be specified + clusterScopedFilterPolicy: at least one resourceFilter must be specified ``` 2. **No duplicate kinds across resource filters.** @@ -392,7 +397,7 @@ This is consistent with how other discovery-dependent features handle this error 4. **Resource name patterns must be valid globs.** -Additionally, in `backup_controller.go`, a validation check ensures that `namespacedFilterPolicies` and `fineGrainedGlobalFilterPolicy` are not used with old-style resource filters (`IncludedResources`/`ExcludedResources`/`IncludeClusterResources`), similar to the existing check for `includeExcludePolicy`. +Additionally, in `backup_controller.go`, a validation check ensures that `namespacedFilterPolicies` and `clusterScopedFilterPolicy` are not used with old-style resource filters (`IncludedResources`/`ExcludedResources`/`IncludeClusterResources`), similar to the existing check for `includeExcludePolicy`. ## ConfigMap Examples From 30381a60e46ab63e5d61adda3e359307e7f4dc8c Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 19 May 2026 19:42:19 +0800 Subject: [PATCH 31/32] Fix the site deployment problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The netlify error analysis: The problem is that base = "site/" makes the build run from the site subdirectory, and publish = "site/public" is resolved relative to the repo root as site/site/public — but Hugo outputs to site/public. Since the base is already site/, the publish path should just be public (relative to the base directory). Signed-off-by: Daniel Jiang --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index ed2de9787..d270f9aba 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ [build] base = "site/" command = "hugo --gc --minify" - publish = "site/public" + publish = "public" [context.production.environment] HUGO_VERSION = "0.73.0" From 1d03217661ed17d67801d8645b78633e99743d99 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 21 May 2026 09:20:24 +0800 Subject: [PATCH 32/32] add CBT bitmap implementation Signed-off-by: Lyndon-Li --- pkg/uploader/cbt/types/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/uploader/cbt/types/types.go b/pkg/uploader/cbt/types/types.go index f512d7a8a..9b13669b2 100644 --- a/pkg/uploader/cbt/types/types.go +++ b/pkg/uploader/cbt/types/types.go @@ -51,7 +51,7 @@ type Iterator interface { // BlockSize returns the granularity of the bitmap BlockSize() uint - // Count returns the toal number of count in the bitmap + // Count returns the total number of count in the bitmap Count() uint64 // Next returns the offset of the next set block and whether it comes to the end of the iteration