diff --git a/go.mod b/go.mod index 38f11fa24..1593197b1 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/ramendr/ramen -go 1.22.5 +go 1.23.0 -toolchain go1.22.7 +toolchain go1.23.4 // This replace should always be here for ease of development. replace github.com/ramendr/ramen/api => ./api @@ -25,18 +25,18 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 golang.org/x/time v0.8.0 - k8s.io/api v0.31.1 + k8s.io/api v0.32.0 k8s.io/apiextensions-apiserver v0.31.1 - k8s.io/apimachinery v0.31.1 + k8s.io/apimachinery v0.32.0 k8s.io/client-go v12.0.0+incompatible k8s.io/component-base v0.31.1 - k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 - k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 open-cluster-management.io/api v0.15.0 open-cluster-management.io/config-policy-controller v0.15.0 open-cluster-management.io/governance-policy-propagator v0.15.0 open-cluster-management.io/multicloud-operators-subscription v0.15.0 - sigs.k8s.io/controller-runtime v0.19.0 + sigs.k8s.io/controller-runtime v0.19.3 sigs.k8s.io/yaml v1.4.0 ) @@ -56,14 +56,12 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect 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 @@ -99,15 +97,17 @@ require ( golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.28.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.36.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect ) // replace directives to accommodate for stolostron -replace k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.31.0 +replace k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.32.0 + +replace github.com/csi-addons/kubernetes-csi-addons v0.10.1-0.20240924092040-c11db0b867a1 => github.com/Nikhil-Ladha/kubernetes-csi-addons v0.0.0-20250106150113-0626866673b5 diff --git a/go.sum b/go.sum index 17f81ffbd..8e473d6c1 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Nikhil-Ladha/kubernetes-csi-addons v0.0.0-20250106150113-0626866673b5 h1:4ifHh7mH1FSP/xb0GsQvx78iJ5PKrda4KjPJ35vrcnU= +github.com/Nikhil-Ladha/kubernetes-csi-addons v0.0.0-20250106150113-0626866673b5/go.mod h1:iS3XN+E9Bf9qlTra9Oaxn76QQ03131Syli9Vyp07+PI= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/backube/volsync v0.11.0 h1:czEwQEWGAEfW4bf4XU9KaW3WN/pOH837+Sm7AoR0c48= @@ -8,8 +10,6 @@ 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/csi-addons/kubernetes-csi-addons v0.10.1-0.20240924092040-c11db0b867a1 h1:9mh79gS8O8uO5okZ2DhFO0LSrhpVXd9R9DLvbnh2He4= -github.com/csi-addons/kubernetes-csi-addons v0.10.1-0.20240924092040-c11db0b867a1/go.mod h1:LeY7UYm8nEBCG1RcJG0DHmJbva0ILmtp+kcegxRuHhc= 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= @@ -40,8 +40,6 @@ 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= @@ -58,8 +56,6 @@ 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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 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= @@ -149,8 +145,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/vmware-tanzu/velero v1.9.1 h1:uZhNMq1Pn8AZjT7HLtKseTq47EeHeIuUxvGPFFp/+Vs= @@ -209,8 +206,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -226,24 +223,24 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24= k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= -k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= open-cluster-management.io/api v0.15.0 h1:lRee1KOlGHZb2scTA7ff9E9Fxt2hJc7jpkHnaCbvkOU= open-cluster-management.io/api v0.15.0/go.mod h1:9erZEWEn4bEqh0nIX2wA7f/s3KCuFycQdBrPrRzi0QM= open-cluster-management.io/config-policy-controller v0.15.0 h1:tDuIVT2L/luKLvLIz7/wkbskTZqWypLkwjyVMxPqhsI= @@ -252,11 +249,11 @@ open-cluster-management.io/governance-policy-propagator v0.15.0 h1:tSDJcq8p/UQHB open-cluster-management.io/governance-policy-propagator v0.15.0/go.mod h1:I1LbX78mavWMv6W3YAeSjCq2YBfSS0RpOBWOskpbLng= open-cluster-management.io/multicloud-operators-subscription v0.15.0 h1:/FPaCfTn8PaDQCYMAhDw7xdH4TsaQlV6Ufi9zyWwyYw= open-cluster-management.io/multicloud-operators-subscription v0.15.0/go.mod h1:lDMnGyFWoyWFjrAJRrnnWz5Gz2IUsqRsvPV44ll7zXc= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= +sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= +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/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/controller/s3utils.go b/internal/controller/s3utils.go index c63e4295a..dc217e1b6 100644 --- a/internal/controller/s3utils.go +++ b/internal/controller/s3utils.go @@ -20,6 +20,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" + volrep "github.com/csi-addons/kubernetes-csi-addons/api/replication.storage/v1alpha1" "github.com/go-logr/logr" ramen "github.com/ramendr/ramen/api/v1alpha1" corev1 "k8s.io/api/core/v1" @@ -344,6 +345,26 @@ func typedKey(prefix, suffix string, typ reflect.Type) string { return prefix + typ.String() + "/" + suffix } +// UploadVGRC uploads the given VGRC to the bucket with a key of +// "". +// - vgrcKeyPrefix should have any required delimiters like '/' +// - OK to call UploadVGRC() concurrently from multiple goroutines safely. +func UploadVGRC(s ObjectStorer, vgrcKeyPrefix, vgrcKeySuffix string, + vgrc volrep.VolumeGroupReplicationContent, +) error { + return uploadTypedObject(s, vgrcKeyPrefix, vgrcKeySuffix, vgrc) +} + +// UploadVGR uploads the given VGR to the bucket with a key of +// "". +// - vgrKeyPrefix should have any required delimiters like '/' +// - OK to call UploadVGR() concurrently from multiple goroutines safely. +func UploadVGR(s ObjectStorer, vgrKeyPrefix, vgrKeySuffix string, + vgr volrep.VolumeGroupReplication, +) error { + return uploadTypedObject(s, vgrKeyPrefix, vgrKeySuffix, vgr) +} + // UploadPV uploads the given PV to the bucket with a key of // "". // - pvKeyPrefix should have any required delimiters like '/' @@ -437,6 +458,28 @@ func (s *s3ObjectStore) UploadObject(key string, return nil } +// downloadVGRCs downloads all VGRCs in the bucket. +// - Downloads VGRCs with the given key prefix. +// - If bucket doesn't exists, will return ErrCodeNoSuchBucket "NoSuchBucket" +func downloadVGRCs(s ObjectStorer, vgrcKeyPrefix string) ( + vgrcList []volrep.VolumeGroupReplicationContent, err error, +) { + err = DownloadTypedObjects(s, vgrcKeyPrefix, &vgrcList) + + return +} + +// downloadVGRs downloads all PVCs in the bucket. +// - Downloads VGRs with the given key prefix. +// - If bucket doesn't exists, will return ErrCodeNoSuchBucket "NoSuchBucket" +func downloadVGRs(s ObjectStorer, vgrKeyPrefix string) ( + vgrList []volrep.VolumeGroupReplication, err error, +) { + err = DownloadTypedObjects(s, vgrKeyPrefix, &vgrList) + + return +} + // downloadPVs downloads all PVs in the bucket. // - Downloads PVs with the given key prefix. // - If bucket doesn't exists, will return ErrCodeNoSuchBucket "NoSuchBucket" diff --git a/internal/controller/volumereplicationgroup_controller.go b/internal/controller/volumereplicationgroup_controller.go index 3331b4b55..198cf2444 100644 --- a/internal/controller/volumereplicationgroup_controller.go +++ b/internal/controller/volumereplicationgroup_controller.go @@ -656,6 +656,17 @@ func (v *VRGInstance) clusterDataRestore(result *ctrl.Result) (int, error) { msg = "Nothing to restore" } + if util.IsCGEnabled(v.instance.GetAnnotations()) { + v.log.Info("Restoring VGRs and VGRCs") + + err := v.restoreVGRCsAndVGRsForVolRep(result) + if err != nil { + v.log.Info("VolRep VGR/VGRC restore failed") + + return numRestoredForVS + numRestoredForVR, fmt.Errorf("failed to restore VGR/VGRC for VolRep (%w)", err) + } + } + setVRGClusterDataReadyCondition(&v.instance.Status.Conditions, v.instance.Generation, msg) return numRestoredForVS + numRestoredForVR, nil diff --git a/internal/controller/vrg_volrep.go b/internal/controller/vrg_volrep.go index 994f50b3b..805b8481c 100644 --- a/internal/controller/vrg_volrep.go +++ b/internal/controller/vrg_volrep.go @@ -60,7 +60,7 @@ func logWithPvcName(log logr.Logger, pvc *corev1.PersistentVolumeClaim) logr.Log // reconcileVolRepsAsPrimary creates/updates VolumeReplication CR for each pvc // from pvcList. If it fails (even for one pvc), then requeue is set to true. // -//nolint:funlen,gocognit +//nolint:funlen,gocognit,cyclop func (v *VRGInstance) reconcileVolRepsAsPrimary() { readyForVRProtectionPVCs := make([]corev1.PersistentVolumeClaim, 0) @@ -126,6 +126,17 @@ func (v *VRGInstance) reconcileVolRepsAsPrimary() { continue } + if rmnutil.IsCGEnabled(v.instance.GetAnnotations()) { + if err := v.uploadVGRandVGRCtoS3Stores(pvc, log); err != nil { + log.Info("Requeuing due to failure to upload VGR and VGRC objects to S3 store(s)", + "errorValue", err) + + v.requeue() + + continue + } + } + log.Info("Successfully processed VolumeReplication for PersistentVolumeClaim") } } @@ -633,6 +644,41 @@ func (v *VRGInstance) isArchivedAlready(pvc *corev1.PersistentVolumeClaim, log l return true } +func (v *VRGInstance) isVGRandVGRCArchivedAlready(pvc *corev1.PersistentVolumeClaim, log logr.Logger) bool { + vgrHasAnnotation := false + vgrcHasAnnotation := false + + vgr, err := v.getVGRFromPVC(pvc) + if err != nil { + log.Error(err, "Failed to get VGR to check if archived") + + return false + } + + vgrDesiredValue := v.generateArchiveAnnotation(vgr.Generation) + if v, ok := vgr.ObjectMeta.Annotations[pvcVRAnnotationArchivedKey]; ok && (v == vgrDesiredValue) { + vgrHasAnnotation = true + } + + vgrc, err := v.getVGRCFromVRG(&vgr) + if err != nil { + log.Error(err, "Failed to get VGRC to check if archived") + + return false + } + + vgrcDesiredValue := v.generateArchiveAnnotation(vgrc.Generation) + if v, ok := vgrc.ObjectMeta.Annotations[pvcVRAnnotationArchivedKey]; ok && (v == vgrcDesiredValue) { + vgrcHasAnnotation = true + } + + if !vgrHasAnnotation || !vgrcHasAnnotation { + return false + } + + return true +} + // Upload PV to the list of S3 stores in the VRG spec func (v *VRGInstance) uploadPVandPVCtoS3Stores(pvc *corev1.PersistentVolumeClaim, log logr.Logger) (err error) { if v.isArchivedAlready(pvc, log) { @@ -692,6 +738,107 @@ func (v *VRGInstance) uploadPVandPVCtoS3Stores(pvc *corev1.PersistentVolumeClaim return nil } +// Upload VGRCs and VGRs to the list of S3 stores in the VRG spec +func (v *VRGInstance) uploadVGRandVGRCtoS3Stores(pvc *corev1.PersistentVolumeClaim, log logr.Logger) (err error) { + if v.isVGRandVGRCArchivedAlready(pvc, log) { + msg := fmt.Sprintf("VGR and VGRC cluster data already protected for PVC %s", pvc.Name) + v.log.Info(msg) + + return nil + } + + // Error out if VRG has no S3 profiles + numProfilesToUpload := len(v.instance.Spec.S3Profiles) + if numProfilesToUpload == 0 { + msg := "Error uploading VGR and VGRC cluster data because VRG spec has no S3 profiles" + v.log.Info(msg) + + return fmt.Errorf("error uploading cluster data of VGR and VGRC %s because VRG spec has no S3 profiles", + pvc.Name) + } + + s3Profiles, err := v.UploadVGRandVGRCtoS3Stores(pvc, log) + if err != nil { + return fmt.Errorf("failed to upload VGR/VGRC with error (%w). Uploaded to %v S3 profile(s)", err, s3Profiles) + } + + numProfilesUploaded := len(s3Profiles) + + if numProfilesUploaded != numProfilesToUpload { + // Merely defensive as we don't expect to reach here + msg := fmt.Sprintf("uploaded VGR/VGRC cluster data to only %d of %d S3 profile(s): %v", + numProfilesUploaded, numProfilesToUpload, s3Profiles) + v.log.Info(msg) + + return errors.New(msg) + } + + if err := v.addArchivedAnnotationForVGRandVGRC(pvc, log); err != nil { + return err + } + + msg := fmt.Sprintf("Done uploading VGR/VGRC cluster data to %d of %d S3 profile(s): %v", + numProfilesUploaded, numProfilesToUpload, s3Profiles) + v.log.Info(msg) + + return nil +} + +func (v *VRGInstance) UploadVGRandVGRCtoS3Store(s3ProfileName string, pvc *corev1.PersistentVolumeClaim) error { + if s3ProfileName == "" { + return fmt.Errorf("missing S3 profiles, failed to protect cluster data for PVC %s", pvc.Name) + } + + objectStore, err := v.getObjectStorer(s3ProfileName) + if err != nil { + return fmt.Errorf("error getting object store, failed to protect cluster data for PVC %s, %w", pvc.Name, err) + } + + vgr, err := v.getVGRFromPVC(pvc) + if err != nil { + return fmt.Errorf("error getting VGR for PVC, failed to protect cluster data for VGR %s to s3Profile %s, %w", + vgr.Name, s3ProfileName, err) + } + + vgrc, err := v.getVGRCFromVRG(&vgr) + if err != nil { + return fmt.Errorf("error getting VGRC for VGR, failed to protect cluster data for VGRC %s to s3Profile %s, %w", + vgrc.Name, s3ProfileName, err) + } + + return v.UploadVGRAndVGRCtoS3(s3ProfileName, objectStore, &vgr, &vgrc) +} + +func (v *VRGInstance) UploadVGRAndVGRCtoS3(s3ProfileName string, objectStore ObjectStorer, + vgr *volrep.VolumeGroupReplication, vgrc *volrep.VolumeGroupReplicationContent, +) error { + if err := UploadVGRC(objectStore, v.s3KeyPrefix(), vgrc.Name, *vgrc); err != nil { + var aerr awserr.Error + if errors.As(err, &aerr) { + // Treat any aws error as a persistent error + v.cacheObjectStorer(s3ProfileName, nil, + fmt.Errorf("persistent error while uploading to s3 profile %s, will retry later", s3ProfileName)) + } + + err := fmt.Errorf("error uploading VGRC to s3Profile %s, failed to protect cluster data for VGRC %s, %w", + s3ProfileName, vgrc.Name, err) + + return err + } + + vgrNamespacedName := types.NamespacedName{Namespace: vgr.Namespace, Name: vgr.Name} + vgrNamespacedNameString := vgrNamespacedName.String() + + if err := UploadVGR(objectStore, v.s3KeyPrefix(), vgrNamespacedNameString, *vgr); err != nil { + err := fmt.Errorf("error uploading VGR to s3Profile %s, failed to protect cluster data for VGR %s, %w", + s3ProfileName, vgrNamespacedNameString, err) + + return err + } + + return nil +} + func (v *VRGInstance) UploadPVandPVCtoS3Store(s3ProfileName string, pvc *corev1.PersistentVolumeClaim) error { if s3ProfileName == "" { return fmt.Errorf("missing S3 profiles, failed to protect cluster data for PVC %s", pvc.Name) @@ -762,6 +909,64 @@ func (v *VRGInstance) UploadPVandPVCtoS3Stores(pvc *corev1.PersistentVolumeClaim return succeededProfiles, nil } +func (v *VRGInstance) UploadVGRandVGRCtoS3Stores(pvc *corev1.PersistentVolumeClaim, + log logr.Logger, +) ([]string, error) { + succeededProfiles := []string{} + // Upload the VGR and VGRC to all the S3 profiles in the VRG spec + for _, s3ProfileName := range v.instance.Spec.S3Profiles { + err := v.UploadVGRandVGRCtoS3Store(s3ProfileName, pvc) + if err != nil { + rmnutil.ReportIfNotPresent(v.reconciler.eventRecorder, v.instance, corev1.EventTypeWarning, + rmnutil.EventReasonUploadFailed, err.Error()) + + return succeededProfiles, err + } + + succeededProfiles = append(succeededProfiles, s3ProfileName) + } + + return succeededProfiles, nil +} + +func (v *VRGInstance) getVGRCFromVRG(vgr *volrep.VolumeGroupReplication) (volrep.VolumeGroupReplicationContent, error) { + vgrc := volrep.VolumeGroupReplicationContent{} + + vgrcName := vgr.Spec.VolumeGroupReplicationContentName + vgrcObjectKey := client.ObjectKey{ + Name: vgrcName, + } + + if err := v.reconciler.Get(v.ctx, vgrcObjectKey, &vgrc); err != nil { + return vgrc, fmt.Errorf("failed to get VGRC %v from VGR %v, %w", + vgrcObjectKey, client.ObjectKeyFromObject(vgr), err) + } + + return vgrc, nil +} + +func (v *VRGInstance) getVGRFromPVC(pvc *corev1.PersistentVolumeClaim) (volrep.VolumeGroupReplication, error) { + vgr := volrep.VolumeGroupReplication{} + + cg, ok := pvc.GetLabels()[ConsistencyGroupLabel] + if !ok { + return vgr, fmt.Errorf("failed to get VGR from PVC %v", client.ObjectKeyFromObject(pvc)) + } + + vgrName := rmnutil.TrimToK8sResourceNameLength(cg + v.instance.Name) + vgrObjectKey := client.ObjectKey{ + Name: vgrName, + Namespace: pvc.Namespace, + } + + if err := v.reconciler.Get(v.ctx, vgrObjectKey, &vgr); err != nil { + return vgr, fmt.Errorf("failed to get VGR %v from PVC %v, %w", + vgrObjectKey, client.ObjectKeyFromObject(pvc), err) + } + + return vgr, nil +} + func (v *VRGInstance) getPVFromPVC(pvc *corev1.PersistentVolumeClaim) (corev1.PersistentVolume, error) { pv := corev1.PersistentVolume{} volumeName := pvc.Spec.VolumeName @@ -2204,6 +2409,133 @@ func (v *VRGInstance) addArchivedAnnotationForPVC(pvc *corev1.PersistentVolumeCl return nil } +func (v *VRGInstance) addArchivedAnnotationForVGRandVGRC(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error { + vgr, err := v.getVGRFromPVC(pvc) + if err != nil { + log.Error(err, "Failed to get VGR to add archived annotation") + + return fmt.Errorf("failed to update VGR (%s) annotation (%s) belonging to"+ + "VolumeReplicationGroup (%s/%s), %w", + vgr.Name, pvcVRAnnotationArchivedKey, v.instance.Namespace, v.instance.Name, err) + } + + if vgr.ObjectMeta.Annotations == nil { + vgr.ObjectMeta.Annotations = map[string]string{} + } + + vgr.ObjectMeta.Annotations[pvcVRAnnotationArchivedKey] = v.generateArchiveAnnotation(vgr.Generation) + + if err := v.reconciler.Update(v.ctx, &vgr); err != nil { + log.Error(err, "Failed to update VGR annotation") + + return fmt.Errorf("failed to update VGR (%s/%s) annotation (%s) belonging to"+ + "VolumeReplicationGroup (%s/%s), %w", + vgr.Namespace, vgr.Name, pvcVRAnnotationArchivedKey, v.instance.Namespace, v.instance.Name, err) + } + + vgrc, err := v.getVGRCFromVRG(&vgr) + if err != nil { + log.Error(err, "Failed to get VGRC to add archived annotation") + + return fmt.Errorf("failed to update VGRC (%s) annotation (%s) belonging to"+ + "VolumeReplicationGroup (%s/%s), %w", + vgrc.Name, pvcVRAnnotationArchivedKey, v.instance.Namespace, v.instance.Name, err) + } + + if vgrc.ObjectMeta.Annotations == nil { + vgrc.ObjectMeta.Annotations = map[string]string{} + } + + vgrc.ObjectMeta.Annotations[pvcVRAnnotationArchivedKey] = v.generateArchiveAnnotation(vgrc.Generation) + if err := v.reconciler.Update(v.ctx, &vgrc); err != nil { + log.Error(err, "Failed to update VGRC annotation") + + return fmt.Errorf("failed to update VGRC (%s) annotation (%s) belonging to"+ + "VolumeReplicationGroup (%s/%s), %w", + vgrc.Name, pvcVRAnnotationArchivedKey, v.instance.Namespace, v.instance.Name, err) + } + + return nil +} + +func (v *VRGInstance) restoreVGRCsAndVGRsForVolRep(result *ctrl.Result) error { + v.log.Info("Restoring VolRep VGRCs and VGRs") + + if len(v.instance.Spec.S3Profiles) == 0 { + v.log.Info("No S3 profiles configured") + + result.Requeue = true + + return fmt.Errorf("no S3Profiles configured") + } + + v.log.Info(fmt.Sprintf("Restoring PVs and PVCs to this managed cluster. ProfileList: %v", v.instance.Spec.S3Profiles)) + + err := v.restoreVGRCsAndVGRsFromS3(result) + if err != nil { + errMsg := fmt.Sprintf("failed to restore PVs and PVCs using profile list (%v)", v.instance.Spec.S3Profiles) + v.log.Info(errMsg) + + return fmt.Errorf("%s: %w", errMsg, err) + } + + return nil +} + +func (v *VRGInstance) restoreVGRCsAndVGRsFromS3(result *ctrl.Result) error { + err := errors.New("s3Profiles empty") + NoS3 := false + + for _, s3ProfileName := range v.instance.Spec.S3Profiles { + if s3ProfileName == NoS3StoreAvailable { + v.log.Info("NoS3 available to fetch") + + NoS3 = true + + continue + } + + var objectStore ObjectStorer + + objectStore, _, err = v.reconciler.ObjStoreGetter.ObjectStore( + v.ctx, v.reconciler.APIReader, s3ProfileName, v.namespacedName, v.log) + if err != nil { + v.log.Error(err, "Kube objects recovery object store inaccessible", "profile", s3ProfileName) + + continue + } + + var vgrcCount, vgrCount int + + // Restore all VGRCs found in the s3 store. If any failure, the next profile will be retried + vgrcCount, err = v.restoreVGRCsFromObjectStore(objectStore, s3ProfileName) + if err != nil { + continue + } + + vgrCount, err = v.restoreVGRsFromObjectStore(objectStore, s3ProfileName) + + if err != nil || vgrcCount != vgrCount { + v.log.Info(fmt.Sprintf("Warning: Mismatch in VGRC/VGR count %d/%d (%v)", + vgrcCount, vgrCount, err)) + + continue + } + + v.log.Info(fmt.Sprintf("Restored %d VGRCs and %d VGRs using profile %s", vgrcCount, vgrCount, s3ProfileName)) + + return v.kubeObjectsRecover(result, s3ProfileName) + } + + if NoS3 { + return nil + } + + result.Requeue = true + + return err +} + // s3KeyPrefix returns the S3 key prefix of cluster data of this VRG. func (v *VRGInstance) s3KeyPrefix() string { return S3KeyPrefix(v.namespacedName) @@ -2370,6 +2702,64 @@ func (v *VRGInstance) checkPVClusterData(pvList []corev1.PersistentVolume) error return nil } +func (v *VRGInstance) restoreVGRCsFromObjectStore(objectStore ObjectStorer, s3ProfileName string) (int, error) { + vgrcList, err := downloadVGRCs(objectStore, v.s3KeyPrefix()) + if err != nil { + v.log.Error(err, fmt.Sprintf("error fetching VGRC cluster data from S3 profile %s", s3ProfileName)) + + return 0, err + } + + v.log.Info(fmt.Sprintf("Found %d PVs in s3 store using profile %s", len(vgrcList), s3ProfileName)) + + if err = v.checkVGRCClusterData(vgrcList); err != nil { + errMsg := fmt.Sprintf("Error found in VGRC cluster data in S3 store %s", s3ProfileName) + v.log.Info(errMsg) + v.log.Error(err, fmt.Sprintf("Resolve VGRC conflict in the S3 store %s to deploy the application", s3ProfileName)) + + return 0, fmt.Errorf("%s: %w", errMsg, err) + } + + return restoreClusterDataObjects(v, vgrcList, "VGRC", v.cleanupVGRCForRestore, v.validateExistingVGRC) +} + +func (v *VRGInstance) restoreVGRsFromObjectStore(objectStore ObjectStorer, s3ProfileName string) (int, error) { + vgrList, err := downloadVGRs(objectStore, v.s3KeyPrefix()) + if err != nil { + v.log.Error(err, fmt.Sprintf("error fetching VGR cluster data from S3 profile %s", s3ProfileName)) + + return 0, err + } + + v.log.Info(fmt.Sprintf("Found %d PVCs in s3 store using profile %s", len(vgrList), s3ProfileName)) + + return restoreClusterDataObjects(v, vgrList, "VGR", cleanupVGRForRestore, v.validateExistingVGR) +} + +func (v *VRGInstance) checkVGRCClusterData(vgrcList []volrep.VolumeGroupReplicationContent) error { + vgrcMap := map[string]volrep.VolumeGroupReplicationContent{} + // Scan the VGRCs and create a map of VGRCs that have conflicting volumeGroupReplicationRef + for _, thisVGRC := range vgrcList { + vgrRef := thisVGRC.Spec.VolumeGroupReplicationRef + vgrKey := fmt.Sprintf("%s/%s", vgrRef.Namespace, vgrRef.Name) + + prevVGRC, found := vgrcMap[vgrKey] + if !found { + vgrcMap[vgrKey] = thisVGRC + + continue + } + + msg := fmt.Sprintf("when restoring VGRC cluster data, detected conflicting vgrKey %s in VGRCs %s and %s", + vgrKey, prevVGRC.Name, thisVGRC.Name) + v.log.Info(msg) + + return errors.New(msg) + } + + return nil +} + func restoreClusterDataObjects[ ObjectType any, ClientObject interface { @@ -2537,6 +2927,65 @@ func (v *VRGInstance) validateExistingPVC(pvc *corev1.PersistentVolumeClaim) err return nil } +func (v *VRGInstance) validateExistingVGRC(vgrc *volrep.VolumeGroupReplicationContent) error { + log := v.log.WithValues("VGRC", vgrc.Name) + + existingVGRC := &volrep.VolumeGroupReplicationContent{} + if err := v.reconciler.Get(v.ctx, types.NamespacedName{Name: vgrc.Name}, existingVGRC); err != nil { + return fmt.Errorf("failed to get existing VGRC %s (%w)", vgrc.Name, err) + } + + if rmnutil.ResourceIsDeleted(existingVGRC) { + return fmt.Errorf("existing VGRC %s is being deleted", existingVGRC.Name) + } + + var vgr volrep.VolumeGroupReplication + + vgrNamespacedName := types.NamespacedName{ + Name: vgrc.Spec.VolumeGroupReplicationRef.Name, + Namespace: vgrc.Spec.VolumeGroupReplicationRef.Namespace, + } + + if err := v.reconciler.Get(v.ctx, vgrNamespacedName, &vgr); err != nil { + return fmt.Errorf("found VGRC %s referring to VGR %s, but failed to find VGR: %w", + existingVGRC.Name, vgrNamespacedName.String(), err) + } + + if rmnutil.ResourceIsDeleted(&vgr) { + return fmt.Errorf("existing VGRC %s refers to VGR %s with deletion timestamp non-zero %v", existingVGRC.Name, + vgrNamespacedName.String(), vgr.DeletionTimestamp) + } + + log.Info(fmt.Sprintf("VGRC %s exists and refers to VGR %s", vgrc.Name, vgrNamespacedName.String())) + + return nil +} + +func (v *VRGInstance) validateExistingVGR(vgr *volrep.VolumeGroupReplication) error { + existingVGR := &volrep.VolumeGroupReplication{} + vgrNSName := types.NamespacedName{Name: vgr.Name, Namespace: vgr.Namespace} + + err := v.reconciler.Get(v.ctx, vgrNSName, existingVGR) + if err != nil { + return fmt.Errorf("failed to get existing VGR %s (%w)", vgrNSName.String(), err) + } + + if rmnutil.ResourceIsDeleted(existingVGR) { + return fmt.Errorf("existing VGR %s is being deleted", vgrNSName.String()) + } + + if existingVGR.Spec.VolumeGroupReplicationContentName != vgr.Spec.VolumeGroupReplicationContentName { + return fmt.Errorf("VGR %s exists and refers to a different VGRC %s than VGRC %s desired", + vgrNSName.String(), existingVGR.Spec.VolumeGroupReplicationContentName, + vgr.Spec.VolumeGroupReplicationContentName) + } + + v.log.Info(fmt.Sprintf("VGR %s exists and refers to desired VGRC %s", vgrNSName.String(), + existingVGR.Spec.VolumeGroupReplicationContentName)) + + return nil +} + // pvMatches checks if the PVs fields match presuming x is bound to a PVC. Used to detect PVCs that were not // deleted, and hence PVC and PV is retained and available for use // @@ -2677,6 +3126,26 @@ func cleanupPVCForRestore(pvc *corev1.PersistentVolumeClaim) error { return nil } +func (v *VRGInstance) cleanupVGRCForRestore(vgrc *volrep.VolumeGroupReplicationContent) error { + vgrc.ResourceVersion = "" + if vgrc.Spec.VolumeGroupReplicationRef != nil { + vgrc.Spec.VolumeGroupReplicationRef.UID = "" + vgrc.Spec.VolumeGroupReplicationRef.ResourceVersion = "" + vgrc.Spec.VolumeGroupReplicationRef.APIVersion = "" + } + + return nil +} + +func cleanupVGRForRestore(vgr *volrep.VolumeGroupReplication) error { + vgr.ObjectMeta.Annotations = PruneAnnotations(vgr.GetAnnotations()) + vgr.ObjectMeta.Finalizers = []string{} + vgr.ObjectMeta.ResourceVersion = "" + vgr.ObjectMeta.OwnerReferences = nil + + return nil +} + // Follow this logic to update VRG (and also ProtectedPVC) conditions for VolRep // while reconciling VolumeReplicationGroup resource. // diff --git a/internal/controller/vrg_volrep_test.go b/internal/controller/vrg_volrep_test.go index c62df5341..26a6e4339 100644 --- a/internal/controller/vrg_volrep_test.go +++ b/internal/controller/vrg_volrep_test.go @@ -906,7 +906,7 @@ var _ = Describe("VolumeReplicationGroupVolRepController", func() { return apiReader.Get(context.TODO(), vrgVGRDeleteEnsureTestCase.vrgNamespacedName(), vrg) }, vrgtimeout*2, vrginterval). - Should(MatchError(errors.NewNotFound(schema.GroupResource{ + Should(MatchError(k8serrors.NewNotFound(schema.GroupResource{ Group: ramendrv1alpha1.GroupVersion.Group, Resource: "volumereplicationgroups", }, vrgVGRDeleteEnsureTestCase.vrgName)), @@ -2210,7 +2210,7 @@ func (v *vrgTest) createVGRC(testTemplate *template) { err := k8sClient.Create(context.TODO(), vrc) if err != nil { - if errors.IsAlreadyExists(err) { + if k8serrors.IsAlreadyExists(err) { err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: testTemplate.replicationClassName}, vrc) } } @@ -2765,7 +2765,7 @@ func (v *vrgTest) cleanupVGRC() { err := k8sClient.Get(context.TODO(), key, vgrc) if err != nil { - if errors.IsNotFound(err) { + if k8serrors.IsNotFound(err) { return } }