diff --git a/commands/settings/rbac/can.go b/commands/settings/rbac/can.go index 8fbc18abd..246fe1be9 100644 --- a/commands/settings/rbac/can.go +++ b/commands/settings/rbac/can.go @@ -88,7 +88,7 @@ func settingsRBACCanPreRunE(cmd *cobra.Command, args []string) error { //nolint: } func settingsRBACCanRun(cmd *cobra.Command, args []string) { - var k *kubernetes.Kubernetes + var k kubernetes.KubernetesConnector if rbacCanPolicyFilePath == "" { // check over policy in Everest deployment (ConfigMap). var l *zap.SugaredLogger diff --git a/commands/settings/rbac/validate.go b/commands/settings/rbac/validate.go index ed2f4399d..90ac256a8 100644 --- a/commands/settings/rbac/validate.go +++ b/commands/settings/rbac/validate.go @@ -57,7 +57,7 @@ func settingsRBACValidatePreRun(cmd *cobra.Command, _ []string) { //nolint:reviv } func settingsRBACValidateRun(cmd *cobra.Command, _ []string) { - var k *kubernetes.Kubernetes + var k kubernetes.KubernetesConnector if rbacValidatePolicyFilePath == "" { // check over policy in Everest deployment (ConfigMap). var l *zap.SugaredLogger diff --git a/commands/version.go b/commands/version.go index e4a23b9f0..0c47a35f0 100644 --- a/commands/version.go +++ b/commands/version.go @@ -56,7 +56,7 @@ func versionRunE(cmd *cobra.Command, _ []string) error { //nolint:revive cmdLogger := logger.GetLogger().With("component", "version") if !clientOnlyFlag { - k, err := utils.NewKubeclient(cmdLogger, rootCmdFlags.KubeconfigPath) + k, err := utils.NewKubeConnector(cmdLogger, rootCmdFlags.KubeconfigPath) if err != nil { return err } diff --git a/go.mod b/go.mod index 4000fc69f..5f8c1edec 100644 --- a/go.mod +++ b/go.mod @@ -38,9 +38,8 @@ require ( github.com/oapi-codegen/echo-middleware v1.0.2 github.com/oapi-codegen/runtime v1.1.1 github.com/operator-framework/api v0.27.0 - github.com/operator-framework/operator-lifecycle-manager v0.27.0 github.com/percona/everest-operator v0.6.0-dev1.0.20250408103821-238475bf8caa - github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250410143440-baa4adaf2c02 + github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250415073336-e8f1fba39786 github.com/rodaine/table v1.3.0 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 @@ -66,7 +65,6 @@ require ( ) require ( - cel.dev/expr v0.19.0 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect @@ -79,7 +77,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/Microsoft/hcsshim v0.12.0-rc.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/atotto/clipboard v0.1.4 // indirect @@ -87,6 +85,8 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect + github.com/bugsnag/bugsnag-go v1.5.3 // indirect + github.com/bugsnag/panicwrap v1.2.0 // indirect github.com/cert-manager/cert-manager v1.16.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect @@ -108,6 +108,7 @@ require ( github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect @@ -116,6 +117,7 @@ require ( github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flosch/pongo2/v6 v6.0.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.5.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect @@ -129,11 +131,11 @@ require ( github.com/go-test/deep v1.1.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.3 // indirect + github.com/gofrs/uuid v3.3.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.22.0 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -143,8 +145,6 @@ require ( github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect - github.com/h2non/filetype v1.1.3 // indirect - github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect @@ -153,6 +153,7 @@ require ( github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/labstack/gommon v0.4.2 // indirect @@ -189,10 +190,8 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect - github.com/onsi/gomega v1.37.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/operator-framework/operator-registry v1.35.0 // indirect github.com/percona/percona-backup-mongodb v1.8.1-0.20241212160532-0157f87a7eee // indirect github.com/percona/percona-postgresql-operator v0.0.0-20250313094841-676233c83e26 // indirect github.com/percona/percona-server-mongodb-operator v1.19.1 // indirect @@ -214,7 +213,6 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect @@ -229,6 +227,9 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 // indirect + github.com/yvasiyarov/gorelic v0.0.7 // indirect + github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 // indirect go.mongodb.org/mongo-driver v1.17.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect @@ -236,12 +237,12 @@ require ( go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect golang.org/x/net v0.37.0 // indirect golang.org/x/oauth2 v0.28.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250212204824-5a70512c5d8b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect google.golang.org/grpc v1.70.0 // indirect diff --git a/go.sum b/go.sum index 63dd60375..51ccf342a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -1394,7 +1392,6 @@ github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= @@ -1514,8 +1511,6 @@ github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKk github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= -github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= -github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= @@ -1524,21 +1519,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= -github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= -github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= -github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= -github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= -github.com/containers/common v0.56.0 h1:hysHUsEai1EkMXanU26UV55wMXns/a6AYmaFqJ4fEMY= -github.com/containers/common v0.56.0/go.mod h1:IjaDdfUtcs2CfCcJMZxuut4XlvkTkY9Nlqkso9xCOq4= -github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt9g048= -github.com/containers/image/v5 v5.28.0/go.mod h1:9aPnNkwHNHgGl9VlQxXEshvmOJRbdRAc1rNDD6sP2eU= -github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= -github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= -github.com/containers/ocicrypt v1.1.10 h1:r7UR6o8+lyhkEywetubUUgcKFjOWOaWz8cEBrCPX0ic= -github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8= -github.com/containers/storage v1.50.2 h1:Fys4BjFUVNRBEXlO70hFI48VW4EXsgnGisTpk9tTMsE= -github.com/containers/storage v1.50.2/go.mod h1:dpspZsUrcKD8SpTofvKWhwPDHD0MkO4Q7VE+oYdWkiA= 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= @@ -1569,7 +1549,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvw github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/distribution/distribution v2.7.1+incompatible h1:aGFx4EvJWKEh//lHPLwFhFgwFHKH06TzNVPamrMn04M= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -1588,8 +1567,6 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -1653,8 +1630,6 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -1747,8 +1722,6 @@ github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU= -github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -1802,7 +1775,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ 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/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -1928,10 +1900,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHg github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40= -github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= -github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc= -github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= 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/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -2128,8 +2096,6 @@ 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/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -2228,20 +2194,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= -github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= -github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= -github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/operator-framework/api v0.27.0 h1:OrVaGKZJvbZo58HTv2guz7aURkhVKYhFqZ/6VpifiXI= github.com/operator-framework/api v0.27.0/go.mod h1:lg2Xx+S8NQWGYlEOvFwQvH46E5EK5IrAIL7HWfAhciM= -github.com/operator-framework/operator-lifecycle-manager v0.27.0 h1:rYPF4zYL9FGzGSJ/lXgwkZPRMmzj1lHbxri2VNzK8Y0= -github.com/operator-framework/operator-lifecycle-manager v0.27.0/go.mod h1:Z/mxDU3Tr4JsSIm/6esAFHGSLxkMwAhL2aIYfNeKGm4= -github.com/operator-framework/operator-registry v1.35.0 h1:BvytqLwhgb0QiAkEODEKXq3vc2vWiHQq0IlofvFA+OI= -github.com/operator-framework/operator-registry v1.35.0/go.mod h1:foC+NO1V9JuDIOk3pjjlrPE0KVkq09m8oDVRz/a/nFA= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= -github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= 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.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= @@ -2249,8 +2205,8 @@ github.com/percona/everest-operator v0.6.0-dev1.0.20250408103821-238475bf8caa h1 github.com/percona/everest-operator v0.6.0-dev1.0.20250408103821-238475bf8caa/go.mod h1:lFCIXi5QtXkrMh5wft5AzhVV2YkH1BJUBJPeTXE8tbI= github.com/percona/percona-backup-mongodb v1.8.1-0.20241212160532-0157f87a7eee h1:LtitxWyhBqCNjIZqdvsSEPBd2HPg11lDBlIExTQAbGQ= github.com/percona/percona-backup-mongodb v1.8.1-0.20241212160532-0157f87a7eee/go.mod h1:zikIUTNTflfcth3ZJRqhvW8+7Jj38aVlg+wSV1jwnxo= -github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250410143440-baa4adaf2c02 h1:XLX7pJ22v8J2ZGnVFxFu8A+h0T0Xp/vTqjxYwieFU+A= -github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250410143440-baa4adaf2c02/go.mod h1:j5Ci48Azwb4Xs4XvZQNfleWCn2uyiZywazklxNH1ut4= +github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250415073336-e8f1fba39786 h1:D24X2b1B81gtg+0Pj1Dil/oCGL60fSBPhy7nePxhAec= +github.com/percona/percona-helm-charts/charts/everest v0.0.0-20250415073336-e8f1fba39786/go.mod h1:j5Ci48Azwb4Xs4XvZQNfleWCn2uyiZywazklxNH1ut4= github.com/percona/percona-postgresql-operator v0.0.0-20250313094841-676233c83e26 h1:hng2p9QPk/OxHVZEdsv+k0ByyHzTNSB0SWlVuuTxMjs= github.com/percona/percona-postgresql-operator v0.0.0-20250313094841-676233c83e26/go.mod h1:3D56UIi6Z0Z2gduNUuBcgjd1RNht3N8RKKmR9Wbfu4o= github.com/percona/percona-server-mongodb-operator v1.19.1 h1:lqIC7V80bZPJwjeYLYl/WA+QVQMHo193uEAx5zyIg84= @@ -2410,7 +2366,6 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -2435,8 +2390,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= @@ -2444,16 +2397,12 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU= github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= -github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -2497,7 +2446,6 @@ github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= @@ -2546,10 +2494,7 @@ go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= @@ -2587,8 +2532,6 @@ go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= @@ -3427,7 +3370,6 @@ google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqt google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= @@ -3751,12 +3693,9 @@ modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= -rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= diff --git a/internal/server/everest.go b/internal/server/everest.go index da7105092..7113ab045 100644 --- a/internal/server/everest.go +++ b/internal/server/everest.go @@ -21,11 +21,11 @@ import ( "encoding/json" "errors" "fmt" - "html/template" "io" "io/fs" "net/http" "slices" + "text/template" "github.com/getkin/kin-openapi/openapi3filter" "github.com/golang-jwt/jwt/v5" @@ -57,14 +57,14 @@ type EverestServer struct { config *config.EverestConfig l *zap.SugaredLogger echo *echo.Echo - kubeClient *kubernetes.Kubernetes + kubeConnector kubernetes.KubernetesConnector sessionMgr *session.Manager attemptsStore *RateLimiterMemoryStore handler handlers.Handler oidcProvider *oidc.ProviderConfig } -func getOIDCProviderConfig(ctx context.Context, kubeClient *kubernetes.Kubernetes) (*oidc.ProviderConfig, error) { +func getOIDCProviderConfig(ctx context.Context, kubeClient kubernetes.KubernetesConnector) (*oidc.ProviderConfig, error) { settings, err := kubeClient.GetEverestSettings(ctx) if client.IgnoreNotFound(err) != nil { return nil, errors.Join(err, errors.New("failed to get Everest settings")) @@ -89,7 +89,7 @@ func getOIDCProviderConfig(ctx context.Context, kubeClient *kubernetes.Kubernete // NewEverestServer creates and configures everest API. func NewEverestServer(ctx context.Context, c *config.EverestConfig, l *zap.SugaredLogger) (*EverestServer, error) { - kubeClient, err := kubernetes.NewInCluster(l) + kubeConnector, err := kubernetes.NewInCluster(l) if err != nil { return nil, errors.Join(err, errors.New("failed creating Kubernetes client")) } @@ -99,13 +99,13 @@ func NewEverestServer(ctx context.Context, c *config.EverestConfig, l *zap.Sugar middleware, store := sessionRateLimiter(c.CreateSessionRateLimit) echoServer.Use(middleware) sessMgr, err := session.New( - session.WithAccountManager(kubeClient.Accounts()), + session.WithAccountManager(kubeConnector.Accounts()), ) if err != nil { return nil, errors.Join(err, errors.New("failed to create session manager")) } - oidcProvider, err := getOIDCProviderConfig(ctx, kubeClient) + oidcProvider, err := getOIDCProviderConfig(ctx, kubeConnector) if err != nil { return nil, errors.Join(err, errors.New("failed to get OIDC provider config")) } @@ -114,14 +114,14 @@ func NewEverestServer(ctx context.Context, c *config.EverestConfig, l *zap.Sugar config: c, l: l, echo: echoServer, - kubeClient: kubeClient, + kubeConnector: kubeConnector, sessionMgr: sessMgr, attemptsStore: store, oidcProvider: oidcProvider, } e.echo.HTTPErrorHandler = e.errorHandlerChain() - if err := e.setupHandlers(ctx, l, kubeClient, c.VersionServiceURL); err != nil { + if err := e.setupHandlers(ctx, l, kubeConnector, c.VersionServiceURL); err != nil { return nil, err } @@ -153,7 +153,9 @@ func (e *EverestServer) initHTTPServer(ctx context.Context) error { return c.Render(http.StatusOK, "index.html", map[string]interface{}{"CSPNonce": secure.CSPNonce(c.Request().Context())}, ) - }, securityHeaders(e.oidcProvider)) + }, + securityHeaders(e.oidcProvider), + ) // Serve static files. fsys, err := fs.Sub(public.Static, "dist") @@ -211,12 +213,12 @@ func (e *EverestServer) initHTTPServer(ctx context.Context) error { func (e *EverestServer) setupHandlers( ctx context.Context, log *zap.SugaredLogger, - kubeClient *kubernetes.Kubernetes, + kubeConnector kubernetes.KubernetesConnector, vsURL string, ) error { - k8sH := k8shandler.New(log, kubeClient, vsURL) - valH := valhandler.New(log, kubeClient) - rbacH, err := rbachandler.New(ctx, log, kubeClient) + k8sH := k8shandler.New(log, kubeConnector, vsURL) + valH := valhandler.New(log, kubeConnector) + rbacH, err := rbachandler.New(ctx, log, kubeConnector) if err != nil { return errors.Join(err, errors.New("could not create rbac handler")) } diff --git a/internal/server/handlers/k8s/backup_storage.go b/internal/server/handlers/k8s/backup_storage.go index 123d7a5cc..d2284451b 100644 --- a/internal/server/handlers/k8s/backup_storage.go +++ b/internal/server/handlers/k8s/backup_storage.go @@ -10,18 +10,19 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" ) func (h *k8sHandler) ListBackupStorages(ctx context.Context, namespace string) (*everestv1alpha1.BackupStorageList, error) { - return h.kubeClient.ListBackupStorages(ctx, namespace) + return h.kubeConnector.ListBackupStorages(ctx, ctrlclient.InNamespace(namespace)) } func (h *k8sHandler) GetBackupStorage(ctx context.Context, namespace, name string) (*everestv1alpha1.BackupStorage, error) { - return h.kubeClient.GetBackupStorage(ctx, namespace, name) + return h.kubeConnector.GetBackupStorage(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } func (h *k8sHandler) CreateBackupStorage(ctx context.Context, namespace string, req *api.CreateBackupStorageParams) (*everestv1alpha1.BackupStorage, error) { @@ -45,9 +46,9 @@ func (h *k8sHandler) CreateBackupStorage(ctx context.Context, namespace string, Type: corev1.SecretTypeOpaque, StringData: backupSecretData(req.SecretKey, req.AccessKey), } - _, err = h.kubeClient.CreateSecret(ctx, secret) + _, err = h.kubeConnector.CreateSecret(ctx, secret) if k8serrors.IsAlreadyExists(err) { - if _, err := h.kubeClient.UpdateSecret(ctx, secret); err != nil { + if _, err := h.kubeConnector.UpdateSecret(ctx, secret); err != nil { return nil, fmt.Errorf("failed to update secret: %w", err) } } else if err != nil { @@ -74,10 +75,16 @@ func (h *k8sHandler) CreateBackupStorage(ctx context.Context, namespace string, if req.Description != nil { bs.Spec.Description = *req.Description } - created, err := h.kubeClient.CreateBackupStorage(ctx, bs) + created, err := h.kubeConnector.CreateBackupStorage(ctx, bs) if err != nil { // TODO: Move this logic to the operator - dErr := h.kubeClient.DeleteSecret(ctx, namespace, req.Name) + delObj := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: req.Name, + Namespace: namespace, + }, + } + dErr := h.kubeConnector.DeleteSecret(ctx, delObj) if dErr != nil { return nil, errors.Join(err, dErr) } @@ -89,7 +96,7 @@ func (h *k8sHandler) CreateBackupStorage(ctx context.Context, namespace string, func (h *k8sHandler) UpdateBackupStorage(ctx context.Context, namespace, name string, req *api.UpdateBackupStorageParams) (*everestv1alpha1.BackupStorage, error) { if req.AccessKey != nil || req.SecretKey != nil { - _, err := h.kubeClient.UpdateSecret(ctx, &corev1.Secret{ + _, err := h.kubeConnector.UpdateSecret(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -100,7 +107,7 @@ func (h *k8sHandler) UpdateBackupStorage(ctx context.Context, namespace, name st return nil, fmt.Errorf("failed to update secret: %w", err) } } - bs, err := h.kubeClient.GetBackupStorage(ctx, namespace, name) + bs, err := h.kubeConnector.GetBackupStorage(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return nil, fmt.Errorf("failed to get backup storage: %w", err) } @@ -125,21 +132,35 @@ func (h *k8sHandler) UpdateBackupStorage(ctx context.Context, namespace, name st if req.ForcePathStyle != nil { bs.Spec.ForcePathStyle = req.ForcePathStyle } - return h.kubeClient.UpdateBackupStorage(ctx, bs) + return h.kubeConnector.UpdateBackupStorage(ctx, bs) } func (h *k8sHandler) DeleteBackupStorage(ctx context.Context, namespace, name string) error { - used, err := h.kubeClient.IsBackupStorageUsed(ctx, namespace, name) + used, err := h.kubeConnector.IsBackupStorageUsed(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { - return fmt.Errorf("failed to check if backup storage is used: %w", err) + return fmt.Errorf("failed to check if backup storage='%s' in namespace='%s' is used: %w", name, namespace, err) } if used { - return fmt.Errorf("backup storage '%s' in namespace '%s' is in use", name, namespace) + return fmt.Errorf("backup storage='%s' in namespace='%s' is in use", name, namespace) + } + + delBSObj := &everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, } - if err := h.kubeClient.DeleteBackupStorage(ctx, namespace, name); client.IgnoreNotFound(err) != nil { + if err := h.kubeConnector.DeleteBackupStorage(ctx, delBSObj); ctrlclient.IgnoreNotFound(err) != nil { return fmt.Errorf("failed to delete backup storage: %w", err) } - if err := h.kubeClient.DeleteSecret(ctx, namespace, name); client.IgnoreNotFound(err) != nil { + + delSecObj := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + if err := h.kubeConnector.DeleteSecret(ctx, delSecObj); ctrlclient.IgnoreNotFound(err) != nil { return fmt.Errorf("failed to delete secret: %w", err) } return nil diff --git a/internal/server/handlers/k8s/database_cluster.go b/internal/server/handlers/k8s/database_cluster.go index 3ee867efe..611e4817a 100644 --- a/internal/server/handlers/k8s/database_cluster.go +++ b/internal/server/handlers/k8s/database_cluster.go @@ -15,6 +15,8 @@ import ( goversion "github.com/hashicorp/go-version" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" @@ -35,17 +37,17 @@ const ( ) func (h *k8sHandler) CreateDatabaseCluster(ctx context.Context, db *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return h.kubeClient.CreateDatabaseCluster(ctx, db) + return h.kubeConnector.CreateDatabaseCluster(ctx, db) } func (h *k8sHandler) ListDatabaseClusters(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseClusterList, error) { - return h.kubeClient.ListDatabaseClusters(ctx, namespace) + return h.kubeConnector.ListDatabaseClusters(ctx, ctrlclient.InNamespace(namespace)) } func (h *k8sHandler) DeleteDatabaseCluster(ctx context.Context, namespace, name string, req *api.DeleteDatabaseClusterParams) error { cleanupStorage := pointer.Get(req.CleanupBackupStorage) - backups, err := h.kubeClient.ListDatabaseClusterBackups(ctx, namespace, metav1.ListOptions{}) + backups, err := h.kubeConnector.ListDatabaseClusterBackups(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return err } @@ -73,23 +75,30 @@ func (h *k8sHandler) DeleteDatabaseCluster(ctx context.Context, namespace, name return errors.Join(err, errors.New("could not ensure foreground deletion")) } } - return h.kubeClient.DeleteDatabaseCluster(ctx, namespace, name) + + delObj := &everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } + return h.kubeConnector.DeleteDatabaseCluster(ctx, delObj) } func (h *k8sHandler) UpdateDatabaseCluster(ctx context.Context, db *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return h.kubeClient.UpdateDatabaseCluster(ctx, db) + return h.kubeConnector.UpdateDatabaseCluster(ctx, db) } func (h *k8sHandler) GetDatabaseCluster(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseCluster, error) { - return h.kubeClient.GetDatabaseCluster(ctx, namespace, name) + return h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } func (h *k8sHandler) GetDatabaseClusterCredentials(ctx context.Context, namespace, name string) (*api.DatabaseClusterCredential, error) { - databaseCluster, err := h.kubeClient.GetDatabaseCluster(ctx, namespace, name) + databaseCluster, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return nil, fmt.Errorf("failed to get database cluster %s/%s: %w", namespace, name, err) } - secret, err := h.kubeClient.GetSecret(ctx, namespace, databaseCluster.Spec.Engine.UserSecretsName) + secret, err := h.kubeConnector.GetSecret(ctx, types.NamespacedName{Namespace: namespace, Name: databaseCluster.Spec.Engine.UserSecretsName}) if err != nil { return nil, fmt.Errorf("failed to get secret %s/%s: %w", namespace, databaseCluster.Spec.Engine.UserSecretsName, err) } @@ -114,9 +123,7 @@ func (h *k8sHandler) GetDatabaseClusterCredentials(ctx context.Context, namespac //nolint:funlen func (h *k8sHandler) GetDatabaseClusterComponents(ctx context.Context, namespace, name string) ([]api.DatabaseClusterComponent, error) { - pods, err := h.kubeClient.GetPods(ctx, namespace, &metav1.LabelSelector{ - MatchLabels: map[string]string{"app.kubernetes.io/instance": name}, - }) + pods, err := h.kubeConnector.ListPods(ctx, ctrlclient.InNamespace(namespace), ctrlclient.MatchingLabels{"app.kubernetes.io/instance": name}) if err != nil { return nil, fmt.Errorf("failed to get pods for database cluster %s/%s: %w", namespace, name, err) } @@ -182,7 +189,7 @@ func (h *k8sHandler) GetDatabaseClusterComponents(ctx context.Context, namespace } func (h *k8sHandler) GetDatabaseClusterPitr(ctx context.Context, namespace, name string) (*api.DatabaseClusterPitr, error) { - databaseCluster, err := h.kubeClient.GetDatabaseCluster(ctx, namespace, name) + databaseCluster, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return nil, fmt.Errorf("failed to get database cluster %s/%s: %w", namespace, name, err) } @@ -193,14 +200,10 @@ func (h *k8sHandler) GetDatabaseClusterPitr(ctx context.Context, namespace, name return response, nil } - options := metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - "clusterName": name, - }, - }), - } - backups, err := h.kubeClient.ListDatabaseClusterBackups(ctx, namespace, options) + backups, err := h.kubeConnector.ListDatabaseClusterBackups(ctx, + ctrlclient.InNamespace(namespace), + ctrlclient.MatchingLabels{common.DatabaseClusterNameLabel: name}, + ) if err != nil { return nil, fmt.Errorf("failed to list database cluster backups: %w", err) } @@ -243,13 +246,13 @@ var everestAPIConstantBackoff = backoff.WithMaxRetries(backoff.NewConstantBackOf func (h *k8sHandler) ensureBackupStorageProtection(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) error { // We wrap this logic in a retry loop to reduce the chances of resource conflicts. return backoff.Retry(func() error { - backup, err := h.kubeClient.GetDatabaseClusterBackup(ctx, backup.GetNamespace(), backup.GetName()) + backup, err := h.kubeConnector.GetDatabaseClusterBackup(ctx, types.NamespacedName{Namespace: backup.GetNamespace(), Name: backup.GetName()}) if err != nil { return err } controllerutil.AddFinalizer(backup, everestv1alpha1.DBBackupStorageProtectionFinalizer) controllerutil.AddFinalizer(backup, common.ForegroundDeletionFinalizer) - _, err = h.kubeClient.UpdateDatabaseClusterBackup(ctx, backup) + _, err = h.kubeConnector.UpdateDatabaseClusterBackup(ctx, backup) return err }, backoff.WithContext(everestAPIConstantBackoff, ctx), @@ -259,12 +262,12 @@ func (h *k8sHandler) ensureBackupStorageProtection(ctx context.Context, backup * func (h *k8sHandler) ensureBackupForegroundDeletion(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) error { // We wrap this logic in a retry loop to reduce the chances of resource conflicts. return backoff.Retry(func() error { - backup, err := h.kubeClient.GetDatabaseClusterBackup(ctx, backup.GetNamespace(), backup.GetName()) + backup, err := h.kubeConnector.GetDatabaseClusterBackup(ctx, types.NamespacedName{Namespace: backup.GetNamespace(), Name: backup.GetName()}) if err != nil { return err } controllerutil.AddFinalizer(backup, common.ForegroundDeletionFinalizer) - _, err = h.kubeClient.UpdateDatabaseClusterBackup(ctx, backup) + _, err = h.kubeConnector.UpdateDatabaseClusterBackup(ctx, backup) return err }, backoff.WithContext(everestAPIConstantBackoff, ctx), @@ -281,7 +284,7 @@ func (h *k8sHandler) connectionURL(ctx context.Context, db *everestv1alpha1.Data case everestv1alpha1.DatabaseEnginePXC: url = queryEscapedURL("jdbc:mysql", user, password, defaultHost) case everestv1alpha1.DatabaseEnginePSMDB: - hosts, err := psmdbHosts(ctx, db, h.kubeClient.GetPods) + hosts, err := psmdbHosts(ctx, db, h.kubeConnector.ListPods) if err != nil { h.log.Error(err) return nil @@ -303,7 +306,7 @@ func queryEscapedURL(scheme, user, password, hosts string) string { func psmdbHosts( ctx context.Context, db *everestv1alpha1.DatabaseCluster, - getPods func(ctx context.Context, namespace string, labelSelector *metav1.LabelSelector) (*corev1.PodList, error), + getPods func(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.PodList, error), ) (string, error) { // for sharded clusters use a single entry point (mongos) if db.Spec.Sharding != nil && db.Spec.Sharding.Enabled { @@ -315,10 +318,13 @@ func psmdbHosts( } // for non-sharded internal clusters use a list of comma-separated host:port pairs from each node - pods, err := getPods(ctx, db.Namespace, &metav1.LabelSelector{MatchLabels: map[string]string{ - "app.kubernetes.io/instance": db.Name, - "app.kubernetes.io/component": "mongod", - }}) + pods, err := getPods(ctx, + ctrlclient.InNamespace(db.GetNamespace()), + ctrlclient.MatchingLabels{ + "app.kubernetes.io/instance": db.GetName(), + "app.kubernetes.io/component": "mongod", + }, + ) if err != nil { return "", err } diff --git a/internal/server/handlers/k8s/database_cluster_backup.go b/internal/server/handlers/k8s/database_cluster_backup.go index 65139e6ee..3534aae9f 100644 --- a/internal/server/handlers/k8s/database_cluster_backup.go +++ b/internal/server/handlers/k8s/database_cluster_backup.go @@ -7,23 +7,19 @@ import ( "github.com/AlekSi/pointer" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" -) - -const ( - databaseClusterNameLabel = "clusterName" + "github.com/percona/everest/pkg/common" ) func (h *k8sHandler) ListDatabaseClusterBackups(ctx context.Context, namespace, clusterName string) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return h.kubeClient.ListDatabaseClusterBackups(ctx, namespace, metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - "clusterName": clusterName, - }, - }), - }) + return h.kubeConnector.ListDatabaseClusterBackups(ctx, + ctrlclient.InNamespace(namespace), + ctrlclient.MatchingLabels{common.DatabaseClusterNameLabel: clusterName}, + ) } func (h *k8sHandler) CreateDatabaseClusterBackup(ctx context.Context, req *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) { @@ -32,12 +28,12 @@ func (h *k8sHandler) CreateDatabaseClusterBackup(ctx context.Context, req *evere } else if !ok { return nil, errors.New("backup is already running for the specified cluster") } - return h.kubeClient.CreateDatabaseClusterBackup(ctx, req) + return h.kubeConnector.CreateDatabaseClusterBackup(ctx, req) } func (h *k8sHandler) DeleteDatabaseClusterBackup(ctx context.Context, namespace, name string, req *api.DeleteDatabaseClusterBackupParams) error { cleanupStorage := pointer.Get(req.CleanupBackupStorage) - backup, err := h.kubeClient.GetDatabaseClusterBackup(ctx, namespace, name) + backup, err := h.kubeConnector.GetDatabaseClusterBackup(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return errors.Join(err, errors.New("could not get Database Cluster Backup")) } @@ -50,22 +46,25 @@ func (h *k8sHandler) DeleteDatabaseClusterBackup(ctx context.Context, namespace, if err := h.ensureBackupForegroundDeletion(ctx, backup); err != nil { return errors.Join(err, errors.New("could not ensure backup foreground deletion")) } - return h.kubeClient.DeleteDatabaseClusterBackup(ctx, namespace, name) + delObj := &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } + return h.kubeConnector.DeleteDatabaseClusterBackup(ctx, delObj) } func (h *k8sHandler) GetDatabaseClusterBackup(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterBackup, error) { - return h.kubeClient.GetDatabaseClusterBackup(ctx, namespace, name) + return h.kubeConnector.GetDatabaseClusterBackup(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } // Returns `true` if no backups are running for the specified cluster. func (h *k8sHandler) ensureNoBackupsRunningForCluster(ctx context.Context, dbClusterName, namespace string) (bool, error) { - backupList, err := h.kubeClient.ListDatabaseClusterBackups(ctx, namespace, metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - databaseClusterNameLabel: dbClusterName, - }, - }), - }) + backupList, err := h.kubeConnector.ListDatabaseClusterBackups(ctx, + ctrlclient.InNamespace(namespace), + ctrlclient.MatchingLabels{common.DatabaseClusterNameLabel: dbClusterName}, + ) if err != nil { return false, errors.Join(err, errors.New("could not list Database Cluster Backups")) } diff --git a/internal/server/handlers/k8s/database_cluster_restore.go b/internal/server/handlers/k8s/database_cluster_restore.go index 18f13c913..a3c242ad6 100644 --- a/internal/server/handlers/k8s/database_cluster_restore.go +++ b/internal/server/handlers/k8s/database_cluster_restore.go @@ -6,39 +6,45 @@ import ( "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" + "github.com/percona/everest/pkg/common" ) func (h *k8sHandler) ListDatabaseClusterRestores(ctx context.Context, namespace, clusterName string) (*everestv1alpha1.DatabaseClusterRestoreList, error) { - return h.kubeClient.ListDatabaseClusterRestores(ctx, namespace, metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - "clusterName": clusterName, - }, - }), - }) + return h.kubeConnector.ListDatabaseClusterRestores(ctx, + ctrlclient.InNamespace(namespace), + ctrlclient.MatchingLabels{common.DatabaseClusterNameLabel: clusterName}, + ) } func (h *k8sHandler) CreateDatabaseClusterRestore(ctx context.Context, req *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - dbCluster, err := h.kubeClient.GetDatabaseCluster(ctx, req.GetNamespace(), req.Spec.DBClusterName) + dbCluster, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: req.GetNamespace(), Name: req.Spec.DBClusterName}) if err != nil { return nil, fmt.Errorf("failed to GetDatabaseCluster: %w", err) } if dbCluster.Status.Status == everestv1alpha1.AppStateRestoring { return nil, errors.New("another restore is already in progress") } - return h.kubeClient.CreateDatabaseClusterRestore(ctx, req) + return h.kubeConnector.CreateDatabaseClusterRestore(ctx, req) } func (h *k8sHandler) DeleteDatabaseClusterRestore(ctx context.Context, namespace, name string) error { - return h.kubeClient.DeleteDatabaseClusterRestore(ctx, namespace, name) + delObj := &everestv1alpha1.DatabaseClusterRestore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } + return h.kubeConnector.DeleteDatabaseClusterRestore(ctx, delObj) } func (h *k8sHandler) GetDatabaseClusterRestore(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterRestore, error) { - return h.kubeClient.GetDatabaseClusterRestore(ctx, namespace, name) + return h.kubeConnector.GetDatabaseClusterRestore(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } func (h *k8sHandler) UpdateDatabaseClusterRestore(ctx context.Context, req *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - return h.kubeClient.UpdateDatabaseClusterRestore(ctx, req) + return h.kubeConnector.UpdateDatabaseClusterRestore(ctx, req) } diff --git a/internal/server/handlers/k8s/database_cluster_test.go b/internal/server/handlers/k8s/database_cluster_test.go index 8ca509293..b205d0416 100644 --- a/internal/server/handlers/k8s/database_cluster_test.go +++ b/internal/server/handlers/k8s/database_cluster_test.go @@ -6,13 +6,15 @@ import ( "time" "github.com/AlekSi/pointer" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/pkg/kubernetes" - "github.com/percona/everest/pkg/kubernetes/client" ) func TestLatestRestorableDate(t *testing.T) { @@ -163,9 +165,9 @@ func TestGetDefaultUploadInterval(t *testing.T) { func TestConnectionURL(t *testing.T) { t.Parallel() type testCase struct { - name string - podList corev1.PodList - db everestv1alpha1.DatabaseCluster + name string + objs []ctrlclient.Object + db everestv1alpha1.DatabaseCluster user, password, expected string @@ -174,40 +176,101 @@ func TestConnectionURL(t *testing.T) { cases := []testCase{ { name: "non-sharded psmdb 1 node", - podList: corev1.PodList{Items: []corev1.Pod{ - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}}, - }}, + objs: []ctrlclient.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-0", + Namespace: "ns-1", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}, + }, + }, db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-1", + }, Spec: everestv1alpha1.DatabaseClusterSpec{Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePSMDB}}, - Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-rs0.my-special-place.svc.cluster.local", Port: 27017}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-rs0.ns-1.svc.cluster.local", Port: 27017}, }, user: "databaseAdmin", password: "azoE4FwvDRVycH83CO", - expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-try-rs0-0.mongodb-56u-rs0.my-special-place.svc.cluster.local:27017", + expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-try-rs0-0.mongodb-56u-rs0.ns-1.svc.cluster.local:27017", }, { name: "non-sharded psmdb, 3 node, external access disabled", - podList: corev1.PodList{Items: []corev1.Pod{ - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}}, - }}, + objs: []ctrlclient.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-0", + Namespace: "ns-2", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-1", + Namespace: "ns-2", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-2", + Namespace: "ns-2", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}, + }, + }, db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-2", + }, Spec: everestv1alpha1.DatabaseClusterSpec{Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePSMDB}}, - Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-rs0.my-special-place.svc.cluster.local", Port: 27017}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-rs0.ns-2.svc.cluster.local", Port: 27017}, }, user: "databaseAdmin", password: "azoE4FwvDRVycH83CO", - expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-try-rs0-0.mongodb-56u-rs0.my-special-place.svc.cluster.local:27017,mongodb-try-rs0-1.mongodb-56u-rs0.my-special-place.svc.cluster.local:27017,mongodb-try-rs0-2.mongodb-56u-rs0.my-special-place.svc.cluster.local:27017", + expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-try-rs0-0.mongodb-56u-rs0.ns-2.svc.cluster.local:27017,mongodb-try-rs0-1.mongodb-56u-rs0.ns-2.svc.cluster.local:27017,mongodb-try-rs0-2.mongodb-56u-rs0.ns-2.svc.cluster.local:27017", }, { name: "non-sharded psmdb, 3 node, external access enabled", - podList: corev1.PodList{Items: []corev1.Pod{ - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}}, - }}, + objs: []ctrlclient.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-0", + Namespace: "ns-3", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-1", + Namespace: "ns-3", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-2", + Namespace: "ns-3", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}, + }, + }, db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-3", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePSMDB}, Proxy: everestv1alpha1.Proxy{Expose: everestv1alpha1.Expose{Type: "external"}}, @@ -220,12 +283,37 @@ func TestConnectionURL(t *testing.T) { }, { name: "sharded psmdb, 3 node, external access enabled", - podList: corev1.PodList{Items: []corev1.Pod{ - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}}, - {Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}}, - }}, + objs: []ctrlclient.Object{ + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-0", + Namespace: "ns-4", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-0"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-1", + Namespace: "ns-4", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-1"}, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mongodb-try-rs0-2", + Namespace: "ns-4", + Labels: map[string]string{"app.kubernetes.io/instance": "psmdb-try", "app.kubernetes.io/component": "mongod"}, + }, + Spec: corev1.PodSpec{Hostname: "mongodb-try-rs0-2"}, + }, + }, db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-4", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePSMDB}, Sharding: &everestv1alpha1.Sharding{Enabled: true}, @@ -238,52 +326,75 @@ func TestConnectionURL(t *testing.T) { expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@34.34.163.11:27017", }, { - name: "sharded psmdb, external access disabled", - podList: corev1.PodList{Items: []corev1.Pod{}}, + name: "sharded psmdb, external access disabled", db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-5", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePSMDB}, Sharding: &everestv1alpha1.Sharding{Enabled: true}, }, - Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-mongos.my-special-place.svc.cluster.local", Port: 27017}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mongodb-56u-mongos.ns-5.svc.cluster.local", Port: 27017}, }, user: "databaseAdmin", password: "azoE4FwvDRVycH83CO", - expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-56u-mongos.my-special-place.svc.cluster.local:27017", + expected: "mongodb://databaseAdmin:azoE4FwvDRVycH83CO@mongodb-56u-mongos.ns-5.svc.cluster.local:27017", }, { - name: "pg", - podList: corev1.PodList{Items: []corev1.Pod{}}, + name: "pg", db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-6", + }, Spec: everestv1alpha1.DatabaseClusterSpec{Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePostgresql}}, - Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "postgresql-a5d-pgbouncer.everest.svc", Port: 5432}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "postgresql-a5d-pgbouncer.ns-6.svc", Port: 5432}, }, user: "postgres", password: "55aBDedMF;So|C?^3x|h.dDC", - expected: "postgres://postgres:55aBDedMF%3BSo%7CC%3F%5E3x%7Ch.dDC@postgresql-a5d-pgbouncer.everest.svc:5432", + expected: "postgres://postgres:55aBDedMF%3BSo%7CC%3F%5E3x%7Ch.dDC@postgresql-a5d-pgbouncer.ns-6.svc:5432", }, { - name: "pxc", - podList: corev1.PodList{Items: []corev1.Pod{}}, + name: "pxc external domain", db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pxc-try", + Namespace: "ns-7", + }, Spec: everestv1alpha1.DatabaseClusterSpec{Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePXC}}, - Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mysql-29o-haproxy.everest", Port: 3306}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mysql-29o-haproxy.everest.io", Port: 3306}, }, user: "root", password: ",0#3PdCIc=9CS(do2", - expected: "jdbc:mysql://root:%2C0%233PdCIc%3D9CS%28do2@mysql-29o-haproxy.everest:3306", + expected: "jdbc:mysql://root:%2C0%233PdCIc%3D9CS%28do2@mysql-29o-haproxy.everest.io:3306", + }, + { + name: "pxc local service", + db: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pxc-try", + Namespace: "ns-8", + }, + Spec: everestv1alpha1.DatabaseClusterSpec{Engine: everestv1alpha1.Engine{Type: everestv1alpha1.DatabaseEnginePXC}}, + Status: everestv1alpha1.DatabaseClusterStatus{Hostname: "mysql-56o-haproxy.ns-8.svc", Port: 3306}, + }, + user: "root", + password: ",0#123AIc=9CS(do2", + expected: "jdbc:mysql://root:%2C0%23123AIc%3D9CS%28do2@mysql-56o-haproxy.ns-8.svc:3306", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - k := &kubernetes.Kubernetes{} - mockConnector := &client.MockKubeClientConnector{} - mockConnector.On("GetPods", mock.Anything, mock.Anything, mock.Anything). - Return(&tc.podList, nil) - k.WithClient(mockConnector) - h := &k8sHandler{kubeClient: k} + mockClient := fakeclient.NewClientBuilder(). + WithScheme(kubernetes.CreateScheme()). + WithObjects(tc.objs...). + WithObjects(&tc.db) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) + h := &k8sHandler{kubeConnector: k} url := h.connectionURL(context.Background(), &tc.db, tc.user, tc.password) require.Equal(t, tc.expected, *url) }) diff --git a/internal/server/handlers/k8s/database_engine.go b/internal/server/handlers/k8s/database_engine.go index 467985266..f7d17fe33 100644 --- a/internal/server/handlers/k8s/database_engine.go +++ b/internal/server/handlers/k8s/database_engine.go @@ -11,6 +11,8 @@ import ( "github.com/AlekSi/pointer" "github.com/cenkalti/backoff" goversion "github.com/hashicorp/go-version" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -23,15 +25,15 @@ var ( ) func (h *k8sHandler) ListDatabaseEngines(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseEngineList, error) { - return h.kubeClient.ListDatabaseEngines(ctx, namespace) + return h.kubeConnector.ListDatabaseEngines(ctx, ctrlclient.InNamespace(namespace)) } func (h *k8sHandler) GetDatabaseEngine(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseEngine, error) { - return h.kubeClient.GetDatabaseEngine(ctx, namespace, name) + return h.kubeConnector.GetDatabaseEngine(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } func (h *k8sHandler) UpdateDatabaseEngine(ctx context.Context, req *everestv1alpha1.DatabaseEngine) (*everestv1alpha1.DatabaseEngine, error) { - return h.kubeClient.UpdateDatabaseEngine(ctx, req) + return h.kubeConnector.UpdateDatabaseEngine(ctx, req) } func (h *k8sHandler) GetUpgradePlan(ctx context.Context, namespace string) (*api.UpgradePlan, error) { @@ -42,7 +44,7 @@ func (h *k8sHandler) GetUpgradePlan(ctx context.Context, namespace string) (*api // No upgrades available, so we will check if our clusters are ready for current version. if len(pointer.Get(result.Upgrades)) == 0 { result.PendingActions = pointer.To([]api.UpgradeTask{}) - engines, err := h.kubeClient.ListDatabaseEngines(ctx, namespace) + engines, err := h.kubeConnector.ListDatabaseEngines(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return nil, err } @@ -101,7 +103,7 @@ func (h *k8sHandler) setLockDBEnginesForUpgrade( ) error { return backoff.Retry(func() error { for _, upgrade := range pointer.Get(up.Upgrades) { - if err := h.kubeClient.SetDatabaseEngineLock(ctx, namespace, pointer.Get(upgrade.Name), lock); err != nil { + if err := h.kubeConnector.SetDatabaseEngineLock(ctx, types.NamespacedName{Namespace: namespace, Name: pointer.Get(upgrade.Name)}, lock); err != nil { return err } } @@ -114,7 +116,7 @@ func (h *k8sHandler) getUpgradePlan( ctx context.Context, namespace string, ) (*api.UpgradePlan, error) { - engines, err := h.kubeClient.ListDatabaseEngines(ctx, namespace) + engines, err := h.kubeConnector.ListDatabaseEngines(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return nil, err } @@ -163,7 +165,7 @@ func (h *k8sHandler) getOperatorUpgradePreflight( ) (*operatorUpgradePreflight, error) { namespace := engine.GetNamespace() // Get all database clusters in the namespace. - databases, err := h.kubeClient.ListDatabaseClusters(ctx, namespace) + databases, err := h.kubeConnector.ListDatabaseClusters(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return nil, err } @@ -332,7 +334,7 @@ func (h *k8sHandler) getDBPostUpgradeTasks( ) ([]api.UpgradeTask, error) { namespace := engine.GetNamespace() // List all clusters in this namespace. - clusters, err := h.kubeClient.ListDatabaseClusters(ctx, namespace) + clusters, err := h.kubeConnector.ListDatabaseClusters(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return nil, err } @@ -367,7 +369,7 @@ func (h *k8sHandler) startOperatorUpgradeWithRetry(ctx context.Context, namespac } func (h *k8sHandler) startOperatorUpgrade(ctx context.Context, namespace string) error { - engines, err := h.kubeClient.ListDatabaseEngines(ctx, namespace) + engines, err := h.kubeConnector.ListDatabaseEngines(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return err } @@ -393,7 +395,7 @@ func (h *k8sHandler) startOperatorUpgrade(ctx context.Context, namespace string) // approve install plans. for _, plan := range installPlans { if err := backoff.Retry(func() error { - _, err := h.kubeClient.ApproveInstallPlan(ctx, namespace, plan) + _, err := h.kubeConnector.ApproveInstallPlan(ctx, types.NamespacedName{Namespace: namespace, Name: plan}) return err }, backoff.WithContext(everestAPIConstantBackoff, ctx), ); err != nil { diff --git a/internal/server/handlers/k8s/handler.go b/internal/server/handlers/k8s/handler.go index c32b861ae..92aca9c22 100644 --- a/internal/server/handlers/k8s/handler.go +++ b/internal/server/handlers/k8s/handler.go @@ -9,7 +9,7 @@ import ( // k8sHandler is usually the last handler in the chain, so it does not have a next handler. type k8sHandler struct { - kubeClient *kubernetes.Kubernetes + kubeConnector kubernetes.KubernetesConnector log *zap.SugaredLogger versionServiceURL string } @@ -17,10 +17,10 @@ type k8sHandler struct { // New returns a new RBAC handler. // //nolint:ireturn -func New(log *zap.SugaredLogger, kubeClient *kubernetes.Kubernetes, vsURL string) handlers.Handler { +func New(log *zap.SugaredLogger, kubeConnector kubernetes.KubernetesConnector, vsURL string) handlers.Handler { l := log.With("handler", "k8s") return &k8sHandler{ - kubeClient: kubeClient, + kubeConnector: kubeConnector, log: l, versionServiceURL: vsURL, } diff --git a/internal/server/handlers/k8s/kubernetes.go b/internal/server/handlers/k8s/kubernetes.go index 61c559c2b..8f387c2f3 100644 --- a/internal/server/handlers/k8s/kubernetes.go +++ b/internal/server/handlers/k8s/kubernetes.go @@ -11,6 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" "github.com/percona/everest/api" "github.com/percona/everest/pkg/common" @@ -24,7 +25,7 @@ const ( func (h *k8sHandler) GetKubernetesClusterResources(ctx context.Context) (*api.KubernetesClusterResources, error) { // Get cluster type - clusterType, err := h.kubeClient.GetClusterType(ctx) + clusterType, err := h.kubeConnector.GetClusterType(ctx) if err != nil { // Instead of failing we switch to a generic cluster type. clusterType = kubernetes.ClusterTypeGeneric @@ -32,13 +33,13 @@ func (h *k8sHandler) GetKubernetesClusterResources(ctx context.Context) (*api.Ku var volumes *corev1.PersistentVolumeList if clusterType == kubernetes.ClusterTypeEKS { - volumes, err = h.kubeClient.GetPersistentVolumes(ctx) + volumes, err = h.kubeConnector.ListPersistentVolumes(ctx) if err != nil { - return nil, fmt.Errorf("failed to GetPersistentVolumes: %w", err) + return nil, fmt.Errorf("failed to ListPersistentVolumes: %w", err) } } - res, err := h.calculateClusterResources(ctx, h.kubeClient, clusterType, volumes) + res, err := h.calculateClusterResources(ctx, h.kubeConnector, clusterType, volumes) if err != nil { return nil, fmt.Errorf("failed to calculateClusterResources: %w", err) } @@ -46,13 +47,13 @@ func (h *k8sHandler) GetKubernetesClusterResources(ctx context.Context) (*api.Ku } func (h *k8sHandler) GetKubernetesClusterInfo(ctx context.Context) (*api.KubernetesClusterInfo, error) { - clusterType, err := h.kubeClient.GetClusterType(ctx) + clusterType, err := h.kubeConnector.GetClusterType(ctx) if err != nil { return nil, fmt.Errorf("failed to GetClusterType: %w", err) } - storagesList, err := h.kubeClient.GetStorageClasses(ctx) + storagesList, err := h.kubeConnector.ListStorageClasses(ctx) if err != nil { - return nil, fmt.Errorf("failed to GetStorageClasses: %w", err) + return nil, fmt.Errorf("failed to ListStorageClasses: %w", err) } // convert k8s storage class to api storage class @@ -72,7 +73,7 @@ func (h *k8sHandler) GetKubernetesClusterInfo(ctx context.Context) (*api.Kuberne } func (h *k8sHandler) GetUserPermissions(ctx context.Context) (*api.UserPermissions, error) { - cm, err := h.kubeClient.GetConfigMap(ctx, common.SystemNamespace, common.EverestRBACConfigMapName) + cm, err := h.kubeConnector.GetConfigMap(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestRBACConfigMapName}) if err != nil { return nil, errors.Join(err, errors.New("could not get Everest RBAC ConfigMap")) } @@ -83,7 +84,7 @@ func (h *k8sHandler) GetUserPermissions(ctx context.Context) (*api.UserPermissio } func (h *k8sHandler) GetSettings(ctx context.Context) (*api.Settings, error) { - settings, err := h.kubeClient.GetEverestSettings(ctx) + settings, err := h.kubeConnector.GetEverestSettings(ctx) if err != nil && !k8serrors.IsNotFound(err) { return nil, err } @@ -115,7 +116,7 @@ func storageClasses(storagesList *storagev1.StorageClassList) []string { } func (h *k8sHandler) calculateClusterResources( - ctx context.Context, kubeClient *kubernetes.Kubernetes, clusterType kubernetes.ClusterType, + ctx context.Context, kubeClient kubernetes.KubernetesConnector, clusterType kubernetes.ClusterType, volumes *corev1.PersistentVolumeList, ) (*api.KubernetesClusterResources, error) { allCPUMillis, allMemoryBytes, allDiskBytes, err := kubeClient.GetAllClusterResources( diff --git a/internal/server/handlers/k8s/monitoring_instance.go b/internal/server/handlers/k8s/monitoring_instance.go index 9136214eb..d6b047a24 100644 --- a/internal/server/handlers/k8s/monitoring_instance.go +++ b/internal/server/handlers/k8s/monitoring_instance.go @@ -10,6 +10,8 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -17,11 +19,11 @@ import ( ) func (h *k8sHandler) ListMonitoringInstances(ctx context.Context, namespace string) (*everestv1alpha1.MonitoringConfigList, error) { - return h.kubeClient.ListMonitoringConfigs(ctx, namespace) + return h.kubeConnector.ListMonitoringConfigs(ctx, ctrlclient.InNamespace(namespace)) } func (h *k8sHandler) CreateMonitoringInstance(ctx context.Context, namespace string, req *api.CreateMonitoringInstanceJSONRequestBody) (*everestv1alpha1.MonitoringConfig, error) { - m, err := h.kubeClient.GetMonitoringConfig(ctx, namespace, req.Name) + m, err := h.kubeConnector.GetMonitoringConfig(ctx, types.NamespacedName{Namespace: namespace, Name: req.Name}) if err != nil && !k8serrors.IsNotFound(err) { return nil, err } @@ -40,25 +42,38 @@ func (h *k8sHandler) CreateMonitoringInstance(ctx context.Context, namespace str } func (h *k8sHandler) DeleteMonitoringInstance(ctx context.Context, namespace, name string) error { - used, err := h.kubeClient.IsMonitoringConfigUsed(ctx, namespace, name) + used, err := h.kubeConnector.IsMonitoringConfigUsed(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return err } if used { return fmt.Errorf("monitoring instance '%s' in namespace '%s' is in use", name, namespace) } - if err := h.kubeClient.DeleteMonitoringConfig(ctx, namespace, name); err != nil { + delMCObj := &everestv1alpha1.MonitoringConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + if err := h.kubeConnector.DeleteMonitoringConfig(ctx, delMCObj); err != nil { return err } - return h.kubeClient.DeleteSecret(ctx, namespace, name) + + delSecObj := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + return h.kubeConnector.DeleteSecret(ctx, delSecObj) } func (h *k8sHandler) GetMonitoringInstance(ctx context.Context, namespace, name string) (*everestv1alpha1.MonitoringConfig, error) { - return h.kubeClient.GetMonitoringConfig(ctx, namespace, name) + return h.kubeConnector.GetMonitoringConfig(ctx, types.NamespacedName{Namespace: namespace, Name: name}) } func (h *k8sHandler) UpdateMonitoringInstance(ctx context.Context, namespace, name string, req *api.UpdateMonitoringInstanceJSONRequestBody) (*everestv1alpha1.MonitoringConfig, error) { - m, err := h.kubeClient.GetMonitoringConfig(ctx, namespace, name) + m, err := h.kubeConnector.GetMonitoringConfig(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return nil, err } @@ -78,7 +93,7 @@ func (h *k8sHandler) UpdateMonitoringInstance(ctx context.Context, namespace, na } } if apiKey != "" { - _, err := h.kubeClient.UpdateSecret(ctx, &corev1.Secret{ + _, err := h.kubeConnector.UpdateSecret(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -99,7 +114,7 @@ func (h *k8sHandler) UpdateMonitoringInstance(ctx context.Context, namespace, na if req.VerifyTLS != nil { m.Spec.VerifyTLS = req.VerifyTLS } - return h.kubeClient.UpdateMonitoringConfig(ctx, m) + return h.kubeConnector.UpdateMonitoringConfig(ctx, m) } func (h *k8sHandler) getPMMApiKey(ctx context.Context, params *api.CreateMonitoringInstanceJSONRequestBody) (string, error) { @@ -127,9 +142,9 @@ func (h *k8sHandler) createMonitoringK8sResources( Type: corev1.SecretTypeOpaque, StringData: h.monitoringConfigSecretData(apiKey), } - if _, err := h.kubeClient.CreateSecret(c, secret); err != nil { + if _, err := h.kubeConnector.CreateSecret(c, secret); err != nil { if k8serrors.IsAlreadyExists(err) { - _, err = h.kubeClient.UpdateSecret(c, secret) + _, err = h.kubeConnector.UpdateSecret(c, secret) if err != nil { return nil, fmt.Errorf("could not update k8s secret %s", params.Name) } @@ -137,7 +152,7 @@ func (h *k8sHandler) createMonitoringK8sResources( return nil, fmt.Errorf("failed creating secret in the Kubernetes cluster") } } - created, err := h.kubeClient.CreateMonitoringConfig(c, &everestv1alpha1.MonitoringConfig{ + created, err := h.kubeConnector.CreateMonitoringConfig(c, &everestv1alpha1.MonitoringConfig{ ObjectMeta: metav1.ObjectMeta{ Name: params.Name, Namespace: namespace, @@ -152,7 +167,13 @@ func (h *k8sHandler) createMonitoringK8sResources( }, }) if err != nil { - if dErr := h.kubeClient.DeleteSecret(c, namespace, params.Name); dErr != nil { + delObj := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: params.Name, + Namespace: namespace, + }, + } + if dErr := h.kubeConnector.DeleteSecret(c, delObj); dErr != nil { return nil, fmt.Errorf("failed cleaning up the secret because failed creating monitoring instance") } return nil, fmt.Errorf("failed creating monitoring instance") diff --git a/internal/server/handlers/k8s/namespaces.go b/internal/server/handlers/k8s/namespaces.go index 10dee10b6..0d6a8a13c 100644 --- a/internal/server/handlers/k8s/namespaces.go +++ b/internal/server/handlers/k8s/namespaces.go @@ -6,9 +6,14 @@ import ( ) func (h *k8sHandler) ListNamespaces(ctx context.Context) ([]string, error) { - result, err := h.kubeClient.GetDBNamespaces(ctx) + nsList, err := h.kubeConnector.GetDBNamespaces(ctx) if err != nil { return nil, fmt.Errorf("failed to GetDBNamespaces: %w", err) } + + result := make([]string, 0, len(nsList.Items)) + for _, ns := range nsList.Items { + result = append(result, ns.GetName()) + } return result, nil } diff --git a/internal/server/handlers/rbac/backup_storage_test.go b/internal/server/handlers/rbac/backup_storage_test.go index fc477ca99..e89897cf8 100644 --- a/internal/server/handlers/rbac/backup_storage_test.go +++ b/internal/server/handlers/rbac/backup_storage_test.go @@ -13,6 +13,7 @@ import ( "go.uber.org/zap" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -749,10 +750,11 @@ func TestRBAC_BackupStorage(t *testing.T) { }) } -func newConfigMapMock(policy string) *kubernetes.MockKubernetesConnector { - k8sMock := &kubernetes.MockKubernetesConnector{} - k8sMock.On("GetConfigMap", mock.Anything, mock.Anything, mock.Anything).Return(newConfigMapPolicy(policy), nil) - return k8sMock +func newConfigMapMock(policy string) kubernetes.KubernetesConnector { + mockClient := fakeclient.NewClientBuilder(). + WithScheme(kubernetes.CreateScheme()). + WithObjects(newConfigMapPolicy(policy)) + return kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) } func newPolicy(lines ...string) string { @@ -761,6 +763,10 @@ func newPolicy(lines ...string) string { func newConfigMapPolicy(policy string) *corev1.ConfigMap { return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: common.SystemNamespace, + Name: common.EverestRBACConfigMapName, + }, Data: map[string]string{ "enabled": "true", "policy.csv": policy, diff --git a/internal/server/handlers/rbac/handler.go b/internal/server/handlers/rbac/handler.go index 37e0172eb..782e2c3dd 100644 --- a/internal/server/handlers/rbac/handler.go +++ b/internal/server/handlers/rbac/handler.go @@ -30,9 +30,9 @@ type rbacHandler struct { func New( ctx context.Context, log *zap.SugaredLogger, - kubeClient *kubernetes.Kubernetes, + kubeConnector kubernetes.KubernetesConnector, ) (handlers.Handler, error) { - enf, err := rbac.NewEnforcerWithRefresh(ctx, kubeClient, log) + enf, err := rbac.NewEnforcerWithRefresh(ctx, kubeConnector, log) if err != nil { return nil, fmt.Errorf("failed to create enforcer: %w", err) } diff --git a/internal/server/handlers/validation/backup_storage.go b/internal/server/handlers/validation/backup_storage.go index 4c3638ace..ff079273d 100644 --- a/internal/server/handlers/validation/backup_storage.go +++ b/internal/server/handlers/validation/backup_storage.go @@ -19,6 +19,8 @@ import ( "github.com/aws/aws-sdk-go/service/s3" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -39,7 +41,7 @@ func (h *validateHandler) GetBackupStorage(ctx context.Context, namespace, name } func (h *validateHandler) CreateBackupStorage(ctx context.Context, namespace string, req *api.CreateBackupStorageParams) (*everestv1alpha1.BackupStorage, error) { - existing, err := h.kubeClient.ListBackupStorages(ctx, namespace) + existing, err := h.kubeConnector.ListBackupStorages(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return nil, fmt.Errorf("failed to ListBackupStorages: %w", err) } @@ -50,11 +52,11 @@ func (h *validateHandler) CreateBackupStorage(ctx context.Context, namespace str } func (h *validateHandler) UpdateBackupStorage(ctx context.Context, namespace, name string, req *api.UpdateBackupStorageParams) (*everestv1alpha1.BackupStorage, error) { - bs, err := h.kubeClient.GetBackupStorage(ctx, namespace, name) + bs, err := h.kubeConnector.GetBackupStorage(ctx, types.NamespacedName{Namespace: namespace, Name: name}) if err != nil { return nil, fmt.Errorf("failed to GetBackupStorage: %w", err) } - secret, err := h.kubeClient.GetSecret(ctx, namespace, bs.Spec.CredentialsSecretName) + secret, err := h.kubeConnector.GetSecret(ctx, types.NamespacedName{Namespace: namespace, Name: bs.Spec.CredentialsSecretName}) if err != nil { return nil, fmt.Errorf("failed to GetSecret: %w", err) } @@ -329,7 +331,7 @@ func (h *validateHandler) validateUpdateBackupStorageRequest( existing *everestv1alpha1.BackupStorage, secret *corev1.Secret, ) error { - used, err := h.kubeClient.IsBackupStorageUsed(ctx, existing.GetNamespace(), existing.GetName()) + used, err := h.kubeConnector.IsBackupStorageUsed(ctx, types.NamespacedName{Namespace: existing.GetNamespace(), Name: existing.GetName()}) if err != nil { return err } @@ -337,7 +339,7 @@ func (h *validateHandler) validateUpdateBackupStorageRequest( return errEditBackupStorageInUse } - existingStorages, err := h.kubeClient.ListBackupStorages(ctx, existing.GetNamespace()) + existingStorages, err := h.kubeConnector.ListBackupStorages(ctx, ctrlclient.InNamespace(existing.GetNamespace())) if err != nil { return err } diff --git a/internal/server/handlers/validation/database_cluster.go b/internal/server/handlers/validation/database_cluster.go index b960c70a5..ef3843deb 100644 --- a/internal/server/handlers/validation/database_cluster.go +++ b/internal/server/handlers/validation/database_cluster.go @@ -11,6 +11,8 @@ import ( "golang.org/x/mod/semver" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -32,7 +34,7 @@ func (h *validateHandler) CreateDatabaseCluster(ctx context.Context, db *everest return nil, errors.Join(ErrInvalidRequest, err) } - if currentDB, err := h.kubeClient.GetDatabaseCluster(ctx, db.GetNamespace(), db.GetName()); err != nil { + if currentDB, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: db.GetNamespace(), Name: db.GetName()}); err != nil { if !k8serrors.IsNotFound(err) { return nil, fmt.Errorf("failed to check if DB cluster with name already exists in namespace: %w", err) } @@ -56,7 +58,7 @@ func (h *validateHandler) UpdateDatabaseCluster(ctx context.Context, db *everest return nil, errors.Join(ErrInvalidRequest, err) } - current, err := h.kubeClient.GetDatabaseCluster(ctx, db.GetNamespace(), db.GetName()) + current, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: db.GetNamespace(), Name: db.GetName()}) if err != nil { return nil, fmt.Errorf("failed to GetDatabaseCluster: %w", err) } @@ -99,7 +101,7 @@ func (h *validateHandler) validateDatabaseClusterCR( if !ok { return errors.New("unsupported database engine") } - engine, err := h.kubeClient.GetDatabaseEngine(ctx, namespace, engineName) + engine, err := h.kubeConnector.GetDatabaseEngine(ctx, types.NamespacedName{Namespace: namespace, Name: engineName}) if err != nil { return err } @@ -129,7 +131,7 @@ func (h *validateHandler) validateDatabaseClusterCR( if err = h.validatePGSchedulesRestrictions(ctx, databaseCluster); err != nil { return err } - if err = validatePGReposForAPIDB(ctx, databaseCluster, h.kubeClient.ListDatabaseClusterBackups); err != nil { + if err = validatePGReposForAPIDB(ctx, databaseCluster, h.kubeConnector.ListDatabaseClusterBackups); err != nil { return err } } @@ -337,7 +339,12 @@ func (h *validateHandler) validateBackupStoragesFor( if databaseCluster.Spec.Backup.PITR.BackupStorageName == nil || *databaseCluster.Spec.Backup.PITR.BackupStorageName == "" { return errPitrNoBackupStorageName } - storage, err := h.kubeClient.GetBackupStorage(ctx, namespace, *databaseCluster.Spec.Backup.PITR.BackupStorageName) + storage, err := h.kubeConnector.GetBackupStorage(ctx, + types.NamespacedName{ + Namespace: namespace, + Name: *databaseCluster.Spec.Backup.PITR.BackupStorageName, + }, + ) if err != nil { return err } @@ -388,7 +395,7 @@ func validateDataSource(dataSource *everestv1alpha1.DataSource) error { func (h *validateHandler) validatePGSchedulesRestrictions(ctx context.Context, newDbc *everestv1alpha1.DatabaseCluster) error { dbcName := newDbc.GetName() dbcNamespace := newDbc.GetNamespace() - existingDbc, err := h.kubeClient.GetDatabaseCluster(ctx, dbcNamespace, dbcName) + existingDbc, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: dbcNamespace, Name: dbcName}) if err != nil { // if there was no such cluster before (creating cluster) - check only the duplicates for storages if k8serrors.IsNotFound(err) { @@ -437,7 +444,7 @@ func checkSchedulesChanges(oldDbc, newDbc *everestv1alpha1.DatabaseCluster) erro func validatePGReposForAPIDB( ctx context.Context, dbc *everestv1alpha1.DatabaseCluster, - getBackupsFunc func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error), + getBackupsFunc func(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterBackupList, error), ) error { bs := make(map[string]bool) for _, shed := range dbc.Spec.Backup.Schedules { @@ -447,9 +454,7 @@ func validatePGReposForAPIDB( dbcName := dbc.GetName() dbcNamespace := dbc.GetNamespace() - backups, err := getBackupsFunc(ctx, dbcNamespace, metav1.ListOptions{ - LabelSelector: fmt.Sprintf("clusterName=%s", dbcName), - }) + backups, err := getBackupsFunc(ctx, ctrlclient.InNamespace(dbcNamespace), ctrlclient.MatchingLabels{common.DatabaseClusterNameLabel: dbcName}) if err != nil { return err } diff --git a/internal/server/handlers/validation/database_cluster_backup.go b/internal/server/handlers/validation/database_cluster_backup.go index 88d6925f9..7d45c4906 100644 --- a/internal/server/handlers/validation/database_cluster_backup.go +++ b/internal/server/handlers/validation/database_cluster_backup.go @@ -6,7 +6,8 @@ import ( "fmt" k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -42,7 +43,7 @@ func (h *validateHandler) validateDatabaseClusterBackup(ctx context.Context, bac return errors.New(".spec.dbClusterName cannot be empty") } namespace := backup.GetNamespace() - db, err := h.kubeClient.GetDatabaseCluster(ctx, namespace, backup.Spec.DBClusterName) + db, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: namespace, Name: backup.Spec.DBClusterName}) if err != nil { if k8serrors.IsNotFound(err) { return fmt.Errorf("database cluster %s does not exist", backup.Spec.DBClusterName) @@ -72,8 +73,8 @@ func (h *validateHandler) validatePGReposForBackup( } // put the backup that being validated to the list of all backups to calculate if the limitations are respected - getBackupsFunc := func(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - list, err := h.kubeClient.ListDatabaseClusterBackups(ctx, namespace, options) + getBackupsFunc := func(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterBackupList, error) { + list, err := h.kubeConnector.ListDatabaseClusterBackups(ctx, opts...) if err != nil { return nil, err } diff --git a/internal/server/handlers/validation/database_cluster_restore.go b/internal/server/handlers/validation/database_cluster_restore.go index 550cec851..5b067b342 100644 --- a/internal/server/handlers/validation/database_cluster_restore.go +++ b/internal/server/handlers/validation/database_cluster_restore.go @@ -6,6 +6,7 @@ import ( "fmt" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) @@ -53,21 +54,21 @@ func (h *validateHandler) validateDatabaseClusterRestore( return errors.New(".spec.dbClusterName cannot be empty") } namespace := restore.GetNamespace() - _, err := h.kubeClient.GetDatabaseCluster(ctx, namespace, restore.Spec.DBClusterName) + _, err := h.kubeConnector.GetDatabaseCluster(ctx, types.NamespacedName{Namespace: namespace, Name: restore.Spec.DBClusterName}) if err != nil { if k8serrors.IsNotFound(err) { return fmt.Errorf("database cluster %s does not exist", restore.Spec.DBClusterName) } return err } - b, err := h.kubeClient.GetDatabaseClusterBackup(ctx, namespace, restore.Spec.DataSource.DBClusterBackupName) + b, err := h.kubeConnector.GetDatabaseClusterBackup(ctx, types.NamespacedName{Namespace: namespace, Name: restore.Spec.DataSource.DBClusterBackupName}) if err != nil { if k8serrors.IsNotFound(err) { return fmt.Errorf("backup %s does not exist", restore.Spec.DataSource.DBClusterBackupName) } return err } - _, err = h.kubeClient.GetBackupStorage(ctx, namespace, b.Spec.BackupStorageName) + _, err = h.kubeConnector.GetBackupStorage(ctx, types.NamespacedName{Namespace: namespace, Name: b.Spec.BackupStorageName}) if err != nil { if k8serrors.IsNotFound(err) { return fmt.Errorf("backup storage %s does not exist", diff --git a/internal/server/handlers/validation/database_cluster_test.go b/internal/server/handlers/validation/database_cluster_test.go index b7010699a..eea86afa1 100644 --- a/internal/server/handlers/validation/database_cluster_test.go +++ b/internal/server/handlers/validation/database_cluster_test.go @@ -7,14 +7,16 @@ import ( "github.com/AlekSi/pointer" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" + "github.com/percona/everest/pkg/common" "github.com/percona/everest/pkg/kubernetes" - "github.com/percona/everest/pkg/kubernetes/client" ) func TestValidateCreateDatabaseClusterRequest(t *testing.T) { @@ -368,16 +370,18 @@ func TestValidateBackupSpec(t *testing.T) { func TestValidateBackupStoragesFor(t *testing.T) { t.Parallel() cases := []struct { - name string - namespace string - cluster everestv1alpha1.DatabaseCluster - storage everestv1alpha1.BackupStorage - err error + name string + cluster everestv1alpha1.DatabaseCluster + storage everestv1alpha1.BackupStorage + err error }{ { - name: "errPSMDBMultipleStorages", - namespace: "everest", + name: "errPSMDBMultipleStorages", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-1", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ Schedules: []everestv1alpha1.BackupSchedule{ @@ -399,6 +403,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage1", + Namespace: "ns-1", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeS3, }, @@ -406,9 +414,12 @@ func TestValidateBackupStoragesFor(t *testing.T) { err: errPSMDBMultipleStorages, }, { - name: "errPSMDBViolateActiveStorage", - namespace: "everest", + name: "errPSMDBViolateActiveStorage", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-2", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ Schedules: []everestv1alpha1.BackupSchedule{ @@ -428,6 +439,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage2", + Namespace: "ns-2", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeS3, }, @@ -435,9 +450,12 @@ func TestValidateBackupStoragesFor(t *testing.T) { err: errPSMDBViolateActiveStorage, }, { - name: "no errPSMDBViolateActiveStorage", - namespace: "everest", + name: "no errPSMDBViolateActiveStorage", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "psmdb-try", + Namespace: "ns-3", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ Schedules: []everestv1alpha1.BackupSchedule{ @@ -457,6 +475,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage1", + Namespace: "ns-3", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeS3, }, @@ -464,9 +486,12 @@ func TestValidateBackupStoragesFor(t *testing.T) { err: nil, }, { - name: "errPXCPitrS3Only", - namespace: "everest", + name: "errPXCPitrS3Only", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pxc-try", + Namespace: "ns-4", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ PITR: everestv1alpha1.PITRSpec{ @@ -490,6 +515,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage", + Namespace: "ns-4", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeAzure, }, @@ -497,9 +526,12 @@ func TestValidateBackupStoragesFor(t *testing.T) { err: errPXCPitrS3Only, }, { - name: "errPitrNoBackupStorageName", - namespace: "everest", + name: "errPitrNoBackupStorageName", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pxc-try", + Namespace: "ns-5", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ PITR: everestv1alpha1.PITRSpec{ @@ -522,6 +554,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage1", + Namespace: "ns-5", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeS3, }, @@ -529,9 +565,12 @@ func TestValidateBackupStoragesFor(t *testing.T) { err: errPitrNoBackupStorageName, }, { - name: "valid", - namespace: "everest", + name: "valid", cluster: everestv1alpha1.DatabaseCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pxc-try", + Namespace: "ns-6", + }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ PITR: everestv1alpha1.PITRSpec{ @@ -555,6 +594,10 @@ func TestValidateBackupStoragesFor(t *testing.T) { }, }, storage: everestv1alpha1.BackupStorage{ + ObjectMeta: metav1.ObjectMeta{ + Name: "storage", + Namespace: "ns-6", + }, Spec: everestv1alpha1.BackupStorageSpec{ Type: everestv1alpha1.BackupStorageTypeS3, }, @@ -566,19 +609,15 @@ func TestValidateBackupStoragesFor(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - k := &kubernetes.Kubernetes{} - mockConnector := &client.MockKubeClientConnector{} - mockConnector.On("GetBackupStorage", mock.Anything, mock.Anything, mock.Anything). - Return(&tc.storage, nil) - k.WithClient(mockConnector) - + mockClient := fakeclient.NewClientBuilder().WithScheme(kubernetes.CreateScheme()).WithObjects(&tc.cluster, &tc.storage) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) h := validateHandler{ - kubeClient: k, + kubeConnector: k, } err := h.validateBackupStoragesFor( context.Background(), - tc.namespace, + tc.cluster.GetNamespace(), &tc.cluster, ) @@ -944,32 +983,28 @@ func TestValidateDataSource(t *testing.T) { func TestValidatePGReposForAPIDB(t *testing.T) { t.Parallel() cases := []struct { - name string - cluster everestv1alpha1.DatabaseCluster - getBackupsFunc func(ctx context.Context, ns string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) - err error + name string + cluster everestv1alpha1.DatabaseCluster + dbClusterBackups []ctrlclient.Object + err error }{ { name: "ok: no schedules no backups", cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-1", }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{}, - }, nil - }, - err: nil, + dbClusterBackups: []ctrlclient.Object{}, + err: nil, }, { name: "ok: 2 schedules 2 backups with the same storages", cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-2", }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ @@ -980,13 +1015,21 @@ func TestValidatePGReposForAPIDB(t *testing.T) { }, }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1", + Namespace: "ns-2", + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2", + Namespace: "ns-2", }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, }, err: nil, }, @@ -995,7 +1038,7 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-3", }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ @@ -1007,12 +1050,15 @@ func TestValidatePGReposForAPIDB(t *testing.T) { }, }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-4", + Namespace: "ns-3", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}, + }, }, err: errTooManyPGStorages, }, @@ -1021,7 +1067,7 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-4", }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ @@ -1033,29 +1079,42 @@ func TestValidatePGReposForAPIDB(t *testing.T) { }, }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{}, - }, nil - }, - err: nil, + dbClusterBackups: []ctrlclient.Object{}, + err: nil, }, { name: "ok: 3 backups with different storages", cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-5", }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1", + Namespace: "ns-5", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2", + Namespace: "ns-5", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-3", + Namespace: "ns-5", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}, + }, }, err: nil, }, @@ -1064,19 +1123,50 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-6", }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1", + Namespace: "ns-6", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2", + Namespace: "ns-6", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-3", + Namespace: "ns-6", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1-1", + Namespace: "ns-6", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2-1", + Namespace: "ns-6", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, }, err: nil, }, @@ -1085,18 +1175,42 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-7", }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1", + Namespace: "ns-7", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2", + Namespace: "ns-7", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-3", + Namespace: "ns-7", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-4", + Namespace: "ns-7", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}, + }, }, err: errTooManyPGStorages, }, @@ -1105,18 +1219,42 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-8", }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1-1", + Namespace: "ns-8", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-1-2", + Namespace: "ns-8", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs1"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2-1", + Namespace: "ns-8", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-2-2", + Namespace: "ns-8", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs2"}, + }, }, err: nil, }, @@ -1125,7 +1263,7 @@ func TestValidatePGReposForAPIDB(t *testing.T) { cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-9", }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ @@ -1138,19 +1276,15 @@ func TestValidatePGReposForAPIDB(t *testing.T) { }, }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{}, - }, nil - }, - err: errTooManyPGStorages, + dbClusterBackups: []ctrlclient.Object{}, + err: errTooManyPGStorages, }, { name: "error: 2 schedules 2 backups with different storages", cluster: everestv1alpha1.DatabaseCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "some", - Namespace: "ns", + Namespace: "ns-10", }, Spec: everestv1alpha1.DatabaseClusterSpec{ Backup: everestv1alpha1.Backup{ @@ -1161,13 +1295,23 @@ func TestValidatePGReposForAPIDB(t *testing.T) { }, }, }, - getBackupsFunc: func(context.Context, string, metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return &everestv1alpha1.DatabaseClusterBackupList{ - Items: []everestv1alpha1.DatabaseClusterBackup{ - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}}, - {Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}}, + dbClusterBackups: []ctrlclient.Object{ + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-3", + Namespace: "ns-10", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, }, - }, nil + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs3"}, + }, + &everestv1alpha1.DatabaseClusterBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bc-bs-4", + Namespace: "ns-10", + Labels: map[string]string{common.DatabaseClusterNameLabel: "some"}, + }, + Spec: everestv1alpha1.DatabaseClusterBackupSpec{BackupStorageName: "bs4"}, + }, }, err: errTooManyPGStorages, }, @@ -1175,7 +1319,12 @@ func TestValidatePGReposForAPIDB(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - assert.Equal(t, tc.err, validatePGReposForAPIDB(context.Background(), &tc.cluster, tc.getBackupsFunc)) + mockClient := fakeclient.NewClientBuilder(). + WithScheme(kubernetes.CreateScheme()). + WithObjects(&tc.cluster). + WithObjects(tc.dbClusterBackups...) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) + assert.Equal(t, tc.err, validatePGReposForAPIDB(context.Background(), &tc.cluster, k.ListDatabaseClusterBackups)) }) } } diff --git a/internal/server/handlers/validation/handler.go b/internal/server/handlers/validation/handler.go index 5872ac782..1a9b31ff9 100644 --- a/internal/server/handlers/validation/handler.go +++ b/internal/server/handlers/validation/handler.go @@ -9,9 +9,9 @@ import ( ) type validateHandler struct { - log *zap.SugaredLogger - next handlers.Handler - kubeClient *kubernetes.Kubernetes + log *zap.SugaredLogger + next handlers.Handler + kubeConnector kubernetes.KubernetesConnector } // New returns a new RBAC handler. @@ -19,12 +19,12 @@ type validateHandler struct { //nolint:ireturn func New( log *zap.SugaredLogger, - kubeClient *kubernetes.Kubernetes, + kubeConnector kubernetes.KubernetesConnector, ) handlers.Handler { l := log.With("handler", "validator") return &validateHandler{ - log: l, - kubeClient: kubeClient, + log: l, + kubeConnector: kubeConnector, } } diff --git a/internal/server/middlewares.go b/internal/server/middlewares.go index ac5191a3f..1b21b43bf 100644 --- a/internal/server/middlewares.go +++ b/internal/server/middlewares.go @@ -24,6 +24,7 @@ import ( "github.com/labstack/echo/v4" "github.com/unrolled/secure" "github.com/unrolled/secure/cspbuilder" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/api" @@ -65,7 +66,7 @@ func (e *EverestServer) shouldAllowRequestDuringEngineUpgrade(c echo.Context) (b } // Check if there's an engine in this namespace that is upgrading the operator? - engines, err := e.kubeClient.ListDatabaseEngines(c.Request().Context(), namespace) + engines, err := e.kubeConnector.ListDatabaseEngines(c.Request().Context(), ctrlclient.InNamespace(namespace)) if err != nil { e.l.Error(err) return false, err diff --git a/internal/server/middlewares_test.go b/internal/server/middlewares_test.go index 93b5828d6..caebb01cf 100644 --- a/internal/server/middlewares_test.go +++ b/internal/server/middlewares_test.go @@ -16,13 +16,14 @@ import ( "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/pkg/kubernetes" - "github.com/percona/everest/pkg/kubernetes/client" ) func TestShouldAllowRequestDuringEngineUpgrade(t *testing.T) { @@ -30,7 +31,7 @@ func TestShouldAllowRequestDuringEngineUpgrade(t *testing.T) { t.Parallel() testCases := []struct { description string - mockFn func(m *client.MockKubeClientConnector) + objs []ctrlclient.Object ctxFn func() echo.Context allow bool }{ @@ -84,22 +85,13 @@ func TestShouldAllowRequestDuringEngineUpgrade(t *testing.T) { ctx.SetParamValues("default") return ctx }, - mockFn: func(m *client.MockKubeClientConnector) { - m.On("ListDatabaseEngines", - mock.Anything, - "default", - ). - Return(&everestv1alpha1.DatabaseEngineList{ - Items: []everestv1alpha1.DatabaseEngine{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-engine", - Namespace: "default", - }, - }, - }, - }, nil, - ) + objs: []ctrlclient.Object{ + &everestv1alpha1.DatabaseEngine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-engine", + Namespace: "default", + }, + }, }, allow: true, }, @@ -117,24 +109,16 @@ func TestShouldAllowRequestDuringEngineUpgrade(t *testing.T) { ctx.SetParamValues("default") return ctx }, - mockFn: func(m *client.MockKubeClientConnector) { - m.On("ListDatabaseEngines", - mock.Anything, "default", - ). - Return(&everestv1alpha1.DatabaseEngineList{ - Items: []everestv1alpha1.DatabaseEngine{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-engine", - Namespace: "default", - Annotations: map[string]string{ - everestv1alpha1.DatabaseOperatorUpgradeLockAnnotation: lockedAt, - }, - }, - }, + objs: []ctrlclient.Object{ + &everestv1alpha1.DatabaseEngine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-engine", + Namespace: "default", + Annotations: map[string]string{ + everestv1alpha1.DatabaseOperatorUpgradeLockAnnotation: lockedAt, }, - }, nil, - ) + }, + }, }, allow: false, }, @@ -143,16 +127,9 @@ func TestShouldAllowRequestDuringEngineUpgrade(t *testing.T) { for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { t.Parallel() - mockConnector := &client.MockKubeClientConnector{} - kubeClient := &kubernetes.Kubernetes{} - kubeClient = kubeClient.WithClient(mockConnector) - - e := EverestServer{kubeClient: kubeClient} - - if tc.mockFn != nil { - tc.mockFn(mockConnector) - } - + mockClient := fakeclient.NewClientBuilder().WithScheme(kubernetes.CreateScheme()).WithObjects(tc.objs...) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) + e := EverestServer{kubeConnector: k} ctx := tc.ctxFn() allow, err := e.shouldAllowRequestDuringEngineUpgrade(ctx) diff --git a/internal/server/telemetry.go b/internal/server/telemetry.go index 530a50026..a57de732a 100644 --- a/internal/server/telemetry.go +++ b/internal/server/telemetry.go @@ -11,6 +11,7 @@ import ( "time" "github.com/google/uuid" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/cmd/config" "github.com/percona/everest/pkg/version" @@ -104,13 +105,13 @@ func (e *EverestServer) collectMetrics(ctx context.Context, config config.Everes if config.DisableTelemetry { return nil } - everestID, err := e.kubeClient.GetEverestID(ctx) + everestID, err := e.kubeConnector.GetEverestID(ctx) if err != nil { e.l.Error(errors.Join(err, errors.New("failed to get Everest settings"))) return err } - namespaces, err := e.kubeClient.GetDBNamespaces(ctx) + namespaces, err := e.kubeConnector.GetDBNamespaces(ctx) if err != nil { e.l.Error(errors.Join(err, errors.New("failed to get watched namespaces"))) return err @@ -124,8 +125,8 @@ func (e *EverestServer) collectMetrics(ctx context.Context, config config.Everes Value: version.Version, }) - for _, ns := range namespaces { - clusters, err := e.kubeClient.ListDatabaseClusters(ctx, ns) + for _, ns := range namespaces.Items { + clusters, err := e.kubeConnector.ListDatabaseClusters(ctx, ctrlclient.InNamespace(ns.GetName())) if err != nil { e.l.Error(errors.Join(err, errors.New("failed to list database clusters"))) return err diff --git a/pkg/accounts/cli/accounts.go b/pkg/accounts/cli/accounts.go index 816161bba..bb58aa8f6 100644 --- a/pkg/accounts/cli/accounts.go +++ b/pkg/accounts/cli/accounts.go @@ -47,7 +47,7 @@ type ( accountManager accounts.Interface l *zap.SugaredLogger config Config - kubeClient *kubernetes.Kubernetes + kubeClient kubernetes.KubernetesConnector } ) @@ -61,7 +61,7 @@ func NewAccounts(c Config, l *zap.SugaredLogger) (*Accounts, error) { cli.l = zap.NewNop().Sugar() } - k, err := cliutils.NewKubeclient(cli.l, c.KubeconfigPath) + k, err := cliutils.NewKubeConnector(cli.l, c.KubeconfigPath) if err != nil { return nil, err } diff --git a/pkg/cli/install/install.go b/pkg/cli/install/install.go index 5aedf14a6..280f7daa7 100644 --- a/pkg/cli/install/install.go +++ b/pkg/cli/install/install.go @@ -29,6 +29,7 @@ import ( goversion "github.com/hashicorp/go-version" "go.uber.org/zap" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/cli/helm" @@ -82,7 +83,7 @@ type ( Installer struct { l *zap.SugaredLogger cfg InstallConfig - kubeClient *kubernetes.Kubernetes + kubeClient kubernetes.KubernetesConnector versionService versionservice.Interface // these are set only when Run is called. installVersion string @@ -107,7 +108,7 @@ func (cfg *InstallConfig) detectKubernetesEnv(ctx context.Context, l *zap.Sugare return nil } - kubeClient, err := cliutils.NewKubeclient(l, cfg.KubeconfigPath) + kubeClient, err := cliutils.NewKubeConnector(l, cfg.KubeconfigPath) if err != nil { return fmt.Errorf("failed to create kubernetes client: %w", err) } @@ -145,7 +146,7 @@ func NewInstall(c InstallConfig, l *zap.SugaredLogger) (*Installer, error) { cli.cfg = c var err error - cli.kubeClient, err = cliutils.NewKubeclient(cli.l, c.KubeconfigPath) + cli.kubeClient, err = cliutils.NewKubeConnector(cli.l, c.KubeconfigPath) if err != nil { return nil, err } @@ -365,7 +366,7 @@ func (o *Installer) latestVersion(meta *versionpb.MetadataResponse) (*goversion. } func (o *Installer) namespaceExists(ctx context.Context, namespace string) (bool, error) { - _, err := o.kubeClient.GetNamespace(ctx, namespace) + _, err := o.kubeClient.GetNamespace(ctx, types.NamespacedName{Name: namespace}) if err != nil { if k8serrors.IsNotFound(err) { return false, nil @@ -377,7 +378,7 @@ func (o *Installer) namespaceExists(ctx context.Context, namespace string) (bool // CheckEverestAlreadyinstalled checks if Everest is already installed. func CheckEverestAlreadyinstalled(ctx context.Context, l *zap.SugaredLogger, kubeConfig string) error { - kubeClient, err := cliutils.NewKubeclient(l, kubeConfig) + kubeClient, err := cliutils.NewKubeConnector(l, kubeConfig) if err != nil { return fmt.Errorf("failed to create kubernetes client: %w", err) } diff --git a/pkg/cli/install/steps.go b/pkg/cli/install/steps.go index 2719b4ded..b6f9c02d4 100644 --- a/pkg/cli/install/steps.go +++ b/pkg/cli/install/steps.go @@ -20,7 +20,9 @@ import ( "fmt" "github.com/AlekSi/pointer" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/cli/steps" "github.com/percona/everest/pkg/common" @@ -58,7 +60,7 @@ func (o *Installer) newStepEnsureEverestOLM() steps.Step { return steps.Step{ Desc: "Ensuring OLM components are ready", F: func(ctx context.Context) error { - depls, err := o.kubeClient.ListDeployments(ctx, kubernetes.OLMNamespace) + depls, err := o.kubeClient.ListDeployments(ctx, client.InNamespace(kubernetes.OLMNamespace)) if err != nil { return err } @@ -102,7 +104,7 @@ func (o *Installer) newStepEnsureCatalogSource() steps.Step { return fmt.Errorf("could not get Everest CatalogSource namespace: %w", err) } return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) { - cs, err := o.kubeClient.GetCatalogSource(ctx, common.PerconaEverestCatalogName, catalogNs) + cs, err := o.kubeClient.GetCatalogSource(ctx, types.NamespacedName{Namespace: catalogNs, Name: common.PerconaEverestCatalogName}) if err != nil { return false, fmt.Errorf("cannot get CatalogSource: %w", err) } @@ -114,7 +116,7 @@ func (o *Installer) newStepEnsureCatalogSource() steps.Step { func (o *Installer) waitForDeployment(ctx context.Context, name, namespace string) error { o.l.Infof("Waiting for Deployment '%s' in namespace '%s'", name, namespace) - if err := o.kubeClient.WaitForRollout(ctx, name, namespace); err != nil { + if err := o.kubeClient.WaitForRollout(ctx, types.NamespacedName{Namespace: namespace, Name: name}); err != nil { return err } o.l.Infof("Deployment '%s' in namespace '%s' is ready", name, namespace) diff --git a/pkg/cli/namespaces/add.go b/pkg/cli/namespaces/add.go index 447c1f5c0..a8018d627 100644 --- a/pkg/cli/namespaces/add.go +++ b/pkg/cli/namespaces/add.go @@ -24,6 +24,7 @@ import ( "go.uber.org/zap" "helm.sh/helm/v3/pkg/cli/values" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/cli/helm" helmutils "github.com/percona/everest/pkg/cli/helm/utils" @@ -85,7 +86,7 @@ type ( NamespaceAdder struct { l *zap.SugaredLogger cfg NamespaceAddConfig - kubeClient *kubernetes.Kubernetes + kubeClient kubernetes.KubernetesConnector } ) @@ -185,7 +186,7 @@ func (cfg *NamespaceAddConfig) ValidateNamespaces(ctx context.Context, nsList [] return err } - k, err := cliutils.NewKubeclient(zap.NewNop().Sugar(), cfg.KubeconfigPath) + k, err := cliutils.NewKubeConnector(zap.NewNop().Sugar(), cfg.KubeconfigPath) if err != nil { return err } @@ -238,7 +239,7 @@ func (cfg *NamespaceAddConfig) detectKubernetesEnv(ctx context.Context, l *zap.S return nil } - client, err := cliutils.NewKubeclient(l, cfg.KubeconfigPath) + client, err := cliutils.NewKubeConnector(l, cfg.KubeconfigPath) if err != nil { return fmt.Errorf("failed to create kubernetes client: %w", err) } @@ -280,7 +281,7 @@ func NewNamespaceAdd(c NamespaceAddConfig, l *zap.SugaredLogger) (*NamespaceAdde n.l = zap.NewNop().Sugar() } - k, err := cliutils.NewKubeclient(n.l, c.KubeconfigPath) + k, err := cliutils.NewKubeConnector(n.l, c.KubeconfigPath) if err != nil { return nil, err } @@ -403,7 +404,7 @@ func (n *NamespaceAdder) provisionDBNamespace( } func (n *NamespaceAdder) validateNamespaceUpdate(ctx context.Context, namespace string) error { - subscriptions, err := n.kubeClient.ListSubscriptions(ctx, namespace) + subscriptions, err := n.kubeClient.ListSubscriptions(ctx, client.InNamespace(namespace)) if err != nil { return fmt.Errorf("cannot list subscriptions: %w", err) } diff --git a/pkg/cli/namespaces/list.go b/pkg/cli/namespaces/list.go index a5d768d23..05992d86a 100644 --- a/pkg/cli/namespaces/list.go +++ b/pkg/cli/namespaces/list.go @@ -20,12 +20,16 @@ import ( "context" "fmt" "slices" + "strings" + goversion "github.com/hashicorp/go-version" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + everestOperator "github.com/percona/everest-operator/api/v1alpha1" cliutils "github.com/percona/everest/pkg/cli/utils" "github.com/percona/everest/pkg/common" "github.com/percona/everest/pkg/kubernetes" @@ -77,7 +81,7 @@ type ( // NamespaceLister is the CLI operation to list namespaces. NamespaceLister struct { cfg NamespaceListConfig - kubeClient *kubernetes.Kubernetes + kubeClient kubernetes.KubernetesConnector l *zap.SugaredLogger } ) @@ -86,13 +90,13 @@ type ( func NewNamespaceLister(c NamespaceListConfig, l *zap.SugaredLogger) (*NamespaceLister, error) { n := &NamespaceLister{ cfg: c, - l: l.With("component", "namespace-remover"), + l: l.With("component", "namespace-lister"), } if c.Pretty { n.l = zap.NewNop().Sugar() } - k, err := cliutils.NewKubeclient(n.l, n.cfg.KubeconfigPath) + k, err := cliutils.NewKubeConnector(n.l, n.cfg.KubeconfigPath) if err != nil { return nil, err } @@ -110,17 +114,14 @@ func (nsL *NamespaceLister) Run(ctx context.Context) ([]NamespaceInfo, error) { } var nsList *corev1.NamespaceList - var labelSelector string + opts := []client.ListOption{client.MatchingFields{"status.phase": string(corev1.NamespaceActive)}} if !nsL.cfg.ListAllNamespaces { // show only namespaces already managed by Everest. - labelSelector = fmt.Sprintf("%s=%s", common.KubernetesManagedByLabel, common.Everest) + opts = append(opts, client.MatchingLabels{common.KubernetesManagedByLabel: common.Everest}) } - if nsList, err = nsL.kubeClient.ListNamespaces(ctx, metav1.ListOptions{ - FieldSelector: fmt.Sprintf("status.phase=%s", corev1.NamespaceActive), - LabelSelector: labelSelector, - }); err != nil { + if nsList, err = nsL.kubeClient.ListNamespaces(ctx, opts...); err != nil { return nil, err } @@ -131,10 +132,11 @@ func (nsL *NamespaceLister) Run(ctx context.Context) ([]NamespaceInfo, error) { var toReturn []NamespaceInfo for _, ns := range nsList.Items { - nsInfo := NamespaceInfo{Name: ns.Name} + nsInfo := NamespaceInfo{Name: ns.GetName()} if nsInfo.InstalledOperators, err = nsL.getNamespaceOperators(ctx, &ns); err != nil { return nil, fmt.Errorf("cannot get namespace subscriptions: %w", err) } + slices.Sort(nsInfo.InstalledOperators) toReturn = append(toReturn, nsInfo) } return toReturn, nil @@ -146,18 +148,42 @@ func (nsL *NamespaceLister) getNamespaceOperators(ctx context.Context, ns *v1.Na var toReturn []string if isManagedByEverest(ns) { // no need to look for installed operators from namespaces not managed by Everest. - dbEngines, err := nsL.kubeClient.ListDatabaseEngines(ctx, ns.Name) - if err != nil { - return []string{}, fmt.Errorf("cannot list installed DB Engines: %w", err) + subList, err := nsL.kubeClient.ListInstalledOperators(ctx, client.InNamespace(ns.GetName())) + if err != nil && client.IgnoreNotFound(err) != nil { + return []string{}, fmt.Errorf("cannot list installed operators in namespace='%s': %w", ns.GetName(), err) } - for _, dbE := range dbEngines.Items { - // need to skip DB Engines that are not installed in this particular namespaces. - if dbE.Status.State != "installed" { - continue + for _, sub := range subList.Items { + csv, csvErr := nsL.kubeClient.GetClusterServiceVersion(ctx, types.NamespacedName{ + Namespace: ns.GetName(), + Name: sub.Status.InstalledCSV, + }) + if csvErr != nil && client.IgnoreNotFound(csvErr) != nil { + return []string{}, fmt.Errorf("cannot list installed operators in namespace='%s': %w", ns.GetName(), csvErr) } - toReturn = append(toReturn, fmt.Sprintf("%s(v%s)", dbE.Spec.Type, dbE.Status.OperatorVersion)) + v, err := goversion.NewVersion(csv.Spec.Version.FinalizeVersion()) + if err != nil { + return []string{}, fmt.Errorf("cannot parse operator='%s' version in namespace='%s': %w", + sub.Spec.CatalogSourceNamespace, + ns.GetName(), + err, + ) + } + toReturn = append(toReturn, fmt.Sprintf("%s(v%s)", convertDbOperatorName(sub.GetName()), v.String())) } } return toReturn, nil } + +func convertDbOperatorName(name string) string { + switch strings.ToLower(name) { + case common.MongoDBOperatorName: + return string(everestOperator.DatabaseEnginePSMDB) + case common.MySQLOperatorName: + return string(everestOperator.DatabaseEnginePXC) + case common.PostgreSQLOperatorName: + return string(everestOperator.DatabaseEnginePostgresql) + default: + return name + } +} diff --git a/pkg/cli/namespaces/remove.go b/pkg/cli/namespaces/remove.go index 22b03c16f..09e4fa082 100644 --- a/pkg/cli/namespaces/remove.go +++ b/pkg/cli/namespaces/remove.go @@ -23,9 +23,12 @@ import ( "time" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/cli/helm" "github.com/percona/everest/pkg/cli/steps" @@ -57,9 +60,9 @@ type ( // NamespaceRemover is the CLI operation to remove namespaces. NamespaceRemover struct { - cfg NamespaceRemoveConfig - kubeClient *kubernetes.Kubernetes - l *zap.SugaredLogger + cfg NamespaceRemoveConfig + kubeConnector kubernetes.KubernetesConnector + l *zap.SugaredLogger } ) @@ -72,7 +75,7 @@ func (cfg *NamespaceRemoveConfig) ValidateNamespaces(ctx context.Context, nsList return err } - k, err := cliutils.NewKubeclient(zap.NewNop().Sugar(), cfg.KubeconfigPath) + k, err := cliutils.NewKubeConnector(zap.NewNop().Sugar(), cfg.KubeconfigPath) if err != nil { return err } @@ -81,16 +84,15 @@ func (cfg *NamespaceRemoveConfig) ValidateNamespaces(ctx context.Context, nsList if err := cfg.validateNamespaceOwnership(ctx, k, ns); err != nil { return err } - } - - // Check that there are no DB clusters left in namespaces. - dbsExist, err := k.DatabasesExist(ctx, nsList...) - if err != nil { - return errors.Join(err, errors.New("failed to check if databases exist")) - } - - if dbsExist && !cfg.Force { - return ErrNamespaceNotEmpty + // if --force flag is passed - it doesn't matter if there are DB clusters in the namespace. + if !cfg.Force { + // Check that there are no DB clusters left in namespaces. + if dbsExist, err := k.DatabasesExist(ctx, ctrlclient.InNamespace(ns)); err != nil { + return errors.Join(err, fmt.Errorf("failed to check if databases exist in namespace='%s'", ns)) + } else if dbsExist { + return ErrNamespaceNotEmpty + } + } } return nil @@ -128,49 +130,49 @@ func NewNamespaceRemove(c NamespaceRemoveConfig, l *zap.SugaredLogger) (*Namespa n.l = zap.NewNop().Sugar() } - k, err := cliutils.NewKubeclient(n.l, n.cfg.KubeconfigPath) + k, err := cliutils.NewKubeConnector(n.l, n.cfg.KubeconfigPath) if err != nil { return nil, err } - n.kubeClient = k + n.kubeConnector = k return n, nil } // Run the namespace removal operation. func (r *NamespaceRemover) Run(ctx context.Context) error { // This command expects a Helm based installation (< 1.4.0) - _, err := cliutils.CheckHelmInstallation(ctx, r.kubeClient) + _, err := cliutils.CheckHelmInstallation(ctx, r.kubeConnector) if err != nil { return err } var removalSteps []steps.Step for _, ns := range r.cfg.NamespaceList { - removalSteps = append(removalSteps, NewRemoveNamespaceSteps(ns, r.cfg.KeepNamespace, r.kubeClient)...) + removalSteps = append(removalSteps, NewRemoveNamespaceSteps(ns, r.cfg.KeepNamespace, r.kubeConnector)...) } return steps.RunStepsWithSpinner(ctx, r.l, removalSteps, r.cfg.Pretty) } // NewRemoveNamespaceSteps returns the steps to remove a namespace. -func NewRemoveNamespaceSteps(namespace string, keepNs bool, k *kubernetes.Kubernetes) []steps.Step { +func NewRemoveNamespaceSteps(namespace string, keepNs bool, k kubernetes.KubernetesConnector) []steps.Step { removeSteps := []steps.Step{ { Desc: fmt.Sprintf("Deleting database clusters in namespace '%s'", namespace), F: func(ctx context.Context) error { - return k.DeleteDatabaseClusters(ctx, namespace) + return k.DeleteDatabaseClusters(ctx, ctrlclient.InNamespace(namespace)) }, }, { Desc: fmt.Sprintf("Deleting backup storages in namespace '%s'", namespace), F: func(ctx context.Context) error { - return k.DeleteBackupStorages(ctx, namespace) + return k.DeleteBackupStorages(ctx, ctrlclient.InNamespace(namespace)) }, }, { Desc: fmt.Sprintf("Deleting monitoring instances in namespace '%s'", namespace), F: func(ctx context.Context) error { - return k.DeleteMonitoringConfigs(ctx, namespace) + return k.DeleteMonitoringConfigs(ctx, ctrlclient.InNamespace(namespace)) }, }, } @@ -192,7 +194,10 @@ func NewRemoveNamespaceSteps(namespace string, keepNs bool, k *kubernetes.Kubern // keep the namespace, but remove the Everest label return removeEverestLabelFromNamespace(ctx, k, namespace) } - if err := k.DeleteNamespace(ctx, namespace); err != nil { + delObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: namespace}, + } + if err := k.DeleteNamespace(ctx, delObj); err != nil { return err } return ensureNamespaceGone(ctx, namespace, k) @@ -201,9 +206,9 @@ func NewRemoveNamespaceSteps(namespace string, keepNs bool, k *kubernetes.Kubern return removeSteps } -func removeEverestLabelFromNamespace(ctx context.Context, k *kubernetes.Kubernetes, namespace string) error { +func removeEverestLabelFromNamespace(ctx context.Context, k kubernetes.KubernetesConnector, namespace string) error { return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) { - ns, err := k.GetNamespace(ctx, namespace) + ns, err := k.GetNamespace(ctx, types.NamespacedName{Name: namespace}) if err != nil { return true, err } @@ -213,7 +218,7 @@ func removeEverestLabelFromNamespace(ctx context.Context, k *kubernetes.Kubernet labels := ns.GetLabels() delete(labels, common.KubernetesManagedByLabel) ns.SetLabels(labels) - _, err = k.UpdateNamespace(ctx, ns, v1.UpdateOptions{}) + _, err = k.UpdateNamespace(ctx, ns) if err != nil && k8serrors.IsConflict(err) { return false, nil } @@ -221,9 +226,9 @@ func removeEverestLabelFromNamespace(ctx context.Context, k *kubernetes.Kubernet }) } -func ensureNamespaceGone(ctx context.Context, namespace string, k *kubernetes.Kubernetes) error { +func ensureNamespaceGone(ctx context.Context, namespace string, k kubernetes.KubernetesConnector) error { return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) { - _, err := k.GetNamespace(ctx, namespace) + _, err := k.GetNamespace(ctx, types.NamespacedName{Name: namespace}) if err != nil && k8serrors.IsNotFound(err) { return true, nil } else if err != nil { diff --git a/pkg/cli/namespaces/utils.go b/pkg/cli/namespaces/utils.go index ff605115b..6345b9025 100644 --- a/pkg/cli/namespaces/utils.go +++ b/pkg/cli/namespaces/utils.go @@ -24,6 +24,7 @@ import ( olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" v1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/strings/slices" "github.com/percona/everest/pkg/common" @@ -97,7 +98,7 @@ func namespaceExists( k kubernetes.KubernetesConnector, namespace string, ) (bool, bool, error) { - ns, err := k.GetNamespace(ctx, namespace) + ns, err := k.GetNamespace(ctx, types.NamespacedName{Name: namespace}) if err != nil { if k8serrors.IsNotFound(err) { return false, false, nil diff --git a/pkg/cli/uninstall/steps.go b/pkg/cli/uninstall/steps.go index c95be7637..84264c3fd 100644 --- a/pkg/cli/uninstall/steps.go +++ b/pkg/cli/uninstall/steps.go @@ -4,8 +4,10 @@ import ( "context" "fmt" + corev1 "k8s.io/api/core/v1" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" @@ -29,7 +31,10 @@ func (u *Uninstall) newStepDeleteNamespace(ns string) steps.Step { Desc: fmt.Sprintf("Deleting namespace '%s'", ns), F: func(ctx context.Context) error { return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - err := u.kubeClient.DeleteNamespace(ctx, ns) + delObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: ns}, + } + err := u.kubeConnector.DeleteNamespace(ctx, delObj) if err != nil { if k8serrors.IsNotFound(err) { return true, nil @@ -53,48 +58,27 @@ func (u *Uninstall) newStepDeleteCRDs() steps.Step { } func (u *Uninstall) deleteEverestCRDs(ctx context.Context) error { - crds, err := u.kubeClient.ListCRDs(ctx) + crds, err := u.kubeConnector.ListCRDs(ctx) if err != nil { return err } - everestCRDs := []string{} for _, crd := range crds.Items { if crd.Spec.Group == everestv1alpha1.GroupVersion.Group { - everestCRDs = append(everestCRDs, crd.Name) - } - } - for _, crd := range everestCRDs { - u.l.Infof("Deleting CRD '%s'", crd) - if err := u.kubeClient.DeleteCRD(ctx, crd); client.IgnoreNotFound(err) != nil { - return err + u.l.Infof("Deleting CRD '%s'", crd.Name) + delObj := &apiextv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: crd.Name, + }, + } + if err = u.kubeConnector.DeleteCRD(ctx, delObj); client.IgnoreNotFound(err) != nil { + return err + } } } return nil } -func (u *Uninstall) uninstallHelmChart(ctx context.Context) error { - // First delete the CSVs in monitoring namespace, otherwise the deletion of the namespace will be stuck. - // TODO: remove this after we install Victoriametrics using its Helm chart. - if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - csvs, err := u.kubeClient.ListClusterServiceVersion(ctx, common.MonitoringNamespace) - if err != nil { - return false, err - } - if len(csvs.Items) == 0 { - return true, nil - } - for _, csv := range csvs.Items { - if err := u.kubeClient.DeleteClusterServiceVersion(ctx, types.NamespacedName{ - Name: csv.Name, - Namespace: csv.Namespace, - }); err != nil { - return false, err - } - } - return false, nil - }); err != nil { - return err - } +func (u *Uninstall) uninstallHelmChart(_ context.Context) error { // Delete helm chart. uninstaller, err := helm.NewUninstaller(common.SystemNamespace, common.SystemNamespace, u.config.KubeconfigPath) if err != nil { diff --git a/pkg/cli/uninstall/uninstall.go b/pkg/cli/uninstall/uninstall.go index 36bec0155..39195937e 100644 --- a/pkg/cli/uninstall/uninstall.go +++ b/pkg/cli/uninstall/uninstall.go @@ -25,6 +25,7 @@ import ( "time" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" "github.com/percona/everest/pkg/cli/namespaces" "github.com/percona/everest/pkg/cli/steps" @@ -41,10 +42,10 @@ const ( // Uninstall implements logic for the cluster command. type Uninstall struct { - config Config - kubeClient *kubernetes.Kubernetes - l *zap.SugaredLogger - clusterType kubernetes.ClusterType + config Config + kubeConnector kubernetes.KubernetesConnector + l *zap.SugaredLogger + clusterType kubernetes.ClusterType } // Config stores configuration for the Uninstall command. @@ -71,11 +72,11 @@ func NewUninstall(c Config, l *zap.SugaredLogger) (*Uninstall, error) { cli.l = zap.NewNop().Sugar() } - kubeClient, err := cliutils.NewKubeclient(cli.l, c.KubeconfigPath) + kubeClient, err := cliutils.NewKubeConnector(cli.l, c.KubeconfigPath) if err != nil { return nil, err } - cli.kubeClient = kubeClient + cli.kubeConnector = kubeClient return cli, nil } @@ -83,7 +84,7 @@ func NewUninstall(c Config, l *zap.SugaredLogger) (*Uninstall, error) { func (u *Uninstall) Run(ctx context.Context) error { // This command expects a Helm based installation. Otherwise, we stop here. // Older versions must use an older version of the CLI. - _, err := cliutils.CheckHelmInstallation(ctx, u.kubeClient) + _, err := cliutils.CheckHelmInstallation(ctx, u.kubeConnector) if err != nil { return err } @@ -102,7 +103,7 @@ func (u *Uninstall) Run(ctx context.Context) error { return fmt.Errorf("failed to detect Kubernetes environment: %w", err) } - dbsExist, err := u.kubeClient.DatabasesExist(ctx) + dbsExist, err := u.kubeConnector.DatabasesExist(ctx) if err != nil { return errors.Join(err, errors.New("failed to check if databases exist")) } @@ -118,7 +119,7 @@ func (u *Uninstall) Run(ctx context.Context) error { } } - dbNamespaces, err := u.kubeClient.GetDBNamespaces(ctx) + dbNamespaces, err := u.kubeConnector.GetDBNamespaces(ctx) if err != nil { return fmt.Errorf("failed to get database namespaces: %w", err) } @@ -142,7 +143,7 @@ func (u *Uninstall) setKubernetesEnv(ctx context.Context) error { if !u.config.SkipEnvDetection { return nil } - t, err := u.kubeClient.GetClusterType(ctx) + t, err := u.kubeConnector.GetClusterType(ctx) if err != nil { return err } @@ -151,17 +152,20 @@ func (u *Uninstall) setKubernetesEnv(ctx context.Context) error { return nil } -func (u *Uninstall) newUninstallSteps(dbNamespaces []string) []steps.Step { - steps := []steps.Step{} +func (u *Uninstall) newUninstallSteps(nsList *corev1.NamespaceList) []steps.Step { + var uninstallSteps []steps.Step - for _, ns := range dbNamespaces { - steps = append(steps, namespaces.NewRemoveNamespaceSteps(ns, false, u.kubeClient)...) + if nsList != nil { + for _, ns := range nsList.Items { + uninstallSteps = append(uninstallSteps, namespaces.NewRemoveNamespaceSteps(ns.GetName(), false, u.kubeConnector)...) + } } - steps = append(steps, u.newStepUninstallHelmChart()) - steps = append(steps, u.newStepDeleteNamespace(common.MonitoringNamespace)) - steps = append(steps, u.newStepDeleteNamespace(common.SystemNamespace)) - steps = append(steps, u.newStepDeleteCRDs()) - return steps + + uninstallSteps = append(uninstallSteps, u.newStepUninstallHelmChart()) + uninstallSteps = append(uninstallSteps, u.newStepDeleteNamespace(common.MonitoringNamespace)) + uninstallSteps = append(uninstallSteps, u.newStepDeleteNamespace(common.SystemNamespace)) + uninstallSteps = append(uninstallSteps, u.newStepDeleteCRDs()) + return uninstallSteps } // Asks user for uninstall confirmation. diff --git a/pkg/cli/upgrade/steps.go b/pkg/cli/upgrade/steps.go index 25980e3f9..ed80e3465 100644 --- a/pkg/cli/upgrade/steps.go +++ b/pkg/cli/upgrade/steps.go @@ -5,8 +5,11 @@ import ( "fmt" "github.com/AlekSi/pointer" + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "helm.sh/helm/v3/pkg/cli/values" + appsv1 "k8s.io/api/apps/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" @@ -70,7 +73,7 @@ func (u *Upgrade) newStepEnsureCatalogSource() steps.Step { return fmt.Errorf("could not get Everest CatalogSource namespace: %w", err) } return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, false, func(ctx context.Context) (bool, error) { - cs, err := u.kubeClient.GetCatalogSource(ctx, common.PerconaEverestCatalogName, catalogNs) + cs, err := u.kubeConnector.GetCatalogSource(ctx, types.NamespacedName{Namespace: catalogNs, Name: common.PerconaEverestCatalogName}) if err != nil { return false, fmt.Errorf("cannot get CatalogSource: %w", err) } @@ -82,7 +85,7 @@ func (u *Upgrade) newStepEnsureCatalogSource() steps.Step { func (u *Upgrade) waitForDeployment(ctx context.Context, name, namespace string) error { u.l.Infof("Waiting for Deployment '%s' in namespace '%s'", name, namespace) - if err := u.kubeClient.WaitForRollout(ctx, name, namespace); err != nil { + if err := u.kubeConnector.WaitForRollout(ctx, types.NamespacedName{Namespace: namespace, Name: name}); err != nil { return err } u.l.Infof("Deployment '%s' in namespace '%s' is ready", name, namespace) @@ -98,7 +101,7 @@ func (u *Upgrade) upgradeCustomResourceDefinitions(ctx context.Context) error { if err != nil { return fmt.Errorf("could not get CRDs: %w", err) } - return u.kubeClient.ApplyManifestFile(helmutils.YAMLStringsToBytes(crds), common.SystemNamespace) + return u.kubeConnector.ApplyManifestFile(ctx, helmutils.YAMLStringsToBytes(crds), common.SystemNamespace) } func (u *Upgrade) upgradeHelmChart(ctx context.Context) error { @@ -122,13 +125,13 @@ func (u *Upgrade) upgradeHelmChart(ctx context.Context) error { } func (u *Upgrade) upgradeEverestDBNamespaceHelmCharts(ctx context.Context) error { - dbNamespaces, err := u.kubeClient.GetDBNamespaces(ctx) + dbNamespaces, err := u.kubeConnector.GetDBNamespaces(ctx) if err != nil { return fmt.Errorf("could not get database namespaces: %w", err) } - for _, ns := range dbNamespaces { - if err := u.upgradeEverestDBNamespaceHelmChart(ctx, ns); err != nil { - return fmt.Errorf("could not upgrade DB namespace '%s' Helm chart: %w", ns, err) + for _, ns := range dbNamespaces.Items { + if err := u.upgradeEverestDBNamespaceHelmChart(ctx, ns.GetName()); err != nil { + return fmt.Errorf("could not upgrade DB namespace '%s' Helm chart: %w", ns.GetName(), err) } } return nil @@ -162,13 +165,13 @@ func (u *Upgrade) migrateLegacyInstallationToHelm(ctx context.Context) error { if err := u.helmInstaller.Install(ctx); err != nil { return fmt.Errorf("failed to install Helm chart: %w", err) } - dbNamespaces, err := u.kubeClient.GetDBNamespaces(ctx) + dbNamespaces, err := u.kubeConnector.GetDBNamespaces(ctx) if err != nil { return fmt.Errorf("could not get database namespaces: %w", err) } - for _, ns := range dbNamespaces { - if err := u.helmAdoptDBNamespaces(ctx, ns, u.upgradeToVersion); err != nil { - return fmt.Errorf("could not migrate DB namespace '%s' installation to Helm: %w", ns, err) + for _, ns := range dbNamespaces.Items { + if err := u.helmAdoptDBNamespaces(ctx, ns.GetName(), u.upgradeToVersion); err != nil { + return fmt.Errorf("could not migrate DB namespace '%s' installation to Helm: %w", ns.GetName(), err) } } return nil @@ -177,7 +180,7 @@ func (u *Upgrade) migrateLegacyInstallationToHelm(ctx context.Context) error { // Creates an installation of the `everest-db-namespace` Helm chart for the given DB namesapce // and adopts its resources. func (u *Upgrade) helmAdoptDBNamespaces(ctx context.Context, namespace, version string) error { - dbEngines, err := u.kubeClient.ListDatabaseEngines(ctx, namespace) + dbEngines, err := u.kubeConnector.ListDatabaseEngines(ctx, client.InNamespace(namespace)) if err != nil { return fmt.Errorf("cannot list database engines in namespace %s: %w", namespace, err) } @@ -214,7 +217,7 @@ func (u *Upgrade) helmAdoptDBNamespaces(ctx context.Context, namespace, version } func helmValuesForDBEngines(list *everestv1alpha1.DatabaseEngineList) values.Options { - vals := []string{} + var vals []string for _, dbEngine := range list.Items { t := dbEngine.Spec.Type vals = append(vals, fmt.Sprintf("%s=%t", t, dbEngine.Status.State == everestv1alpha1.DBEngineStateInstalled)) @@ -229,10 +232,13 @@ func helmValuesForDBEngines(list *everestv1alpha1.DatabaseEngineList) values.Opt func (u *Upgrade) cleanupLegacyResources(ctx context.Context) error { // Delete OLM PackageServer CSV. if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - if err := u.kubeClient.DeleteClusterServiceVersion(ctx, types.NamespacedName{ - Namespace: kubernetes.OLMNamespace, - Name: "packageserver", - }); err != nil { + delObj := &olmv1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kubernetes.OLMNamespace, + Name: "packageserver", + }, + } + if err := u.kubeConnector.DeleteClusterServiceVersion(ctx, delObj); err != nil { if k8serrors.IsNotFound(err) { return true, nil } @@ -244,20 +250,48 @@ func (u *Upgrade) cleanupLegacyResources(ctx context.Context) error { } // Delete resources related to Everest Operator Subscription. - if err := deleteOLMOperator(ctx, u.kubeClient, common.EverestOperatorName, common.SystemNamespace); err != nil { - return fmt.Errorf("could not delete Everest operator: %w", err) + if err := deleteOLMOperator(ctx, u.kubeConnector, common.EverestOperatorName, common.SystemNamespace); err != nil { + return fmt.Errorf("could not delete operator='%s' in namespace='%s': %w", + common.EverestOperatorName, + common.SystemNamespace, + err, + ) } // Delete resources related to victoria metrics operator Subscription. - if err := deleteOLMOperator(ctx, u.kubeClient, "victoriametrics-operator", common.MonitoringNamespace); err != nil { - return fmt.Errorf("could not delete victoria metrics operator: %w", err) + if err := deleteOLMOperator(ctx, u.kubeConnector, common.VictoriaMetricsOperatorName, common.MonitoringNamespace); err != nil { + return fmt.Errorf("could not delete operator='%s' in namespace='%s': %w", + common.VictoriaMetricsOperatorName, + common.MonitoringNamespace, + err, + ) } - if err := u.kubeClient.DeleteDeployment(ctx, "percona-everest", common.SystemNamespace); client.IgnoreNotFound(err) != nil { - return fmt.Errorf("could not delete percona-everest deployment: %w", err) + delDep := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: common.SystemNamespace, + Name: common.PerconaEverestDeploymentName, + }, + } + if err := u.kubeConnector.DeleteDeployment(ctx, delDep); client.IgnoreNotFound(err) != nil { + return fmt.Errorf("could not delete deployment='%s' in namespace='%s': %w", + common.PerconaEverestDeploymentName, + common.SystemNamespace, + err, + ) } // Delete Everest Catalog. // This is not a legacy resource, but we need to delete it so that Helm creates a new one that is owned by the release. - if err := u.kubeClient.DeleteCatalogSource(ctx, common.PerconaEverestCatalogName, kubernetes.OLMNamespace); client.IgnoreNotFound(err) != nil { - return fmt.Errorf("could not delete Everest CatalogSource: %w", err) + delObj := &olmv1alpha1.CatalogSource{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kubernetes.OLMNamespace, + Name: common.PerconaEverestCatalogName, + }, + } + if err := u.kubeConnector.DeleteCatalogSource(ctx, delObj); client.IgnoreNotFound(err) != nil { + return fmt.Errorf("could not delete CatalogSource='%s' in namespace='%s': %w", + common.PerconaEverestCatalogName, + kubernetes.OLMNamespace, + err, + ) } return nil } @@ -266,8 +300,7 @@ func (u *Upgrade) cleanupLegacyResources(ctx context.Context) error { func deleteOLMOperator(ctx context.Context, k kubernetes.KubernetesConnector, subscription, namespace string) error { currentCSV := "" if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - key := types.NamespacedName{Name: subscription, Namespace: namespace} - sub, err := k.GetSubscription(ctx, key.Name, key.Namespace) + sub, err := k.GetSubscription(ctx, types.NamespacedName{Name: subscription, Namespace: namespace}) if err != nil { if k8serrors.IsNotFound(err) { return true, nil @@ -275,16 +308,25 @@ func deleteOLMOperator(ctx context.Context, k kubernetes.KubernetesConnector, su return false, err } currentCSV = sub.Status.InstalledCSV - return false, k.DeleteSubscription(ctx, key) + delObj := &olmv1alpha1.Subscription{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: subscription, + }, + } + return false, k.DeleteSubscription(ctx, delObj) }); err != nil { return err } if currentCSV != "" { if err := wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - if err := k.DeleteClusterServiceVersion(ctx, types.NamespacedName{ - Namespace: namespace, - Name: currentCSV, - }); err != nil { + delObj := &olmv1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: currentCSV, + }, + } + if err := k.DeleteClusterServiceVersion(ctx, delObj); err != nil { if k8serrors.IsNotFound(err) { return true, nil } diff --git a/pkg/cli/upgrade/upgrade.go b/pkg/cli/upgrade/upgrade.go index 9b042e89a..8e84b0b5a 100644 --- a/pkg/cli/upgrade/upgrade.go +++ b/pkg/cli/upgrade/upgrade.go @@ -27,6 +27,7 @@ import ( version "github.com/Percona-Lab/percona-version-service/versionpb" goversion "github.com/hashicorp/go-version" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/cli/helm" @@ -70,7 +71,7 @@ type ( l *zap.SugaredLogger config *Config - kubeClient kubernetes.KubernetesConnector + kubeConnector kubernetes.KubernetesConnector versionService versionservice.Interface // these are set on calling Run @@ -108,7 +109,7 @@ func NewUpgrade(cfg *Config, l *zap.SugaredLogger) (*Upgrade, error) { kubeClient = k } if cfg.KubeconfigPath != "" { - k, err := cliutils.NewKubeclient(cli.l, cfg.KubeconfigPath) + k, err := cliutils.NewKubeConnector(cli.l, cfg.KubeconfigPath) if err != nil { return nil, err } @@ -118,14 +119,14 @@ func NewUpgrade(cfg *Config, l *zap.SugaredLogger) (*Upgrade, error) { return nil, errors.New("must provide kubeconfig path or run in-cluster") } - cli.kubeClient = kubeClient + cli.kubeConnector = kubeClient cli.versionService = versionservice.New(cfg.VersionMetadataURL) return cli, nil } // Run runs the operators installation process. func (u *Upgrade) Run(ctx context.Context) error { - everestVersion, err := cliVersion.EverestVersionFromDeployment(ctx, u.kubeClient) + everestVersion, err := cliVersion.EverestVersionFromDeployment(ctx, u.kubeConnector) if err != nil { return errors.Join(err, errors.New("could not retrieve Everest version")) } @@ -187,7 +188,7 @@ func (u *Upgrade) setKubernetesEnv(ctx context.Context) error { if u.config.SkipEnvDetection { return nil } - t, err := u.kubeClient.GetClusterType(ctx) + t, err := u.kubeConnector.GetClusterType(ctx) if err != nil { return fmt.Errorf("failed to detect cluster type: %w", err) } @@ -233,7 +234,7 @@ func (u *Upgrade) setupHelmInstaller(ctx context.Context) error { func (u *Upgrade) printPostUpgradeMessage(ctx context.Context, out io.Writer) error { _, _ = fmt.Fprintln(out, "\n", output.Rocket("Everest has been upgraded to version %s", u.upgradeToVersion)) - if isSecure, err := u.kubeClient.Accounts().IsSecure(ctx, common.EverestAdminUser); err != nil { + if isSecure, err := u.kubeConnector.Accounts().IsSecure(ctx, common.EverestAdminUser); err != nil { return fmt.Errorf("could not check if the admin password is secure: %w", err) } else if !isSecure { _, _ = fmt.Fprint(os.Stdout, "\n", common.InitialPasswordWarningMessage, "\n") @@ -389,7 +390,7 @@ func (u *Upgrade) checkRequirements(ctx context.Context, supVer *common.Supporte } func (u *Upgrade) checkOperatorRequirements(ctx context.Context, supVer *common.SupportedVersion) error { - nss, err := u.kubeClient.GetDBNamespaces(ctx) + nss, err := u.kubeConnector.GetDBNamespaces(ctx) if err != nil { return err } @@ -399,11 +400,11 @@ func (u *Upgrade) checkOperatorRequirements(ctx context.Context, supVer *common. {common.PostgreSQLOperatorName, supVer.PGOperator}, {common.MongoDBOperatorName, supVer.PSMBDOperator}, } - for _, ns := range nss { + for _, ns := range nss.Items { u.l.Infof("Checking operator requirements in namespace %s", ns) for _, c := range cfg { - v, err := u.kubeClient.OperatorInstalledVersion(ctx, ns, c.operatorName) + v, err := u.kubeConnector.GetInstalledOperatorVersion(ctx, types.NamespacedName{Namespace: ns.GetName(), Name: c.operatorName}) if err != nil && !errors.Is(err, kubernetes.ErrOperatorNotInstalled) { return err } @@ -427,7 +428,7 @@ func (u *Upgrade) checkOperatorRequirements(ctx context.Context, supVer *common. } func (u *Upgrade) applyConfigMapValues(ctx context.Context) error { - cm, err := u.kubeClient.GetConfigMap(ctx, common.SystemNamespace, common.EverestRBACConfigMapName) + cm, err := u.kubeConnector.GetConfigMap(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestRBACConfigMapName}) if client.IgnoreNotFound(err) != nil { return err } @@ -440,7 +441,7 @@ func (u *Upgrade) applyConfigMapValues(ctx context.Context) error { } } - cm, err = u.kubeClient.GetConfigMap(ctx, common.SystemNamespace, common.EverestSettingsConfigMapName) + cm, err = u.kubeConnector.GetConfigMap(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestSettingsConfigMapName}) if client.IgnoreNotFound(err) != nil { return err } @@ -455,7 +456,7 @@ func (u *Upgrade) applyConfigMapValues(ctx context.Context) error { // We don't handle the accounts secret here because we cannot configure it via the Helm values. // The Helm chart handles the secret differently. func (u *Upgrade) applySecretValues(ctx context.Context) error { - secret, err := u.kubeClient.GetSecret(ctx, common.SystemNamespace, common.EverestJWTSecretName) + secret, err := u.kubeConnector.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestJWTSecretName}) if client.IgnoreNotFound(err) != nil { return err } diff --git a/pkg/cli/upgrade/upgrade_test.go b/pkg/cli/upgrade/upgrade_test.go index 862393941..37cbbc957 100644 --- a/pkg/cli/upgrade/upgrade_test.go +++ b/pkg/cli/upgrade/upgrade_test.go @@ -26,9 +26,9 @@ import ( version "github.com/Percona-Lab/percona-version-service/versionpb" goversion "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/percona/everest/pkg/kubernetes" versionservice "github.com/percona/everest/pkg/version_service" @@ -188,15 +188,16 @@ func TestUpgrade_canUpgrade(t *testing.T) { })) defer ts.Close() - k := &kubernetes.MockKubernetesConnector{} - k.On("GetDBNamespaces", mock.Anything, mock.Anything).Return([]string{}, nil) + mockClient := fakeclient.NewClientBuilder(). + WithScheme(kubernetes.CreateScheme()) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) u := &Upgrade{ - l: zap.L().Sugar(), + l: zap.NewNop().Sugar(), config: &Config{ VersionMetadataURL: ts.URL, }, - kubeClient: k, + kubeConnector: k, versionService: versionservice.New(ts.URL), } everestVersion, err := goversion.NewVersion(tt.everestVersion) diff --git a/pkg/cli/utils/utils.go b/pkg/cli/utils/utils.go index 3e4febeb1..e42a70f3e 100644 --- a/pkg/cli/utils/utils.go +++ b/pkg/cli/utils/utils.go @@ -32,8 +32,8 @@ func DBNamespaceSubChartPath(dir string) string { // CheckHelmInstallation ensures that the current installation was done using Helm chart. // Returns the version of Everest installed in the cluster. // Returns an error if the installation was not done using Helm chart. -func CheckHelmInstallation(ctx context.Context, client kubernetes.KubernetesConnector) (string, error) { - everestVersion, err := version.EverestVersionFromDeployment(ctx, client) +func CheckHelmInstallation(ctx context.Context, kubeConnector kubernetes.KubernetesConnector) (string, error) { + everestVersion, err := version.EverestVersionFromDeployment(ctx, kubeConnector) if err != nil { if k8serrors.IsNotFound(err) { return "", errors.New("everest is not installed in the cluster") @@ -50,8 +50,8 @@ func CheckHelmInstallation(ctx context.Context, client kubernetes.KubernetesConn return ver, nil } -// NewKubeclient creates a new Kubernetes client. -func NewKubeclient(l *zap.SugaredLogger, kubeconfigPath string) (*kubernetes.Kubernetes, error) { +// NewKubeConnector creates a new Kubernetes client. +func NewKubeConnector(l *zap.SugaredLogger, kubeconfigPath string) (kubernetes.KubernetesConnector, error) { k, err := kubernetes.New(kubeconfigPath, l) if err != nil { var u *url.Error diff --git a/pkg/cli/utils/utils_test.go b/pkg/cli/utils/utils_test.go index 8a5643ca0..68ae09dcd 100644 --- a/pkg/cli/utils/utils_test.go +++ b/pkg/cli/utils/utils_test.go @@ -5,10 +5,12 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/zap" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/percona/everest/pkg/common" "github.com/percona/everest/pkg/kubernetes" @@ -17,51 +19,67 @@ import ( func TestCheckHelmInstallation(t *testing.T) { t.Parallel() - c := &kubernetes.MockKubernetesConnector{} testCases := []struct { everestVersion string wantErr bool + getDpFunc func(v string) *appsv1.Deployment }{ { everestVersion: "1.4.0", wantErr: false, + getDpFunc: getNewDeployment, }, { everestVersion: "1.4.0-rc3", wantErr: false, + getDpFunc: getNewDeployment, }, { everestVersion: "1.3.0", wantErr: true, + getDpFunc: getLegacyDeployment, }, { everestVersion: "0.0.0", wantErr: false, + getDpFunc: getNewDeployment, }, } ctx := context.Background() for _, tc := range testCases { - mockCall := c.On("GetDeployment", - mock.Anything, - mock.Anything, - mock.Anything, - ). - Return(getDeployment(tc.everestVersion), nil) + mockClient := fakeclient.NewClientBuilder(). + WithScheme(kubernetes.CreateScheme()). + WithObjects(tc.getDpFunc(tc.everestVersion)) + k := kubernetes.NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) - v, err := CheckHelmInstallation(ctx, c) + v, err := CheckHelmInstallation(ctx, k) if tc.wantErr { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tc.everestVersion, v) } - mockCall.Unset() } } -func getDeployment(v string) *appsv1.Deployment { +func getNewDeployment(v string) *appsv1.Deployment { + return getDeployment(v, common.PerconaEverestDeploymentName) +} + +func getLegacyDeployment(v string) *appsv1.Deployment { + return getDeployment(v, common.PerconaEverestDeploymentNameLegacy) +} + +func getDeployment(v, depName string) *appsv1.Deployment { return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: depName, + Namespace: common.SystemNamespace, + CreationTimestamp: metav1.Time{ + Time: metav1.Now().Add(-5), + }, + }, Spec: appsv1.DeploymentSpec{ Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ diff --git a/pkg/common/constants.go b/pkg/common/constants.go index b0f0619f6..4147c70d3 100644 --- a/pkg/common/constants.go +++ b/pkg/common/constants.go @@ -62,6 +62,9 @@ const ( // EverestOperatorName holds the name for Everest operator. EverestOperatorName = "everest-operator" + // VictoriaMetricsOperatorName holds the name for VictoriaMetrics operator. + VictoriaMetricsOperatorName = "victoriametrics-operator" + // EverestAccountsSecretName is the name of the secret that holds accounts. EverestAccountsSecretName = "everest-accounts" // EverestJWTSecretName is the name of the secret that holds JWT secret. @@ -84,6 +87,8 @@ const ( EverestRBACConfigMapName = "everest-rbac" // KubernetesManagedByLabel is the label used to identify resources managed by Everest. KubernetesManagedByLabel = "app.kubernetes.io/managed-by" + // DatabaseClusterNameLabel is the label used to identify resources by DB cluster name. + DatabaseClusterNameLabel = "clusterName" // ForegroundDeletionFinalizer is the finalizer used to delete resources in foreground. ForegroundDeletionFinalizer = "foregroundDeletion" // UserCtxKey is the key used to store the user in the context. diff --git a/pkg/kubernetes/accounts.go b/pkg/kubernetes/accounts.go index 601e9a879..0398f4416 100644 --- a/pkg/kubernetes/accounts.go +++ b/pkg/kubernetes/accounts.go @@ -17,13 +17,251 @@ package kubernetes import ( + "context" + "crypto/sha256" + "crypto/subtle" + "errors" + "fmt" + "time" + + "golang.org/x/crypto/pbkdf2" + "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/types" + "github.com/percona/everest/pkg/accounts" - k8sAccounts "github.com/percona/everest/pkg/kubernetes/client/accounts" + "github.com/percona/everest/pkg/common" ) -// Accounts returns a new client for managing everest user accounts. -// -//nolint:ireturn,stylecheck -func (c *Kubernetes) Accounts() accounts.Interface { - return k8sAccounts.New(c.client) +const ( + usersFile = "users.yaml" + // We set this annotation on the secret to indicate which passwords are stored in plain text. + insecurePasswordAnnotation = "insecure-password/%s" + insecurePasswordValueTrue = "true" + + keyLength = 32 + iter = 4096 +) + +type configMapsClient struct { + k KubernetesConnector +} + +// Accounts returns an implementation of the accounts interface that +// manages everest accounts directly via ConfigMaps. +func (k *Kubernetes) Accounts() accounts.Interface { + return &configMapsClient{k: k} +} + +// Get returns an account by username. +func (a *configMapsClient) Get(ctx context.Context, username string) (*accounts.Account, error) { + users, err := a.listAllAccounts(ctx) + if err != nil { + return nil, err + } + user, found := users[username] + if !found { + return nil, accounts.ErrAccountNotFound + } + return user, nil +} + +// List returns a list of all accounts. +func (a *configMapsClient) List(ctx context.Context) (map[string]*accounts.Account, error) { + return a.listAllAccounts(ctx) +} + +func (a *configMapsClient) listAllAccounts(ctx context.Context) (map[string]*accounts.Account, error) { + result := make(map[string]*accounts.Account) + secret, err := a.k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestAccountsSecretName}) + if err != nil { + return nil, err + } + if err := yaml.Unmarshal(secret.Data[usersFile], result); err != nil { + return nil, err + } + return result, nil +} + +// Create a new user account. +func (a *configMapsClient) Create(ctx context.Context, username, password string) error { + // Ensure that the user does not already exist. + _, err := a.Get(ctx, username) + if err != nil && !errors.Is(err, accounts.ErrAccountNotFound) { + return errors.Join(err, errors.New("failed to check if account already exists")) + } else if err == nil { + return accounts.ErrUserAlreadyExists + } + + if password == "" { + return errors.New("password cannot be empty") + } + + // Compute a hash for the password. + hash, err := a.computePasswordHash(ctx, password) + if err != nil { + return errors.Join(err, errors.New("failed to compute hash")) + } + + account := &accounts.Account{ + Enabled: true, + Capabilities: []accounts.AccountCapability{accounts.AccountCapabilityLogin}, + PasswordMtime: time.Now().Format(time.RFC3339), + PasswordHash: hash, + } + return a.insertOrUpdateAccount(ctx, username, account, true) +} + +// SetPassword sets a new password for an existing user account. +func (a *configMapsClient) SetPassword(ctx context.Context, username, newPassword string, secure bool) error { + user, err := a.Get(ctx, username) + if err != nil { + return err + } + user.PasswordHash = newPassword + if secure { + pwHash, err := a.computePasswordHash(ctx, newPassword) + if err != nil { + return err + } + user.PasswordHash = pwHash + } + user.PasswordMtime = time.Now().Format(time.RFC3339) + return a.insertOrUpdateAccount(ctx, username, user, secure) +} + +func (a *configMapsClient) insertOrUpdateAccount( + ctx context.Context, + username string, + account *accounts.Account, + secure bool, +) error { + secret, err := a.k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestAccountsSecretName}) + if err != nil { + return err + } + + accounts := make(map[string]*accounts.Account) + if err := yaml.Unmarshal(secret.Data[usersFile], &accounts); err != nil { + return err + } + + accounts[username] = account + data, err := yaml.Marshal(accounts) + if err != nil { + return err + } + + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + secret.Data[usersFile] = data + + annotations := secret.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + delete(annotations, fmt.Sprintf(insecurePasswordAnnotation, username)) + if !secure { + annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] = insecurePasswordValueTrue + } + secret.SetAnnotations(annotations) + + if _, err := a.k.UpdateSecret(ctx, secret); err != nil { + return err + } + return nil +} + +func (a *configMapsClient) salt(ctx context.Context) ([]byte, error) { + ns, err := a.k.GetNamespace(ctx, types.NamespacedName{Name: common.SystemNamespace}) + if err != nil { + return nil, err + } + return []byte(ns.UID), nil +} + +// Delete an existing user account specified by username. +func (a *configMapsClient) Delete(ctx context.Context, username string) error { + users, err := a.listAllAccounts(ctx) + if err != nil { + return err + } + + if _, found := users[username]; !found { + return accounts.ErrAccountNotFound + } + + delete(users, username) + secret, err := a.k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestAccountsSecretName}) + if err != nil { + return err + } + data, err := yaml.Marshal(users) + if err != nil { + return err + } + secret.Data[usersFile] = data + if _, err := a.k.UpdateSecret(ctx, secret); err != nil { + return err + } + return nil +} + +func (a *configMapsClient) Verify(ctx context.Context, username, password string) error { + secret, err := a.k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestAccountsSecretName}) + if err != nil { + return err + } + + users := make(map[string]*accounts.Account) + if err := yaml.Unmarshal(secret.Data[usersFile], users); err != nil { + return err + } + user, found := users[username] + if !found { + return accounts.ErrAccountNotFound + } + + // helper to check if a password should be compared as a hash. + shouldCompareAsHash := func() bool { + annotations := secret.GetAnnotations() + _, found := annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] + return !found + } + + actual := user.PasswordHash + provided := password + + if shouldCompareAsHash() { + computedHash, err := a.computePasswordHash(ctx, password) + if err != nil { + return err + } + provided = computedHash + } + + if subtle.ConstantTimeCompare([]byte(actual), []byte(provided)) == 0 { + return accounts.ErrIncorrectPassword + } + return nil +} + +// IsSecure returns true if the password for the given user is stored as a hash. +func (a *configMapsClient) IsSecure(ctx context.Context, username string) (bool, error) { + secret, err := a.k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestAccountsSecretName}) + if err != nil { + return false, err + } + annotations := secret.GetAnnotations() + isSecure, found := annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] + return !found || isSecure != insecurePasswordValueTrue, nil +} + +func (a *configMapsClient) computePasswordHash(ctx context.Context, password string) (string, error) { + salt, err := a.salt(ctx) + if err != nil { + return "", errors.Join(err, errors.New("failed to get salt")) + } + hash := pbkdf2.Key([]byte(password), salt, iter, keyLength, sha256.New) + return string(hash), nil } diff --git a/pkg/kubernetes/accounts_test.go b/pkg/kubernetes/accounts_test.go new file mode 100644 index 000000000..9bc46c745 --- /dev/null +++ b/pkg/kubernetes/accounts_test.go @@ -0,0 +1,50 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "testing" + + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/percona/everest/pkg/accounts" + "github.com/percona/everest/pkg/common" +) + +func TestAccounts(t *testing.T) { + t.Parallel() + + objs := []ctrlclient.Object{ + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: common.SystemNamespace}, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.EverestAccountsSecretName, + Namespace: common.SystemNamespace, + }, + }, + } + + mockClient := fakeclient.NewClientBuilder().WithScheme(CreateScheme()) + mockClient.WithObjects(objs...) + k := NewEmpty(zap.NewNop().Sugar()).WithKubernetesClient(mockClient.Build()) + accounts.Tests(t, k.Accounts()) +} diff --git a/pkg/kubernetes/backup_storage.go b/pkg/kubernetes/backup_storage.go index 7edb690e0..b45fc1649 100644 --- a/pkg/kubernetes/backup_storage.go +++ b/pkg/kubernetes/backup_storage.go @@ -26,93 +26,143 @@ import ( everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) -// ListBackupStorages returns list of managed backup storages. -func (k *Kubernetes) ListBackupStorages(ctx context.Context, namespace string) (*everestv1alpha1.BackupStorageList, error) { - return k.client.ListBackupStorages(ctx, namespace, metav1.ListOptions{}) +// ListBackupStorages returns list of managed backup storages in a given namespace. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListBackupStorages(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.BackupStorageList, error) { + result := &everestv1alpha1.BackupStorageList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetBackupStorage returns backup storages by provided name. -func (k *Kubernetes) GetBackupStorage(ctx context.Context, namespace, name string) (*everestv1alpha1.BackupStorage, error) { - return k.client.GetBackupStorage(ctx, namespace, name) +// listBackupStoragesMeta returns list of managed backup storages in a given namespace. +// This method returns a list of simplified objects (meta only). +func (k *Kubernetes) listBackupStoragesMeta(ctx context.Context, opts ...ctrlclient.ListOption) (*metav1.PartialObjectMetadataList, error) { + bsListMeta := &metav1.PartialObjectMetadataList{} + bsListMeta.SetGroupVersionKind(everestv1alpha1.GroupVersion.WithKind("BackupStorageList")) + if err := k.k8sClient.List(ctx, bsListMeta, opts...); err != nil { + return nil, err + } + return bsListMeta, nil } -// CreateBackupStorage returns backup storages by provided name. +// GetBackupStorage returns backup storages by provided name and namespace. +func (k *Kubernetes) GetBackupStorage(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.BackupStorage, error) { + result := &everestv1alpha1.BackupStorage{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// CreateBackupStorage creates backup storages by provided object. func (k *Kubernetes) CreateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) { - return k.client.CreateBackupStorage(ctx, storage) + if err := k.k8sClient.Create(ctx, storage); err != nil { + return nil, err + } + return storage, nil } -// UpdateBackupStorage returns backup storages by provided name. +// UpdateBackupStorage updates backup storages by provided new object. func (k *Kubernetes) UpdateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) { - return k.client.UpdateBackupStorage(ctx, storage) + if err := k.k8sClient.Update(ctx, storage); err != nil { + return nil, err + } + return storage, nil } -// DeleteBackupStorage returns backup storages by provided name. -func (k *Kubernetes) DeleteBackupStorage(ctx context.Context, namespace, name string) error { - return k.client.DeleteBackupStorage(ctx, namespace, name) +// DeleteBackupStorage returns backup storages by provided name and namespace. +func (k *Kubernetes) DeleteBackupStorage(ctx context.Context, obj *everestv1alpha1.BackupStorage) error { + return k.k8sClient.Delete(ctx, obj) } // DeleteBackupStorages deletes all backup storages in provided namespace. // This function will wait until all storages are deleted. -func (k *Kubernetes) DeleteBackupStorages(ctx context.Context, namespace string) error { +func (k *Kubernetes) DeleteBackupStorages(ctx context.Context, opts ...ctrlclient.ListOption) error { + // No need to fetch full objects, we only need the fact there are objects that match the criteria(opts). + delList, err := k.listBackupStoragesMeta(ctx, opts...) + if err != nil { + k.l.Errorf("Could not list backup storages: %s", err) + return err + } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return nil + } + + // need to convert ListOptions to DeleteAllOfOptions + delOpts := &ctrlclient.DeleteAllOfOptions{} + for _, opt := range opts { + opt.ApplyToList(&delOpts.ListOptions) + } + + k.l.Debugf("Setting backup storages removal timeout to %s", pollTimeout) return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - list, err := k.ListBackupStorages(ctx, namespace) - if err != nil { - return false, err - } - if len(list.Items) == 0 { - return true, nil - } - for _, storage := range list.Items { - if err := k.DeleteBackupStorage(ctx, storage.GetNamespace(), storage.GetName()); ctrlclient.IgnoreNotFound(err) != nil { + // Skip fetching the list of objects to delete again, we already have it (see code above). + if delList == nil { + var err error + if delList, err = k.listBackupStoragesMeta(ctx, opts...); err != nil { + k.l.Errorf("Could not list backup storages in polling: %s", err) return false, err } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return true, nil + } + } + + // Reset the list to nil to fetch it again on the next iteration. + delList = nil + + if err := k.k8sClient.DeleteAllOf(ctx, &everestv1alpha1.BackupStorage{}, delOpts); err != nil { + return false, err } return false, nil }) } -// IsBackupStorageUsed checks if a backup storage in a given namespace is used by any clusters -// in that namespace. -// -//nolint:cyclop -func (k *Kubernetes) IsBackupStorageUsed(ctx context.Context, namespace, name string) (bool, error) { - _, err := k.client.GetBackupStorage(ctx, namespace, name) +// IsBackupStorageUsed checks if a backup storage that matches the criteria is used by any DB clusters. +func (k *Kubernetes) IsBackupStorageUsed(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) { + _, err := k.GetBackupStorage(ctx, key) if err != nil { return false, err } // Check if it is in use by clusters? - clusters, err := k.client.ListDatabaseClusters(ctx, namespace, metav1.ListOptions{}) + clusters, err := k.ListDatabaseClusters(ctx, ctrlclient.InNamespace(key.Namespace)) if err != nil { return false, err } for _, cluster := range clusters.Items { for _, sched := range cluster.Spec.Backup.Schedules { - if sched.Enabled && sched.BackupStorageName == name { + if sched.Enabled && sched.BackupStorageName == key.Name { return true, nil } } } // Check if it is in use by backups? - backups, err := k.client.ListDatabaseClusterBackups(ctx, namespace, metav1.ListOptions{}) + backups, err := k.ListDatabaseClusterBackups(ctx, ctrlclient.InNamespace(key.Namespace)) if err != nil { return false, err } for _, backup := range backups.Items { - if backup.Spec.BackupStorageName == name { + if backup.Spec.BackupStorageName == key.Name { return true, nil } } // Check if it is in use by restores? - restores, err := k.client.ListDatabaseClusterRestores(ctx, namespace, metav1.ListOptions{}) + restores, err := k.ListDatabaseClusterRestores(ctx, ctrlclient.InNamespace(key.Namespace)) if err != nil { return false, err } for _, restore := range restores.Items { src := restore.Spec.DataSource.BackupSource - if src != nil && src.BackupStorageName == name { + if src != nil && src.BackupStorageName == key.Name { return true, nil } for _, db := range clusters.Items { diff --git a/pkg/kubernetes/client/accounts/accounts.go b/pkg/kubernetes/client/accounts/accounts.go deleted file mode 100644 index b552c8518..000000000 --- a/pkg/kubernetes/client/accounts/accounts.go +++ /dev/null @@ -1,269 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 accounts provides functionality for managing Everest user accounts -package accounts - -import ( - "context" - "crypto/sha256" - "crypto/subtle" - "errors" - "fmt" - "time" - - "golang.org/x/crypto/pbkdf2" - "gopkg.in/yaml.v2" - - "github.com/percona/everest/pkg/accounts" - "github.com/percona/everest/pkg/common" - "github.com/percona/everest/pkg/kubernetes/client" -) - -const ( - usersFile = "users.yaml" - // We set this annotation on the secret to indicate which passwords are stored in plain text. - insecurePasswordAnnotation = "insecure-password/%s" - insecurePasswordValueTrue = "true" - - keyLength = 32 - iter = 4096 -) - -type configMapsClient struct { - k client.KubeClientConnector -} - -// New returns an implementation of the accounts interface that -// manages everest accounts directly via ConfigMaps. -// -//nolint:ireturn -func New(k client.KubeClientConnector) accounts.Interface { - return &configMapsClient{k: k} -} - -// Get returns an account by username. -func (a *configMapsClient) Get(ctx context.Context, username string) (*accounts.Account, error) { - users, err := a.listAllAccounts(ctx) - if err != nil { - return nil, err - } - user, found := users[username] - if !found { - return nil, accounts.ErrAccountNotFound - } - return user, nil -} - -// List returns a list of all accounts. -func (a *configMapsClient) List(ctx context.Context) (map[string]*accounts.Account, error) { - return a.listAllAccounts(ctx) -} - -func (a *configMapsClient) listAllAccounts(ctx context.Context) (map[string]*accounts.Account, error) { - result := make(map[string]*accounts.Account) - secret, err := a.k.GetSecret(ctx, common.SystemNamespace, common.EverestAccountsSecretName) - if err != nil { - return nil, err - } - if err := yaml.Unmarshal(secret.Data[usersFile], result); err != nil { - return nil, err - } - return result, nil -} - -// Create a new user account. -func (a *configMapsClient) Create(ctx context.Context, username, password string) error { - // Ensure that the user does not already exist. - _, err := a.Get(ctx, username) - if err != nil && !errors.Is(err, accounts.ErrAccountNotFound) { - return errors.Join(err, errors.New("failed to check if account already exists")) - } else if err == nil { - return accounts.ErrUserAlreadyExists - } - - if password == "" { - return errors.New("password cannot be empty") - } - - // Compute a hash for the password. - hash, err := a.computePasswordHash(ctx, password) - if err != nil { - return errors.Join(err, errors.New("failed to compute hash")) - } - - account := &accounts.Account{ - Enabled: true, - Capabilities: []accounts.AccountCapability{accounts.AccountCapabilityLogin}, - PasswordMtime: time.Now().Format(time.RFC3339), - PasswordHash: hash, - } - return a.insertOrUpdateAccount(ctx, username, account, true) -} - -// SetPassword sets a new password for an existing user account. -func (a *configMapsClient) SetPassword(ctx context.Context, username, newPassword string, secure bool) error { - user, err := a.Get(ctx, username) - if err != nil { - return err - } - user.PasswordHash = newPassword - if secure { - pwHash, err := a.computePasswordHash(ctx, newPassword) - if err != nil { - return err - } - user.PasswordHash = pwHash - } - user.PasswordMtime = time.Now().Format(time.RFC3339) - return a.insertOrUpdateAccount(ctx, username, user, secure) -} - -func (a *configMapsClient) insertOrUpdateAccount( - ctx context.Context, - username string, - account *accounts.Account, - secure bool, -) error { - secret, err := a.k.GetSecret(ctx, common.SystemNamespace, common.EverestAccountsSecretName) - if err != nil { - return err - } - - accounts := make(map[string]*accounts.Account) - if err := yaml.Unmarshal(secret.Data[usersFile], &accounts); err != nil { - return err - } - - accounts[username] = account - data, err := yaml.Marshal(accounts) - if err != nil { - return err - } - - if secret.Data == nil { - secret.Data = make(map[string][]byte) - } - secret.Data[usersFile] = data - - annotations := secret.GetAnnotations() - if annotations == nil { - annotations = make(map[string]string) - } - delete(annotations, fmt.Sprintf(insecurePasswordAnnotation, username)) - if !secure { - annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] = insecurePasswordValueTrue - } - secret.SetAnnotations(annotations) - - if _, err := a.k.UpdateSecret(ctx, secret); err != nil { - return err - } - return nil -} - -func (a *configMapsClient) salt(ctx context.Context) ([]byte, error) { - ns, err := a.k.GetNamespace(ctx, common.SystemNamespace) - if err != nil { - return nil, err - } - return []byte(ns.UID), nil -} - -// Delete an existing user account specified by username. -func (a *configMapsClient) Delete(ctx context.Context, username string) error { - users, err := a.listAllAccounts(ctx) - if err != nil { - return err - } - - if _, found := users[username]; !found { - return accounts.ErrAccountNotFound - } - - delete(users, username) - secret, err := a.k.GetSecret(ctx, common.SystemNamespace, common.EverestAccountsSecretName) - if err != nil { - return err - } - data, err := yaml.Marshal(users) - if err != nil { - return err - } - secret.Data[usersFile] = data - if _, err := a.k.UpdateSecret(ctx, secret); err != nil { - return err - } - return nil -} - -func (a *configMapsClient) Verify(ctx context.Context, username, password string) error { - secret, err := a.k.GetSecret(ctx, common.SystemNamespace, common.EverestAccountsSecretName) - if err != nil { - return err - } - - users := make(map[string]*accounts.Account) - if err := yaml.Unmarshal(secret.Data[usersFile], users); err != nil { - return err - } - user, found := users[username] - if !found { - return accounts.ErrAccountNotFound - } - - // helper to check if a password should be compared as a hash. - shouldCompareAsHash := func() bool { - annotations := secret.GetAnnotations() - _, found := annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] - return !found - } - - actual := user.PasswordHash - provided := password - - if shouldCompareAsHash() { - computedHash, err := a.computePasswordHash(ctx, password) - if err != nil { - return err - } - provided = computedHash - } - - if subtle.ConstantTimeCompare([]byte(actual), []byte(provided)) == 0 { - return accounts.ErrIncorrectPassword - } - return nil -} - -// IsSecure returns true if the password for the given user is stored as a hash. -func (a *configMapsClient) IsSecure(ctx context.Context, username string) (bool, error) { - secret, err := a.k.GetSecret(ctx, common.SystemNamespace, common.EverestAccountsSecretName) - if err != nil { - return false, err - } - annotations := secret.GetAnnotations() - isSecure, found := annotations[fmt.Sprintf(insecurePasswordAnnotation, username)] - return !found || isSecure != insecurePasswordValueTrue, nil -} - -func (a *configMapsClient) computePasswordHash(ctx context.Context, password string) (string, error) { - salt, err := a.salt(ctx) - if err != nil { - return "", errors.Join(err, errors.New("failed to get salt")) - } - hash := pbkdf2.Key([]byte(password), salt, iter, keyLength, sha256.New) - return string(hash), nil -} diff --git a/pkg/kubernetes/client/accounts/accounts_test.go b/pkg/kubernetes/client/accounts/accounts_test.go deleted file mode 100644 index 4a255655b..000000000 --- a/pkg/kubernetes/client/accounts/accounts_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package accounts - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/percona/everest/pkg/accounts" - "github.com/percona/everest/pkg/common" - "github.com/percona/everest/pkg/kubernetes/client" -) - -func TestAccounts(t *testing.T) { - t.Parallel() - ctx := context.Background() - - c := client.NewFromFakeClient() - - // Create system namespace for testing. - _, err := c.Clientset(). - CoreV1(). - Namespaces(). - Create(ctx, &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: common.SystemNamespace}, - }, metav1.CreateOptions{}, - ) - require.NoError(t, err) - - // Prepare secret. - _, err = c.Clientset(). - CoreV1(). - Secrets(common.SystemNamespace). - Create(ctx, &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.EverestAccountsSecretName, - Namespace: common.SystemNamespace, - }, - }, metav1.CreateOptions{}, - ) - require.NoError(t, err) - - accounts.Tests(t, New(c)) -} diff --git a/pkg/kubernetes/client/backup_storage.go b/pkg/kubernetes/client/backup_storage.go deleted file mode 100644 index 316b110e2..000000000 --- a/pkg/kubernetes/client/backup_storage.go +++ /dev/null @@ -1,50 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client ... -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// CreateBackupStorage creates an backupStorage. -func (c *Client) CreateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) { - return c.customClientSet.BackupStorage(storage.Namespace).Create(ctx, storage, metav1.CreateOptions{}) -} - -// UpdateBackupStorage updates an backupStorage. -func (c *Client) UpdateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) { - return c.customClientSet.BackupStorage(storage.Namespace).Update(ctx, storage, metav1.UpdateOptions{}) -} - -// GetBackupStorage returns the backupStorage. -func (c *Client) GetBackupStorage(ctx context.Context, namespace, name string) (*everestv1alpha1.BackupStorage, error) { - return c.customClientSet.BackupStorage(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListBackupStorages returns the backupStorage. -func (c *Client) ListBackupStorages(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.BackupStorageList, error) { - return c.customClientSet.BackupStorage(namespace).List(ctx, options) -} - -// DeleteBackupStorage deletes the backupStorage. -func (c *Client) DeleteBackupStorage(ctx context.Context, namespace, name string) error { - return c.customClientSet.BackupStorage(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/catalog_source.go b/pkg/kubernetes/client/catalog_source.go deleted file mode 100644 index 39e0f2097..000000000 --- a/pkg/kubernetes/client/catalog_source.go +++ /dev/null @@ -1,12 +0,0 @@ -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// DeleteCatalogSource deletes a catalog source. -func (c *Client) DeleteCatalogSource(ctx context.Context, namespace string, name string) error { - return c.olmClientset.OperatorsV1alpha1().CatalogSources(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/client.go b/pkg/kubernetes/client/client.go deleted file mode 100644 index ec761d749..000000000 --- a/pkg/kubernetes/client/client.go +++ /dev/null @@ -1,1425 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client provides a way to communicate with a k8s cluster. -package client - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "os" - "slices" - "sort" - "strings" - "sync" - "text/tabwriter" - "time" - - v1 "github.com/operator-framework/api/pkg/operators/v1" - "github.com/operator-framework/api/pkg/operators/v1alpha1" - olmVersioned "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - packagev1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" - packageServerClient "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/client/clientset/versioned" - "go.uber.org/zap" - "gopkg.in/yaml.v3" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextv1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - yamlSerializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/duration" - "k8s.io/apimachinery/pkg/util/wait" - yamlutil "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/apimachinery/pkg/version" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - _ "k8s.io/client-go/plugin/pkg/client/auth" // load all auth plugins - "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/reference" - deploymentutil "k8s.io/kubectl/pkg/util/deployment" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" - - "github.com/percona/everest/pkg/kubernetes/client/customresources" -) - -const ( - configKind = "Config" - apiVersion = "v1" - defaultName = "default" - - defaultQPSLimit = 100 - defaultBurstLimit = 150 - defaultChunkSize = 500 - - defaultAPIURIPath = "/api" - defaultAPIsURIPath = "/apis" - - defaultLogLines = 3000 - - requestTimeout = 10 * time.Second - - objectsBufferSize = 100 -) - -// Each level has 2 spaces for PrefixWriter. -const ( - LEVEL0 = iota - LEVEL1 - LEVEL2 - LEVEL3 - LEVEL4 -) - -// Client is the internal client for Kubernetes. -type Client struct { - l *zap.SugaredLogger - - clientset kubernetes.Interface - customClientSet *customresources.Client - apiextClientset apiextv1clientset.Interface - dynamicClientset dynamic.Interface - olmClientset olmVersioned.Interface - rcLock *sync.Mutex - restConfig *rest.Config - namespace string - clusterName string -} - -// SortableEvents implements sort.Interface for []api.Event based on the Timestamp field. -type SortableEvents []corev1.Event - -func (list SortableEvents) Len() int { - return len(list) -} - -func (list SortableEvents) Swap(i, j int) { - list[i], list[j] = list[j], list[i] -} - -func (list SortableEvents) Less(i, j int) bool { - return list[i].LastTimestamp.Time.Before(list[j].LastTimestamp.Time) -} - -type resourceError struct { - name string - issue string -} - -type podError struct { - resourceError -} - -type deploymentError struct { - resourceError - podErrs podErrors -} - -type ( - deploymentErrors []deploymentError - podErrors []podError -) - -func (e deploymentErrors) Error() string { - var sb strings.Builder - for _, i := range e { - sb.WriteString(fmt.Sprintf("deployment %s has error: %s\n%s", i.name, i.issue, i.podErrs.Error())) - } - return sb.String() -} - -func (e podErrors) Error() string { - var sb strings.Builder - for _, i := range e { - sb.WriteString(fmt.Sprintf("\tpod %s has error: %s\n", i.name, i.issue)) - } - return sb.String() -} - -// NewFromKubeConfig returns new Client from path to a kubeconfig. -func NewFromKubeConfig(kubeconfig string, l *zap.SugaredLogger) (*Client, error) { - home := os.Getenv("HOME") - path := strings.ReplaceAll(kubeconfig, "~", home) - fileData, err := os.ReadFile(path) //nolint:gosec - if err != nil { - return nil, errors.Join(err, errors.New("could not read kubeconfig file")) - } - - clientConfig, err := clientcmd.Load(fileData) - if err != nil { - return nil, errors.Join(err, errors.New("could not parse kubeconfig file")) - } - - config, err := clientcmd.RESTConfigFromKubeConfig(fileData) - if err != nil { - return nil, err - } - - config.QPS = defaultQPSLimit - config.Burst = defaultBurstLimit - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - apiextClientset, err := apiextv1clientset.NewForConfig(config) - if err != nil { - return nil, err - } - dynamicClientset, err := dynamic.NewForConfig(config) - if err != nil { - return nil, err - } - olmClientset, err := olmVersioned.NewForConfig(config) - if err != nil { - return nil, err - } - - c := &Client{ - l: l, - clientset: clientset, - apiextClientset: apiextClientset, - dynamicClientset: dynamicClientset, - olmClientset: olmClientset, - restConfig: config, - rcLock: &sync.Mutex{}, - clusterName: clientConfig.Contexts[clientConfig.CurrentContext].Cluster, - } - err = c.setup() - return c, err -} - -func (c *Client) setup() error { - namespace := "default" - if space := os.Getenv("NAMESPACE"); space != "" { - namespace = space - } - c.namespace = namespace - return c.initOperatorClients() -} - -// NewInCluster creates a client using incluster authentication. -func NewInCluster() (*Client, error) { - config, err := rest.InClusterConfig() - if err != nil { - return nil, err - } - config.QPS = defaultQPSLimit - config.Burst = defaultBurstLimit - config.Timeout = requestTimeout - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - namespace, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") - if err != nil { - return nil, err - } - - olmClientset, err := olmVersioned.NewForConfig(config) - if err != nil { - return nil, err - } - - c := &Client{ - clientset: clientset, - olmClientset: olmClientset, - restConfig: config, - rcLock: &sync.Mutex{}, - namespace: string(namespace), - } - - err = c.initOperatorClients() - return c, err -} - -// NewFromFakeClient returns a Client with a fake (in-memory) clientset. -// This is used only for unit testing. -func NewFromFakeClient() *Client { - return &Client{ - clientset: fake.NewSimpleClientset(), - } -} - -func (c *Client) kubeClient() (client.Client, error) { //nolint:ireturn,nolintlint - rcl, err := rest.HTTPClientFor(c.restConfig) - if err != nil { - return nil, err - } - - rm, err := apiutil.NewDynamicRESTMapper(c.restConfig, rcl) - if err != nil { - return nil, errors.Join(err, errors.New("failed to create dynamic rest mapper")) - } - - cl, err := client.New(c.restConfig, client.Options{ - Scheme: scheme.Scheme, - Mapper: rm, - }) - if err != nil { - return nil, errors.Join(err, errors.New("failed to create client")) - } - return cl, nil -} - -// Initializes clients for operators. -func (c *Client) initOperatorClients() error { - customClient, err := customresources.NewForConfig(c.restConfig) - if err != nil { - return err - } - c.customClientSet = customClient - _, err = c.GetServerVersion() - if err != nil { - return err - } - - return nil -} - -// Config returns restConfig to the pkg/kubernetes.Kubernetes client. -func (c *Client) Config() *rest.Config { - return c.restConfig -} - -// Clientset returns the k8s clientset. -// -//nolint:ireturn -func (c *Client) Clientset() kubernetes.Interface { - return c.clientset -} - -// ClusterName returns the name of the k8s cluster. -func (c *Client) ClusterName() string { - return c.clusterName -} - -// Namespace returns the namespace of the k8s cluster. -func (c *Client) Namespace() string { - return c.namespace -} - -// GetSecretsForServiceAccount returns secret by given service account name. -func (c *Client) GetSecretsForServiceAccount(ctx context.Context, accountName string) (*corev1.Secret, error) { - serviceAccount, err := c.clientset.CoreV1().ServiceAccounts(c.namespace).Get(ctx, accountName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - if len(serviceAccount.Secrets) == 0 { - return nil, fmt.Errorf("no secrets available for namespace %s", c.namespace) - } - - return c.clientset.CoreV1().Secrets(c.namespace).Get( - ctx, - serviceAccount.Secrets[0].Name, - metav1.GetOptions{}, - ) -} - -// GenerateKubeConfigWithToken generates kubeconfig with a user and token provided as a secret. -func (c *Client) GenerateKubeConfigWithToken(user string, secret *corev1.Secret) ([]byte, error) { - conf := &Config{ - Kind: configKind, - APIVersion: apiVersion, - CurrentContext: defaultName, - } - conf.Clusters = []ClusterInfo{ - { - Name: defaultName, - Cluster: Cluster{ - CertificateAuthorityData: secret.Data["ca.crt"], - Server: c.restConfig.Host, - }, - }, - } - conf.Contexts = []ContextInfo{ - { - Name: defaultName, - Context: Context{ - Cluster: defaultName, - User: user, - Namespace: defaultName, - }, - }, - } - conf.Users = []UserInfo{ - { - Name: user, - User: User{ - Token: string(secret.Data["token"]), - }, - }, - } - - return c.marshalKubeConfig(conf) -} - -// GetServerVersion returns server version. -func (c *Client) GetServerVersion() (*version.Info, error) { - return c.clientset.Discovery().ServerVersion() -} - -// ApplyObject applies object. -func (c *Client) ApplyObject(obj runtime.Object) error { - // Instantiate a new restmapper so we discover any new resources before applying object. - groupResources, err := restmapper.GetAPIGroupResources(c.clientset.Discovery()) - if err != nil { - return err - } - mapper := restmapper.NewDiscoveryRESTMapper(groupResources) - - gvk := obj.GetObjectKind().GroupVersionKind() - gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind} - mapping, err := mapper.RESTMapping(gk, gvk.Version) - if err != nil { - return err - } - namespace, name, err := c.retrieveMetaFromObject(obj) - if err != nil { - return err - } - cli, err := c.resourceClient(mapping.GroupVersionKind.GroupVersion()) - if err != nil { - return err - } - helper := resource.NewHelper(cli, mapping) - return c.applyObject(helper, namespace, name, obj) -} - -func (c *Client) applyObject(helper *resource.Helper, namespace, name string, obj runtime.Object) error { - if _, err := helper.Get(namespace, name); err != nil { - _, err = helper.Create(namespace, false, obj) - if err != nil { - return err - } - } else { - _, err = helper.Replace(namespace, name, true, obj) - if err != nil { - return err - } - } - return nil -} - -func (c *Client) retrieveMetaFromObject(obj runtime.Object) (string, string, error) { - name, err := meta.NewAccessor().Name(obj) - if err != nil { - return "", name, err - } - namespace, err := meta.NewAccessor().Namespace(obj) - if err != nil { - return namespace, name, err - } - if namespace == "" { - namespace = c.namespace - } - return namespace, name, nil -} - -func (c *Client) resourceClient( //nolint:ireturn,nolintlint - gv schema.GroupVersion, -) (rest.Interface, error) { - cfg := c.restConfig - cfg.ContentConfig = resource.UnstructuredPlusDefaultContentConfig() - cfg.GroupVersion = &gv - if len(gv.Group) == 0 { - cfg.APIPath = defaultAPIURIPath - } else { - cfg.APIPath = defaultAPIsURIPath - } - return rest.RESTClientFor(cfg) -} - -// DeleteObject deletes object from the k8s cluster. -func (c *Client) DeleteObject(obj runtime.Object) error { - groupResources, err := restmapper.GetAPIGroupResources(c.clientset.Discovery()) - if err != nil { - return err - } - mapper := restmapper.NewDiscoveryRESTMapper(groupResources) - - gvk := obj.GetObjectKind().GroupVersionKind() - gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind} - mapping, err := mapper.RESTMapping(gk, gvk.Version) - if err != nil { - return err - } - namespace, name, err := c.retrieveMetaFromObject(obj) - if err != nil { - return err - } - cli, err := c.resourceClient(mapping.GroupVersionKind.GroupVersion()) - if err != nil { - return err - } - helper := resource.NewHelper(cli, mapping) - err = deleteObject(helper, namespace, name) - return err -} - -func deleteObject(helper *resource.Helper, namespace, name string) error { - if _, err := helper.Get(namespace, name); err == nil { - _, err = helper.Delete(namespace, name) - if client.IgnoreNotFound(err) != nil { - return err - } - } - return nil -} - -// ListObjects lists objects by provided group, version, kind. -func (c *Client) ListObjects(gvk schema.GroupVersionKind, into runtime.Object) error { - helper, err := c.helperForGVK(gvk) - if err != nil { - return errors.Join(err, errors.New("could not create helper")) - } - - l, err := helper.List(c.namespace, gvk.Version, &metav1.ListOptions{}) - if err != nil { - return err - } - - u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(l) - if err != nil { - return err - } - - return runtime.DefaultUnstructuredConverter.FromUnstructured(u, into) -} - -// GetObject retrieves an object by provided group, version, kind and name. -func (c *Client) GetObject(gvk schema.GroupVersionKind, name string, into runtime.Object) error { - helper, err := c.helperForGVK(gvk) - if err != nil { - return errors.Join(err, errors.New("could not create helper")) - } - - l, err := helper.Get(c.namespace, name) - if err != nil { - return errors.Join(err, errors.New("failed to get object using helper")) - } - - u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(l) - if err != nil { - return errors.Join(err, errors.New("failed to convert object to unstructured")) - } - - return runtime.DefaultUnstructuredConverter.FromUnstructured(u, into) -} - -func (c *Client) helperForGVK(gvk schema.GroupVersionKind) (*resource.Helper, error) { - groupResources, err := restmapper.GetAPIGroupResources(c.clientset.Discovery()) - if err != nil { - return nil, err - } - mapper := restmapper.NewDiscoveryRESTMapper(groupResources) - - gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind} - mapping, err := mapper.RESTMapping(gk, gvk.Version) - if err != nil { - return nil, err - } - cli, err := c.resourceClient(mapping.GroupVersionKind.GroupVersion()) - if err != nil { - return nil, err - } - - return resource.NewHelper(cli, mapping), nil -} - -func (c *Client) marshalKubeConfig(conf *Config) ([]byte, error) { - config, err := json.Marshal(&conf) - if err != nil { - return nil, err - } - - var jsonObj interface{} - err = yaml.Unmarshal(config, &jsonObj) - if err != nil { - return nil, err - } - - return yaml.Marshal(jsonObj) -} - -// GetLogs returns logs for pod. -func (c *Client) GetLogs(ctx context.Context, pod, container string) (string, error) { - defaultLogLines := int64(defaultLogLines) - options := &corev1.PodLogOptions{} - if container != "" { - options.Container = container - } - - options.TailLines = &defaultLogLines - buf := &bytes.Buffer{} - - req := c.clientset.CoreV1().Pods(c.namespace).GetLogs(pod, options) - podLogs, err := req.Stream(ctx) - if err != nil { - return buf.String(), err - } - - _, err = io.Copy(buf, podLogs) - if err != nil { - return buf.String(), err - } - - return buf.String(), nil -} - -// GetEvents retrieves events from a pod by a name. -func (c *Client) GetEvents(ctx context.Context, name string) (string, error) { - pod, err := c.clientset.CoreV1().Pods(c.namespace).Get(ctx, name, metav1.GetOptions{}) - if err != nil { - eventsInterface := c.clientset.CoreV1().Events(c.namespace) - selector := eventsInterface.GetFieldSelector(&name, &c.namespace, nil, nil) - initialOpts := metav1.ListOptions{ - FieldSelector: selector.String(), - Limit: defaultChunkSize, - } - events := &corev1.EventList{} - err2 := resource.FollowContinue(&initialOpts, - func(options metav1.ListOptions) (runtime.Object, error) { - newList, err := eventsInterface.List(ctx, options) - if err != nil { - return nil, resource.EnhanceListError(err, options, "events") - } - - events.Items = append(events.Items, newList.Items...) - return newList, nil - }) - - if err2 == nil && len(events.Items) > 0 { - return tabbedString(func(out io.Writer) error { - w := NewPrefixWriter(out) - w.Writef(0, "Pod '%v': error '%v', but found events.\n", name, err) - DescribeEvents(events, w) - return nil - }) - } - - return "", err - } - - var events *corev1.EventList - if ref, err := reference.GetReference(scheme.Scheme, pod); err != nil { - c.l.Warnf("Unable to construct reference to '%#v': %v", pod, err) - } else { - ref.Kind = "" - if _, isMirrorPod := pod.Annotations[corev1.MirrorPodAnnotationKey]; isMirrorPod { - ref.UID = types.UID(pod.Annotations[corev1.MirrorPodAnnotationKey]) - } - - events, _ = searchEvents(ctx, c.clientset.CoreV1(), ref, defaultChunkSize) - } - - return tabbedString(func(out io.Writer) error { - w := NewPrefixWriter(out) - w.Writef(LEVEL0, name+" ") - DescribeEvents(events, w) - return nil - }) -} - -func tabbedString(f func(io.Writer) error) (string, error) { - out := &tabwriter.Writer{} - buf := &bytes.Buffer{} - out.Init(buf, 0, 8, 2, ' ', 0) //nolint:mnd - - if err := f(out); err != nil { - return "", err - } - - if err := out.Flush(); err != nil { - return "", err - } - - str := buf.String() - return str, nil -} - -// DescribeEvents describes events. -func DescribeEvents(el *corev1.EventList, w PrefixWriter) { - if len(el.Items) == 0 { - w.Writef(LEVEL0, "Events:\t\n") - return - } - - w.Flush() - sort.Sort(SortableEvents(el.Items)) - w.Writef(LEVEL0, "Events:\n Type\tReason\tAge\tFrom\tMessage\n") - w.Writef(LEVEL1, "----\t------\t----\t----\t-------\n") - for _, e := range el.Items { - var interval string - firstTimestampSince := translateMicroTimestampSince(e.EventTime) - if e.EventTime.IsZero() { - firstTimestampSince = translateTimestampSince(e.FirstTimestamp) - } - - switch { - case e.Series != nil: - interval = fmt.Sprintf( - "%s (x%d over %s)", - translateMicroTimestampSince(e.Series.LastObservedTime), - e.Series.Count, - firstTimestampSince, - ) - case e.Count > 1: - interval = fmt.Sprintf("%s (x%d over %s)", translateTimestampSince(e.LastTimestamp), e.Count, firstTimestampSince) - default: - interval = firstTimestampSince - } - - source := e.Source.Component - if source == "" { - source = e.ReportingController - } - - w.Writef(LEVEL1, "%v\t%v\t%s\t%v\t%v\n", - e.Type, - e.Reason, - interval, - source, - strings.TrimSpace(e.Message), - ) - } -} - -// searchEvents finds events about the specified object. -// It is very similar to CoreV1.Events.Search, but supports the Limit parameter. -func searchEvents( - ctx context.Context, - client corev1client.EventsGetter, - objOrRef runtime.Object, - limit int64, -) (*corev1.EventList, error) { - ref, err := reference.GetReference(scheme.Scheme, objOrRef) - if err != nil { - return nil, err - } - - var refKind *string - if stringRefKind := ref.Kind; len(stringRefKind) > 0 { - refKind = &stringRefKind - } - - stringRefUID := string(ref.UID) - var refUID *string - if len(stringRefUID) > 0 { - refUID = &stringRefUID - } - - e := client.Events(ref.Namespace) - fieldSelector := e.GetFieldSelector(&ref.Name, &ref.Namespace, refKind, refUID) - initialOpts := metav1.ListOptions{FieldSelector: fieldSelector.String(), Limit: limit} - eventList := &corev1.EventList{} - err = resource.FollowContinue(&initialOpts, - func(options metav1.ListOptions) (runtime.Object, error) { - newEvents, err := e.List(ctx, options) - if err != nil { - return nil, resource.EnhanceListError(err, options, "events") - } - - eventList.Items = append(eventList.Items, newEvents.Items...) - return newEvents, nil - }) - - return eventList, err -} - -// translateMicroTimestampSince returns the elapsed time since timestamp in -// human-readable approximation. -func translateMicroTimestampSince(timestamp metav1.MicroTime) string { - if timestamp.IsZero() { - return "" - } - - return duration.HumanDuration(time.Since(timestamp.Time)) -} - -// translateTimestampSince returns the elapsed time since timestamp in -// human-readable approximation. -func translateTimestampSince(timestamp metav1.Time) string { - if timestamp.IsZero() { - return "" - } - - return duration.HumanDuration(time.Since(timestamp.Time)) -} - -// ApplyFile accepts manifest file contents, parses into []runtime.Object -// and applies them against the cluster. -func (c *Client) ApplyFile(fileBytes []byte) error { - objs, err := c.getObjects(fileBytes) - if err != nil { - return err - } - for i := range objs { - err := c.ApplyObject(objs[i]) - if err != nil { - return err - } - } - return nil -} - -// ApplyManifestFile accepts manifest file contents, parses into []runtime.Object -// and applies them against the cluster. -func (c *Client) ApplyManifestFile(fileBytes []byte, namespace string, ignoreObjects ...client.Object) error { - objs, err := c.getObjects(fileBytes) - if err != nil { - return err - } - for i := range objs { - o := objs[i] - - // Check if this object should be ignored? - if slices.ContainsFunc(ignoreObjects, func(ign client.Object) bool { - return o.GetKind() == ign.GetObjectKind().GroupVersionKind().Kind && - o.GetName() == ign.GetName() && - ign.GetNamespace() == namespace - }) { - continue - } - - if err := c.applyTemplateCustomization(o, namespace); err != nil { - return err - } - err := c.ApplyObject(o) - if err != nil { - return err - } - } - return nil -} - -// DeleteManifestFile accepts manifest file contents, parses into []runtime.Object -// and deletes them from the cluster. -func (c *Client) DeleteManifestFile(fileBytes []byte, namespace string) error { - objs, err := c.getObjects(fileBytes) - if err != nil { - return err - } - for i := range objs { - o := objs[i] - if err := c.applyTemplateCustomization(o, namespace); err != nil { - return err - } - err := c.DeleteObject(o) - if err != nil { - return err - } - } - return nil -} - -func (c *Client) applyTemplateCustomization(u *unstructured.Unstructured, namespace string) error { - if err := unstructured.SetNestedField(u.Object, namespace, "metadata", "namespace"); err != nil { - return err - } - - kind, ok, err := unstructured.NestedString(u.Object, "kind") - if err != nil { - return err - } - - if ok && kind == "ClusterRoleBinding" { - if err := c.updateClusterRoleBinding(u, namespace); err != nil { - return err - } - } - if ok && kind == "Service" { - // During installation or upgrading of the everest API Server - // CLI should keep spec.type untouched to prevent overriding of it. - if err := c.setEverestServiceType(u, namespace); err != nil { - return err - } - } - - return nil -} - -func (c *Client) setEverestServiceType(u *unstructured.Unstructured, namespace string) error { - s, err := c.GetService(context.Background(), namespace, "everest") - if err != nil && !apierrors.IsNotFound(err) { - return err - } - if err != nil && apierrors.IsNotFound(err) { - return nil - } - if s != nil && s.Name != "" { - if err := unstructured.SetNestedField(u.Object, string(s.Spec.Type), "spec", "type"); err != nil { - return err - } - } - return nil -} - -func (c *Client) updateClusterRoleBinding(u *unstructured.Unstructured, namespace string) error { - sub, ok, err := unstructured.NestedFieldNoCopy(u.Object, "subjects") - if err != nil { - return err - } - - if !ok { - return nil - } - - subjects, ok := sub.([]interface{}) - if !ok { - return nil - } - - for _, s := range subjects { - sub, ok := s.(map[string]interface{}) - if !ok { - continue - } - - if err := unstructured.SetNestedField(sub, namespace, "namespace"); err != nil { - return err - } - } - return unstructured.SetNestedSlice(u.Object, subjects, "subjects") -} - -func (c *Client) getObjects(f []byte) ([]*unstructured.Unstructured, error) { - objs := []*unstructured.Unstructured{} - decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(f), objectsBufferSize) - var err error - for { - var rawObj runtime.RawExtension - if err = decoder.Decode(&rawObj); err != nil { - break - } - - obj, _, err := yamlSerializer.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil) - if err != nil { - return nil, err - } - - unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - return nil, err - } - - objs = append(objs, &unstructured.Unstructured{Object: unstructuredMap}) - } - - return objs, nil //nolint:nilerr -} - -// DoCSVWait waits until for a CSV to be applied. -func (c Client) DoCSVWait(ctx context.Context, key types.NamespacedName) error { - kubeclient, err := c.getKubeclient() - if err != nil { - return err - } - - return c.pollCsvPhaseSucceeded(ctx, key, kubeclient) -} - -func (c Client) pollCsvPhaseSucceeded(ctx context.Context, key types.NamespacedName, kubeclient client.Client) error { - var ( - curPhase v1alpha1.ClusterServiceVersionPhase - newPhase v1alpha1.ClusterServiceVersionPhase - ) - - csv := v1alpha1.ClusterServiceVersion{} - csvPhaseSucceeded := func(ctx context.Context) (bool, error) { - err := kubeclient.Get(ctx, key, &csv) - if err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } - return false, err - } - newPhase = csv.Status.Phase - if newPhase != curPhase { - curPhase = newPhase - } - - //nolint:exhaustive - switch curPhase { - case v1alpha1.CSVPhaseFailed: - return false, fmt.Errorf("csv failed: reason: %q, message: %q", csv.Status.Reason, csv.Status.Message) - case v1alpha1.CSVPhaseSucceeded: - return true, nil - default: - return false, nil - } - } - - err := wait.PollUntilContextCancel(ctx, time.Second, true, csvPhaseSucceeded) - if err != nil && errors.Is(err, context.DeadlineExceeded) { - depCheckErr := c.checkDeploymentErrors(ctx, key, csv) - if depCheckErr != nil { - return depCheckErr - } - } - return err -} - -// GetSubscriptionCSV retrieves a subscription CSV. -func (c Client) GetSubscriptionCSV(ctx context.Context, subKey types.NamespacedName) (types.NamespacedName, error) { - var csvKey types.NamespacedName - - kubeclient, err := c.getKubeclient() - if err != nil { - return csvKey, err - } - - subscriptionInstalledCSV := func(ctx context.Context) (bool, error) { - sub := v1alpha1.Subscription{} - err := kubeclient.Get(ctx, subKey, &sub) - if err != nil { - return false, err - } - installedCSV := sub.Status.InstalledCSV - if installedCSV == "" { - return false, nil - } - csvKey = types.NamespacedName{ - Namespace: subKey.Namespace, - Name: installedCSV, - } - log.Printf(" Found installed CSV %q", installedCSV) - return true, nil - } - return csvKey, wait.PollUntilContextCancel(ctx, time.Second, true, subscriptionInstalledCSV) -} - -func (c *Client) getKubeclient() (client.Client, error) { //nolint:ireturn,nolintlint - rcl, err := rest.HTTPClientFor(c.restConfig) - if err != nil { - return nil, err - } - - rm, err := apiutil.NewDynamicRESTMapper(c.restConfig, rcl) - if err != nil { - return nil, errors.Join(err, errors.New("failed to create dynamic rest mapper")) - } - - cl, err := client.New(c.restConfig, client.Options{ - Scheme: scheme.Scheme, - Mapper: rm, - }) - if err != nil { - return nil, errors.Join(err, errors.New("failed to create client")) - } - return cl, nil -} - -// checkDeploymentErrors function loops through deployment specs of a given CSV, and prints reason -// in case of failures, based on deployment condition. -func (c Client) checkDeploymentErrors( - ctx context.Context, - key types.NamespacedName, - csv v1alpha1.ClusterServiceVersion, -) error { - depErrs := deploymentErrors{} - if key.Namespace == "" { - return errors.New("no namespace provided to get deployment failures") - } - - kubeclient, err := c.getKubeclient() - if err != nil { - return err - } - - dep := &appsv1.Deployment{} - for _, ds := range csv.Spec.InstallStrategy.StrategySpec.DeploymentSpecs { - depKey := types.NamespacedName{ - Namespace: key.Namespace, - Name: ds.Name, - } - depSelectors := ds.Spec.Selector - if err := kubeclient.Get(ctx, depKey, dep); err != nil { - depErrs = append(depErrs, deploymentError{ - resourceError: resourceError{ - name: ds.Name, - issue: err.Error(), - }, - }) - continue - } - for _, s := range dep.Status.Conditions { - if s.Type == appsv1.DeploymentAvailable && s.Status != corev1.ConditionTrue { - depErr := deploymentError{ - resourceError: resourceError{ - name: ds.Name, - issue: s.Reason, - }, - } - podErr := c.checkPodErrors(ctx, kubeclient, depSelectors, key) - podErrs := podErrors{} - if errors.As(podErr, &podErrs) { - depErr.podErrs = append(depErr.podErrs, podErrs...) - } else { - return podErr - } - depErrs = append(depErrs, depErr) - } - } - } - - return depErrs -} - -// checkPodErrors loops through pods, and returns pod errors if any. -func (c Client) checkPodErrors( - ctx context.Context, - kubeclient client.Client, - depSelectors *metav1.LabelSelector, - key types.NamespacedName, -) error { - // loop through pods and return specific error message. - podErr := podErrors{} - podList := &corev1.PodList{} - podLabelSelectors, err := metav1.LabelSelectorAsSelector(depSelectors) - if err != nil { - return err - } - - options := client.ListOptions{ - LabelSelector: podLabelSelectors, - Namespace: key.Namespace, - } - - if err := kubeclient.List(ctx, podList, &options); err != nil { - return errors.Join(err, errors.New("error getting Pods")) - } - - for _, p := range podList.Items { - for _, cs := range p.Status.ContainerStatuses { - if !cs.Ready { - if cs.State.Waiting != nil { - containerName := p.Name + ":" + cs.Name - podErr = append(podErr, podError{resourceError{name: containerName, issue: cs.State.Waiting.Message}}) - } - } - } - } - - return podErr -} - -// DoRolloutWait waits until a deployment has been rolled out susccessfully or there is an error. -func (c Client) DoRolloutWait(ctx context.Context, key types.NamespacedName) error { - kubeclient, err := c.getKubeclient() - if err != nil { - return err - } - - return c.pollRolloutComplete(ctx, key, kubeclient) -} - -func (c Client) pollRolloutComplete(ctx context.Context, key types.NamespacedName, kubeclient client.Client) error { - rolloutComplete := func(ctx context.Context) (bool, error) { - deployment := appsv1.Deployment{} - err := kubeclient.Get(ctx, key, &deployment) - if err != nil { - if apierrors.IsNotFound(err) { - // Waiting for Deployment to appear - return false, nil - } - return false, err - } - if deployment.Generation <= deployment.Status.ObservedGeneration { - cond := deploymentutil.GetDeploymentCondition(deployment.Status, appsv1.DeploymentProgressing) - if cond != nil && cond.Reason == deploymentutil.TimedOutReason { - return false, errors.New("progress deadline exceeded") - } - if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { - // Waiting for Deployment to rollout. Not all replicas have been updated - return false, nil - } - if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { - // Waiting for Deployment to rollout. Old replicas are pending termination - return false, nil - } - if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { - // Waiting for Deployment to rollout. Not all updated replicas are available - return false, nil - } - // Deployment successfully rolled out - return true, nil - } - // Waiting for Deployment to rollout: waiting for deployment spec update to be observed - return false, nil - } - return wait.PollUntilContextCancel(ctx, time.Second, true, rolloutComplete) -} - -// GetOperatorGroup retrieves an operator group details by namespace and name. -func (c *Client) GetOperatorGroup(ctx context.Context, namespace, name string) (*v1.OperatorGroup, error) { - operatorClient, err := olmVersioned.NewForConfig(c.restConfig) - if err != nil { - return nil, errors.Join(err, errors.New("cannot create an operator client instance")) - } - - if namespace == "" { - namespace = c.namespace - } - - return operatorClient.OperatorsV1().OperatorGroups(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// CreateOperatorGroup creates an operator group to be used as part of a subscription. -func (c *Client) CreateOperatorGroup(ctx context.Context, namespace, name string, targetNamespaces []string) (*v1.OperatorGroup, error) { - if namespace == "" { - namespace = c.namespace - } - og := &v1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1.OperatorGroupSpec{ - TargetNamespaces: targetNamespaces, - }, - Status: v1.OperatorGroupStatus{ - LastUpdated: &metav1.Time{ - Time: time.Now(), - }, - }, - } - - return c.olmClientset.OperatorsV1().OperatorGroups(namespace).Create(ctx, og, metav1.CreateOptions{}) -} - -// CreateSubscription creates an OLM subscription. -func (c *Client) CreateSubscription(ctx context.Context, namespace string, subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) { - sub, err := c.olmClientset. - OperatorsV1alpha1(). - Subscriptions(namespace). - Create(ctx, subscription, metav1.CreateOptions{}) - if err != nil { - if apierrors.IsAlreadyExists(err) { - return sub, nil - } - return sub, err - } - return sub, nil -} - -// UpdateSubscription updates an OLM subscription. -func (c *Client) UpdateSubscription(ctx context.Context, namespace string, subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) { - sub, err := c.olmClientset. - OperatorsV1alpha1(). - Subscriptions(namespace). - Update(ctx, subscription, metav1.UpdateOptions{}) - if err != nil { - return sub, err - } - return sub, nil -} - -// CreateSubscriptionForCatalog creates an OLM subscription. -func (c *Client) CreateSubscriptionForCatalog(ctx context.Context, namespace, name, catalogNamespace, catalog, - packageName, channel, startingCSV string, approval v1alpha1.Approval, -) (*v1alpha1.Subscription, error) { - subscription := &v1alpha1.Subscription{ - TypeMeta: metav1.TypeMeta{ - Kind: v1alpha1.SubscriptionKind, - APIVersion: v1alpha1.SubscriptionCRDAPIVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Spec: &v1alpha1.SubscriptionSpec{ - CatalogSource: catalog, - CatalogSourceNamespace: catalogNamespace, - Package: packageName, - Channel: channel, - StartingCSV: startingCSV, - InstallPlanApproval: approval, - }, - } - sub, err := c.olmClientset. - OperatorsV1alpha1(). - Subscriptions(namespace). - Create(ctx, subscription, metav1.CreateOptions{}) - if err != nil { - if apierrors.IsAlreadyExists(err) { - return sub, nil - } - return sub, err - } - return sub, nil -} - -// GetSubscription retrieves an OLM subscription by namespace and name. -func (c *Client) GetSubscription(ctx context.Context, namespace, name string) (*v1alpha1.Subscription, error) { - c.rcLock.Lock() - defer c.rcLock.Unlock() - - return c.olmClientset.OperatorsV1alpha1().Subscriptions(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListSubscriptions all the subscriptions in the namespace. -func (c *Client) ListSubscriptions(ctx context.Context, namespace string) (*v1alpha1.SubscriptionList, error) { - c.rcLock.Lock() - defer c.rcLock.Unlock() - - return c.olmClientset.OperatorsV1alpha1().Subscriptions(namespace).List(ctx, metav1.ListOptions{}) -} - -// DoPackageWait for the package to be available in OLM. -func (c *Client) DoPackageWait(ctx context.Context, namespace, name string) error { - packageInstalled := func(ctx context.Context) (bool, error) { - _, err := c.GetPackageManifest(ctx, namespace, name) - if err != nil { - if apierrors.ReasonForError(err) == metav1.StatusReasonUnknown { - return false, err - } - return false, nil - } - return true, nil - } - return wait.PollUntilContextCancel(ctx, time.Second, true, packageInstalled) -} - -// GetPackageManifest returns a package manifest by given name. -func (c *Client) GetPackageManifest(ctx context.Context, namespace, name string) (*packagev1.PackageManifest, error) { - operatorClient, err := packageServerClient.NewForConfig(c.restConfig) - if err != nil { - return nil, errors.Join(err, errors.New("cannot create an operator client instance")) - } - - return operatorClient.OperatorsV1().PackageManifests(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListCRDs returns a list of CRDs. -func (c *Client) ListCRDs( - ctx context.Context, - labelSelector *metav1.LabelSelector, -) (*apiextv1.CustomResourceDefinitionList, error) { - options := metav1.ListOptions{} - if labelSelector != nil && (labelSelector.MatchLabels != nil || labelSelector.MatchExpressions != nil) { - options.LabelSelector = metav1.FormatLabelSelector(labelSelector) - } - - return c.apiextClientset.ApiextensionsV1().CustomResourceDefinitions().List(ctx, options) -} - -// DeleteCRD deletes a CRD by name. -func (c *Client) DeleteCRD( - ctx context.Context, - name string, -) error { - return c.apiextClientset.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, name, metav1.DeleteOptions{}) -} - -// ListCRs returns a list of CRs. -func (c *Client) ListCRs( - ctx context.Context, - namespace string, - gvr schema.GroupVersionResource, - labelSelector *metav1.LabelSelector, -) (*unstructured.UnstructuredList, error) { - options := metav1.ListOptions{} - if labelSelector != nil && (labelSelector.MatchLabels != nil || labelSelector.MatchExpressions != nil) { - options.LabelSelector = metav1.FormatLabelSelector(labelSelector) - } - - return c.dynamicClientset.Resource(gvr).Namespace(namespace).List(ctx, options) -} - -// GetClusterServiceVersion retrieve a CSV by namespaced name. -func (c *Client) GetClusterServiceVersion( - ctx context.Context, - key types.NamespacedName, -) (*v1alpha1.ClusterServiceVersion, error) { - return c.olmClientset.OperatorsV1alpha1().ClusterServiceVersions(key.Namespace).Get(ctx, key.Name, metav1.GetOptions{}) -} - -// ListClusterServiceVersion list all CSVs for the given namespace. -func (c *Client) ListClusterServiceVersion( - ctx context.Context, - namespace string, -) (*v1alpha1.ClusterServiceVersionList, error) { - return c.olmClientset.OperatorsV1alpha1().ClusterServiceVersions(namespace).List(ctx, metav1.ListOptions{}) -} - -// UpdateClusterServiceVersion updates a CSV and returns the updated CSV. -func (c *Client) UpdateClusterServiceVersion( - ctx context.Context, - csv *v1alpha1.ClusterServiceVersion, -) (*v1alpha1.ClusterServiceVersion, error) { - return c.olmClientset.OperatorsV1alpha1().ClusterServiceVersions(csv.Namespace).Update(ctx, csv, metav1.UpdateOptions{}) -} - -// DeleteClusterServiceVersion deletes a CSV by namespaced name. -func (c *Client) DeleteClusterServiceVersion( - ctx context.Context, - key types.NamespacedName, -) error { - return c.olmClientset.OperatorsV1alpha1().ClusterServiceVersions(key.Namespace).Delete(ctx, key.Name, metav1.DeleteOptions{}) -} - -// DeleteSubscription deletes a subscription by namespaced name. -func (c *Client) DeleteSubscription( - ctx context.Context, - key types.NamespacedName, -) error { - return c.olmClientset.OperatorsV1alpha1().Subscriptions(key.Namespace).Delete(ctx, key.Name, metav1.DeleteOptions{}) -} - -// DeleteFile accepts manifest file contents parses into []runtime.Object -// and deletes them from the cluster. -func (c *Client) DeleteFile(fileBytes []byte) error { - objs, err := c.getObjects(fileBytes) - if err != nil { - return err - } - for i := range objs { - err := c.DeleteObject(objs[i]) - if err != nil { - return err - } - } - return nil -} - -// GetService returns k8s service by provided namespace and name. -func (c *Client) GetService(ctx context.Context, namespace, name string) (*corev1.Service, error) { - return c.clientset.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// GetClusterRoleBinding returns cluster role binding by given name. -func (c *Client) GetClusterRoleBinding(ctx context.Context, name string) (*rbacv1.ClusterRoleBinding, error) { - return c.clientset.RbacV1().ClusterRoleBindings().Get(ctx, name, metav1.GetOptions{}) -} diff --git a/pkg/kubernetes/client/client_test.go b/pkg/kubernetes/client/client_test.go deleted file mode 100644 index 40b63d509..000000000 --- a/pkg/kubernetes/client/client_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/version" - fake "k8s.io/client-go/kubernetes/fake" -) - -func TestGetServerVersion(t *testing.T) { - t.Parallel() - clientset := fake.NewSimpleClientset() - client := &Client{clientset: clientset, namespace: "default"} - ver, err := client.GetServerVersion() - expectedVersion := &version.Info{} - require.NoError(t, err) - assert.Equal(t, expectedVersion.Minor, ver.Minor) -} diff --git a/pkg/kubernetes/client/configmap.go b/pkg/kubernetes/client/configmap.go deleted file mode 100644 index 97dd96f7e..000000000 --- a/pkg/kubernetes/client/configmap.go +++ /dev/null @@ -1,23 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetConfigMap returns config map by name and namespace. -func (c *Client) GetConfigMap(ctx context.Context, namespace, name string) (*corev1.ConfigMap, error) { - return c.clientset.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// CreateConfigMap creates the provided ConfigMap. -func (c *Client) CreateConfigMap(ctx context.Context, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error) { - return c.clientset.CoreV1().ConfigMaps(configMap.Namespace).Create(ctx, configMap, metav1.CreateOptions{}) -} - -// UpdateConfigMap updates the provided ConfigMap. -func (c *Client) UpdateConfigMap(ctx context.Context, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error) { - return c.clientset.CoreV1().ConfigMaps(configMap.Namespace).Update(ctx, configMap, metav1.UpdateOptions{}) -} diff --git a/pkg/kubernetes/client/ctl.go b/pkg/kubernetes/client/ctl.go deleted file mode 100644 index 52a23d507..000000000 --- a/pkg/kubernetes/client/ctl.go +++ /dev/null @@ -1,66 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client - -type ( - // Cluster contains information about how to communicate with a kubernetes cluster. - Cluster struct { - CertificateAuthorityData []byte `json:"certificate-authority-data"` //nolint:tagliatelle - Server string `json:"server"` - } - // ClusterInfo is a struct used to parse Cluster config from kubeconfig. - ClusterInfo struct { - Name string `json:"name"` - Cluster Cluster `json:"cluster"` - } - // User contains information that describes identity information. - // This is use to tell the kubernetes cluster who you are. - User struct { - Token string `json:"token"` - } - // UserInfo is a struct used to parse User config from kubeconfig. - UserInfo struct { - Name string `json:"name"` - User User `json:"user"` - } - // Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), - // a user (how do I identify myself), and a namespace (what subset of resources do I want to work with). - Context struct { - Cluster string `json:"cluster"` - User string `json:"user"` - Namespace string `json:"namespace"` - } - // ContextInfo is a struct used to parse Context config from kubeconfig. - ContextInfo struct { - Name string `json:"name"` - Context Context `json:"context"` - } - // Config holds the information needed to build connect to remote kubernetes clusters as a given user. - Config struct { - // Legacy field from pkg/api/types.go TypeMeta. - Kind string `json:"kind,omitempty"` - // Legacy field from pkg/api/types.go TypeMeta. - APIVersion string `json:"apiVersion,omitempty"` - // Preferences holds general information to be use for cli interactions - Clusters []ClusterInfo `json:"clusters"` - // AuthInfos is a map of referencable names to user configs - Users []UserInfo `json:"users"` - // Contexts is a map of referencable names to context configs - Contexts []ContextInfo `json:"contexts"` - // CurrentContext is the name of the context that you would like to use by default - CurrentContext string `json:"current-context"` //nolint:tagliatelle - } -) diff --git a/pkg/kubernetes/client/customresources/backupstorages.go b/pkg/kubernetes/client/customresources/backupstorages.go deleted file mode 100644 index 301f06c6f..000000000 --- a/pkg/kubernetes/client/customresources/backupstorages.go +++ /dev/null @@ -1,137 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources provides methods to work with custom everest k8s resources. -// -//nolint:dupl -package customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - backupStorageAPIKind = "backupstorages" -) - -// BackupStorage returns a db cluster client. -func (c *Client) BackupStorage( //nolint:ireturn - namespace string, -) BackupStoragesInterface { - return &client{ - restClient: c.restClient, - namespace: namespace, - } -} - -// BackupStoragesInterface supports methods to work with BackupStorages. -type BackupStoragesInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.BackupStorageList, error) - Create(ctx context.Context, storage *everestv1alpha1.BackupStorage, opts metav1.CreateOptions) (*everestv1alpha1.BackupStorage, error) - Update(ctx context.Context, storage *everestv1alpha1.BackupStorage, opts metav1.UpdateOptions) (*everestv1alpha1.BackupStorage, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*everestv1alpha1.BackupStorage, error) -} - -type client struct { - restClient rest.Interface - namespace string -} - -// Create creates a resource. -func (c *client) Create( - ctx context.Context, - storage *everestv1alpha1.BackupStorage, - opts metav1.CreateOptions, -) (*everestv1alpha1.BackupStorage, error) { - result := &everestv1alpha1.BackupStorage{} - err := c.restClient. - Post(). - Namespace(c.namespace). - Resource(backupStorageAPIKind).Body(storage). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Into(result) - return result, err -} - -// Update creates a resource. -func (c *client) Update( - ctx context.Context, - storage *everestv1alpha1.BackupStorage, - opts metav1.UpdateOptions, -) (*everestv1alpha1.BackupStorage, error) { - result := &everestv1alpha1.BackupStorage{} - err := c.restClient. - Put().Name(storage.Name). - Namespace(c.namespace). - Resource(backupStorageAPIKind).Body(storage). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Into(result) - return result, err -} - -// Delete creates a resource. -func (c *client) Delete( - ctx context.Context, - name string, - opts metav1.DeleteOptions, -) error { - return c.restClient. - Delete().Name(name). - Namespace(c.namespace). - Resource(backupStorageAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Error() -} - -// Get retrieves backup storage based on opts. -func (c *client) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.BackupStorage, error) { - result := &everestv1alpha1.BackupStorage{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(backupStorageAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// List retrieves backup storage list based on opts. -func (c *client) List( - ctx context.Context, - opts metav1.ListOptions, -) (*everestv1alpha1.BackupStorageList, error) { - result := &everestv1alpha1.BackupStorageList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(backupStorageAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} diff --git a/pkg/kubernetes/client/customresources/client.go b/pkg/kubernetes/client/customresources/client.go deleted file mode 100644 index c3bc86faa..000000000 --- a/pkg/kubernetes/client/customresources/client.go +++ /dev/null @@ -1,63 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources provides methods to work with custom everest k8s resources. -package customresources - -import ( - "sync" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -//nolint:gochecknoglobals -var addToScheme sync.Once - -// Client contains a rest client. -type Client struct { - restClient rest.Interface -} - -// NewForConfig creates a new database cluster client based on config. -func NewForConfig(c *rest.Config) (*Client, error) { - config := *c - config.ContentConfig.GroupVersion = &everestv1alpha1.GroupVersion - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - config.UserAgent = rest.DefaultKubernetesUserAgent() - - var err error - addToScheme.Do(func() { - err = everestv1alpha1.SchemeBuilder.AddToScheme(scheme.Scheme) - metav1.AddToGroupVersion(scheme.Scheme, everestv1alpha1.GroupVersion) - }) - - if err != nil { - return nil, err - } - - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - - return &Client{ - restClient: client, - }, nil -} diff --git a/pkg/kubernetes/client/customresources/database_cluster_backups.go b/pkg/kubernetes/client/customresources/database_cluster_backups.go deleted file mode 100644 index d4d2485d2..000000000 --- a/pkg/kubernetes/client/customresources/database_cluster_backups.go +++ /dev/null @@ -1,143 +0,0 @@ -// Package customresources ... -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - dbClusterBackupsAPIKind = "databaseclusterbackups" -) - -// DBClusterBackups returns a db cluster backup. -func (c *Client) DBClusterBackups(namespace string) DBClusterBackupInterface { //nolint:ireturn - return &dbClusterBackupClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -type dbClusterBackupClient struct { - restClient rest.Interface - namespace string -} - -// DBClusterBackupInterface supports list, get and watch methods. -type DBClusterBackupInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) - Get(ctx context.Context, name string, options metav1.GetOptions) (*everestv1alpha1.DatabaseClusterBackup, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Update(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup, opts metav1.UpdateOptions) (*everestv1alpha1.DatabaseClusterBackup, error) - Create(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup, opts metav1.CreateOptions) (*everestv1alpha1.DatabaseClusterBackup, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error -} - -// Delete deletes a resource. -func (c *dbClusterBackupClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.restClient. - Delete().Name(name). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Error() -} - -// Create creates a resource. -func (c *dbClusterBackupClient) Create( - ctx context.Context, - backup *everestv1alpha1.DatabaseClusterBackup, - opts metav1.CreateOptions, -) (*everestv1alpha1.DatabaseClusterBackup, error) { - result := &everestv1alpha1.DatabaseClusterBackup{} - err := c.restClient. - Post(). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind). - Body(backup). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// List lists database cluster backups based on opts. -func (c *dbClusterBackupClient) List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - result := &everestv1alpha1.DatabaseClusterBackupList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// Get retrieves database cluster backup based on opts. -func (c *dbClusterBackupClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.DatabaseClusterBackup, error) { - result := &everestv1alpha1.DatabaseClusterBackup{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// Watch starts a watch based on opts. -func (c *dbClusterBackupClient) Watch( //nolint:ireturn - ctx context.Context, - opts metav1.ListOptions, -) (watch.Interface, error) { - opts.Watch = true - return c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Watch(ctx) -} - -// Update creates a resource. -func (c *dbClusterBackupClient) Update( - ctx context.Context, - backup *everestv1alpha1.DatabaseClusterBackup, - opts metav1.UpdateOptions, -) (*everestv1alpha1.DatabaseClusterBackup, error) { - result := &everestv1alpha1.DatabaseClusterBackup{} - err := c.restClient. - Put().Name(backup.GetName()). - Namespace(c.namespace). - Resource(dbClusterBackupsAPIKind).Body(backup). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Into(result) - return result, err -} diff --git a/pkg/kubernetes/client/customresources/database_cluster_restores.go b/pkg/kubernetes/client/customresources/database_cluster_restores.go deleted file mode 100644 index 7c48ad5d6..000000000 --- a/pkg/kubernetes/client/customresources/database_cluster_restores.go +++ /dev/null @@ -1,152 +0,0 @@ -// Package customresources ... -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - dbClusterRestoresAPIKind = "databaseclusterrestores" -) - -// DBClusterRestores returns a db cluster client. -func (c *Client) DBClusterRestores(namespace string) DBClusterRestoreInterface { //nolint:ireturn - return &dbClusterRestoreClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -type dbClusterRestoreClient struct { - restClient rest.Interface - namespace string -} - -// DBClusterRestoreInterface supports list, get and watch methods. -type DBClusterRestoreInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterRestoreList, error) - Get(ctx context.Context, name string, options metav1.GetOptions) (*everestv1alpha1.DatabaseClusterRestore, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Create(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore, opts metav1.CreateOptions) (*everestv1alpha1.DatabaseClusterRestore, error) - Update(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore, opts metav1.UpdateOptions) (*everestv1alpha1.DatabaseClusterRestore, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error -} - -// Create creates a resource. -func (c *dbClusterRestoreClient) Create( - ctx context.Context, - restore *everestv1alpha1.DatabaseClusterRestore, - opts metav1.CreateOptions, -) (*everestv1alpha1.DatabaseClusterRestore, error) { - result := &everestv1alpha1.DatabaseClusterRestore{} - err := c.restClient. - Post(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Body(restore). - Do(ctx). - Into(result) - return result, err -} - -// Update updates a resource. -func (c *dbClusterRestoreClient) Update( - ctx context.Context, - restore *everestv1alpha1.DatabaseClusterRestore, - opts metav1.UpdateOptions, -) (*everestv1alpha1.DatabaseClusterRestore, error) { - result := &everestv1alpha1.DatabaseClusterRestore{} - err := c.restClient. - Put(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - Name(restore.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(restore). - Do(ctx). - Into(result) - return result, err -} - -// Delete deletes a resource. -func (c *dbClusterRestoreClient) Delete( - ctx context.Context, - name string, - opts metav1.DeleteOptions, -) error { - return c.restClient. - Delete(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - Name(name). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Error() -} - -// List lists database cluster restores based on opts. -func (c *dbClusterRestoreClient) List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterRestoreList, error) { - result := &everestv1alpha1.DatabaseClusterRestoreList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// Get retrieves database cluster restore based on opts. -func (c *dbClusterRestoreClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.DatabaseClusterRestore, error) { - result := &everestv1alpha1.DatabaseClusterRestore{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// Watch starts a watch based on opts. -func (c *dbClusterRestoreClient) Watch( //nolint:ireturn - ctx context.Context, - opts metav1.ListOptions, -) (watch.Interface, error) { - opts.Watch = true - return c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClusterRestoresAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Watch(ctx) -} diff --git a/pkg/kubernetes/client/customresources/databaseclusters.go b/pkg/kubernetes/client/customresources/databaseclusters.go deleted file mode 100644 index 4910498d8..000000000 --- a/pkg/kubernetes/client/customresources/databaseclusters.go +++ /dev/null @@ -1,151 +0,0 @@ -// Package customresources ... -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - dbClustersAPIKind = "databaseclusters" -) - -// DBClusters returns a db cluster client. -func (c *Client) DBClusters(namespace string) DBClusterInterface { //nolint:ireturn - return &dbClusterClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -type dbClusterClient struct { - restClient rest.Interface - namespace string -} - -// DBClusterInterface supports list, get and watch methods. -type DBClusterInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) - Get(ctx context.Context, name string, options metav1.GetOptions) (*everestv1alpha1.DatabaseCluster, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Update(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster, opts metav1.UpdateOptions) (*everestv1alpha1.DatabaseCluster, error) - Create(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster, opts metav1.CreateOptions) (*everestv1alpha1.DatabaseCluster, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error -} - -// Create creates a resource. -func (c *dbClusterClient) Create( - ctx context.Context, - cluster *everestv1alpha1.DatabaseCluster, - opts metav1.CreateOptions, -) (*everestv1alpha1.DatabaseCluster, error) { - result := &everestv1alpha1.DatabaseCluster{} - err := c.restClient. - Post(). - Namespace(c.namespace). - Resource(dbClustersAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Body(cluster). - Do(ctx). - Into(result) - return result, err -} - -// Update creates a resource. -func (c *dbClusterClient) Update( - ctx context.Context, - cluster *everestv1alpha1.DatabaseCluster, - opts metav1.UpdateOptions, -) (*everestv1alpha1.DatabaseCluster, error) { - result := &everestv1alpha1.DatabaseCluster{} - err := c.restClient. - Put(). - Namespace(c.namespace).Name(cluster.GetName()). - Resource(dbClustersAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Body(cluster). - Do(ctx). - Into(result) - return result, err -} - -// Delete creates a resource. -func (c *dbClusterClient) Delete( - ctx context.Context, - name string, - opts metav1.DeleteOptions, -) error { - return c.restClient. - Delete(). - Namespace(c.namespace). - Resource(dbClustersAPIKind). - Name(name). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Error() -} - -// List lists database clusters based on opts. -func (c *dbClusterClient) List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) { - result := &everestv1alpha1.DatabaseClusterList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClustersAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// Get retrieves database cluster based on opts. -func (c *dbClusterClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.DatabaseCluster, error) { - result := &everestv1alpha1.DatabaseCluster{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClustersAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// Watch starts a watch based on opts. -func (c *dbClusterClient) Watch( //nolint:ireturn - ctx context.Context, - opts metav1.ListOptions, -) (watch.Interface, error) { - opts.Watch = true - return c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbClustersAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Watch(ctx) -} diff --git a/pkg/kubernetes/client/customresources/databaseengines.go b/pkg/kubernetes/client/customresources/databaseengines.go deleted file mode 100644 index ebd9c451e..000000000 --- a/pkg/kubernetes/client/customresources/databaseengines.go +++ /dev/null @@ -1,116 +0,0 @@ -// Package customresources ... -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - dbEnginesAPIKind = "databaseengines" -) - -// DBEngines returns a db engine. -func (c *Client) DBEngines(namespace string) DBEngineInterface { //nolint:ireturn - return &dbEngineClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -type dbEngineClient struct { - restClient rest.Interface - namespace string -} - -// DBEngineInterface supports list, get and watch methods. -type DBEngineInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseEngineList, error) - Get(ctx context.Context, name string, options metav1.GetOptions) (*everestv1alpha1.DatabaseEngine, error) - Update(ctx context.Context, engine *everestv1alpha1.DatabaseEngine, opts metav1.UpdateOptions) (*everestv1alpha1.DatabaseEngine, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) -} - -// Update the database engine. -func (c *dbEngineClient) Update( - ctx context.Context, - engine *everestv1alpha1.DatabaseEngine, - opts metav1.UpdateOptions, -) (*everestv1alpha1.DatabaseEngine, error) { - result := &everestv1alpha1.DatabaseEngine{} - err := c.restClient. - Put(). - Namespace(c.namespace). - Resource(dbEnginesAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(engine.Name). - Body(engine). - Do(ctx). - Into(result) - return result, err -} - -// List lists database clusters based on opts. -func (c *dbEngineClient) List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseEngineList, error) { - result := &everestv1alpha1.DatabaseEngineList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbEnginesAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// Get retrieves database cluster based on opts. -func (c *dbEngineClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.DatabaseEngine, error) { - result := &everestv1alpha1.DatabaseEngine{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbEnginesAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// Watch starts a watch based on opts. -func (c *dbEngineClient) Watch( //nolint:ireturn - ctx context.Context, - opts metav1.ListOptions, -) (watch.Interface, error) { - opts.Watch = true - return c.restClient. - Get(). - Namespace(c.namespace). - Resource(dbEnginesAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Watch(ctx) -} diff --git a/pkg/kubernetes/client/customresources/monitoringconfigs.go b/pkg/kubernetes/client/customresources/monitoringconfigs.go deleted file mode 100644 index 45a2beae4..000000000 --- a/pkg/kubernetes/client/customresources/monitoringconfigs.go +++ /dev/null @@ -1,137 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 customresources provides methods to work with custom everest k8s resources. -// -//nolint:dupl -package customresources - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - monitoringConfigAPIKind = "monitoringconfigs" -) - -// MonitoringConfig returns a db cluster monitoringConfigClient. -func (c *Client) MonitoringConfig( //nolint:ireturn - namespace string, -) MonitoringConfigsInterface { - return &monitoringConfigClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -// MonitoringConfigsInterface supports methods to work with MonitoringConfigs. -type MonitoringConfigsInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.MonitoringConfigList, error) - Create(ctx context.Context, storage *everestv1alpha1.MonitoringConfig, opts metav1.CreateOptions) (*everestv1alpha1.MonitoringConfig, error) - Update(ctx context.Context, storage *everestv1alpha1.MonitoringConfig, opts metav1.UpdateOptions) (*everestv1alpha1.MonitoringConfig, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*everestv1alpha1.MonitoringConfig, error) -} - -type monitoringConfigClient struct { - restClient rest.Interface - namespace string -} - -// Create creates a monitoring config. -func (c *monitoringConfigClient) Create( - ctx context.Context, - storage *everestv1alpha1.MonitoringConfig, - opts metav1.CreateOptions, -) (*everestv1alpha1.MonitoringConfig, error) { - result := &everestv1alpha1.MonitoringConfig{} - err := c.restClient. - Post(). - Namespace(c.namespace). - Resource(monitoringConfigAPIKind).Body(storage). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Into(result) - return result, err -} - -// Update updates a monitoring config. -func (c *monitoringConfigClient) Update( - ctx context.Context, - storage *everestv1alpha1.MonitoringConfig, - opts metav1.UpdateOptions, -) (*everestv1alpha1.MonitoringConfig, error) { - result := &everestv1alpha1.MonitoringConfig{} - err := c.restClient. - Put().Name(storage.Name). - Namespace(c.namespace). - Resource(monitoringConfigAPIKind).Body(storage). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Into(result) - return result, err -} - -// Delete creates a resource. -func (c *monitoringConfigClient) Delete( - ctx context.Context, - name string, - opts metav1.DeleteOptions, -) error { - return c.restClient. - Delete().Name(name). - Namespace(c.namespace). - Resource(monitoringConfigAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx).Error() -} - -// Get retrieves a monitoring config based on opts. -func (c *monitoringConfigClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.MonitoringConfig, error) { - result := &everestv1alpha1.MonitoringConfig{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(monitoringConfigAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// List retrieves a monitoring configs list based on opts. -func (c *monitoringConfigClient) List( - ctx context.Context, - opts metav1.ListOptions, -) (*everestv1alpha1.MonitoringConfigList, error) { - result := &everestv1alpha1.MonitoringConfigList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(monitoringConfigAPIKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} diff --git a/pkg/kubernetes/client/database/database.go b/pkg/kubernetes/client/database/database.go deleted file mode 100644 index fdd852cb2..000000000 --- a/pkg/kubernetes/client/database/database.go +++ /dev/null @@ -1,139 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 database TODO -package database - -import ( - "context" - "sync" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -const ( - // DBClusterKind defines kind for DB cluster. - DBClusterKind = "DatabaseCluster" - apiKind = "databaseclusters" -) - -// DBClusterClientInterface supports getting a database cluster client. -type DBClusterClientInterface interface { - DBClusters(namespace string) DBClusterInterface -} - -// DBClusterClient contains a rest client. -type DBClusterClient struct { - restClient rest.Interface -} - -//nolint:gochecknoglobals -var addToScheme sync.Once - -// NewForConfig creates a new database cluster client based on config. -func NewForConfig(c *rest.Config) (*DBClusterClient, error) { - config := *c - config.ContentConfig.GroupVersion = &everestv1alpha1.GroupVersion - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - config.UserAgent = rest.DefaultKubernetesUserAgent() - - var err error - addToScheme.Do(func() { - err = everestv1alpha1.SchemeBuilder.AddToScheme(scheme.Scheme) - metav1.AddToGroupVersion(scheme.Scheme, everestv1alpha1.GroupVersion) - }) - - if err != nil { - return nil, err - } - - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - - return &DBClusterClient{restClient: client}, nil -} - -// DBClusters returns a db cluster client. -func (c *DBClusterClient) DBClusters(namespace string) DBClusterInterface { //nolint:ireturn - return &dbClusterClient{ - restClient: c.restClient, - namespace: namespace, - } -} - -// DBClusterInterface supports list, get and watch methods. -type DBClusterInterface interface { - List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) - Get(ctx context.Context, name string, options metav1.GetOptions) (*everestv1alpha1.DatabaseCluster, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) -} - -type dbClusterClient struct { - restClient rest.Interface - namespace string -} - -// List lists database clusters based on opts. -func (c *dbClusterClient) List(ctx context.Context, opts metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) { - result := &everestv1alpha1.DatabaseClusterList{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(apiKind). - VersionedParams(&opts, scheme.ParameterCodec). - Do(ctx). - Into(result) - return result, err -} - -// Get retrieves database cluster based on opts. -func (c *dbClusterClient) Get( - ctx context.Context, - name string, - opts metav1.GetOptions, -) (*everestv1alpha1.DatabaseCluster, error) { - result := &everestv1alpha1.DatabaseCluster{} - err := c.restClient. - Get(). - Namespace(c.namespace). - Resource(apiKind). - VersionedParams(&opts, scheme.ParameterCodec). - Name(name). - Do(ctx). - Into(result) - return result, err -} - -// Watch starts a watch based on opts. -func (c *dbClusterClient) Watch( //nolint:ireturn - ctx context.Context, - opts metav1.ListOptions, -) (watch.Interface, error) { - opts.Watch = true - return c.restClient. - Get(). - Namespace(c.namespace). - Resource(apiKind). - VersionedParams(&opts, scheme.ParameterCodec). - Watch(ctx) -} diff --git a/pkg/kubernetes/client/database_cluster.go b/pkg/kubernetes/client/database_cluster.go deleted file mode 100644 index a074ab160..000000000 --- a/pkg/kubernetes/client/database_cluster.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package client contains the client for the custom resources. -// -//nolint:dupl -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// ListDatabaseClusters returns list of managed database clusters. -func (c *Client) ListDatabaseClusters(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) { - return c.customClientSet.DBClusters(namespace).List(ctx, options) -} - -// GetDatabaseCluster returns database clusters by provided name. -func (c *Client) GetDatabaseCluster(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseCluster, error) { - return c.customClientSet.DBClusters(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// CreateDatabaseCluster creates a new database cluster. -func (c *Client) CreateDatabaseCluster(ctx context.Context, namespace string, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return c.customClientSet.DBClusters(namespace).Create(ctx, cluster, metav1.CreateOptions{}) -} - -// UpdateDatabaseCluster updates a database cluster. -func (c *Client) UpdateDatabaseCluster(ctx context.Context, namespace string, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return c.customClientSet.DBClusters(namespace).Update(ctx, cluster, metav1.UpdateOptions{}) -} - -// DeleteDatabaseCluster deletes a database cluster. -func (c *Client) DeleteDatabaseCluster(ctx context.Context, namespace, name string) error { - return c.customClientSet.DBClusters(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/database_cluster_backup.go b/pkg/kubernetes/client/database_cluster_backup.go deleted file mode 100644 index cce429ec3..000000000 --- a/pkg/kubernetes/client/database_cluster_backup.go +++ /dev/null @@ -1,34 +0,0 @@ -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// ListDatabaseClusterBackups returns list of managed database cluster backups. -func (c *Client) ListDatabaseClusterBackups(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return c.customClientSet.DBClusterBackups(namespace).List(ctx, options) -} - -// GetDatabaseClusterBackup returns database cluster backups by provided name. -func (c *Client) GetDatabaseClusterBackup(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterBackup, error) { - return c.customClientSet.DBClusterBackups(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// UpdateDatabaseClusterBackup updates the provided database cluster backup. -func (c *Client) UpdateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) { - return c.customClientSet.DBClusterBackups(backup.GetNamespace()).Update(ctx, backup, metav1.UpdateOptions{}) -} - -// CreateDatabaseClusterBackup creates a new database cluster backup. -func (c *Client) CreateDatabaseClusterBackup(ctx context.Context, namespace string, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) { - return c.customClientSet.DBClusterBackups(namespace).Create(ctx, backup, metav1.CreateOptions{}) -} - -// DeleteDatabaseClusterBackup deletes a database cluster backup. -func (c *Client) DeleteDatabaseClusterBackup(ctx context.Context, namespace, name string) error { - return c.customClientSet.DBClusterBackups(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/database_cluster_restore.go b/pkg/kubernetes/client/database_cluster_restore.go deleted file mode 100644 index b7a6e1125..000000000 --- a/pkg/kubernetes/client/database_cluster_restore.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package client contains the client for the custom resources. -// -//nolint:dupl -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// ListDatabaseClusterRestores returns list of managed database clusters. -func (c *Client) ListDatabaseClusterRestores(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterRestoreList, error) { - return c.customClientSet.DBClusterRestores(namespace).List(ctx, options) -} - -// GetDatabaseClusterRestore returns database clusters by provided name. -func (c *Client) GetDatabaseClusterRestore(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterRestore, error) { - return c.customClientSet.DBClusterRestores(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// CreateDatabaseClusterRestore creates a new database cluster. -func (c *Client) CreateDatabaseClusterRestore(ctx context.Context, namespace string, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - return c.customClientSet.DBClusterRestores(namespace).Create(ctx, restore, metav1.CreateOptions{}) -} - -// UpdateDatabaseClusterRestore updates a database cluster. -func (c *Client) UpdateDatabaseClusterRestore(ctx context.Context, namespace string, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - return c.customClientSet.DBClusterRestores(namespace).Update(ctx, restore, metav1.UpdateOptions{}) -} - -// DeleteDatabaseClusterRestore deletes a database cluster. -func (c *Client) DeleteDatabaseClusterRestore(ctx context.Context, namespace, name string) error { - return c.customClientSet.DBClusterRestores(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/database_engine.go b/pkg/kubernetes/client/database_engine.go deleted file mode 100644 index 7f3b06760..000000000 --- a/pkg/kubernetes/client/database_engine.go +++ /dev/null @@ -1,24 +0,0 @@ -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// ListDatabaseEngines returns list of managed database clusters. -func (c *Client) ListDatabaseEngines(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseEngineList, error) { - return c.customClientSet.DBEngines(namespace).List(ctx, metav1.ListOptions{}) -} - -// GetDatabaseEngine returns database clusters by provided name. -func (c *Client) GetDatabaseEngine(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseEngine, error) { - return c.customClientSet.DBEngines(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// UpdateDatabaseEngine updates a database engine and returns the updated object. -func (c *Client) UpdateDatabaseEngine(ctx context.Context, namespace string, engine *everestv1alpha1.DatabaseEngine) (*everestv1alpha1.DatabaseEngine, error) { - return c.customClientSet.DBEngines(namespace).Update(ctx, engine, metav1.UpdateOptions{}) -} diff --git a/pkg/kubernetes/client/deployment.go b/pkg/kubernetes/client/deployment.go deleted file mode 100644 index bb2189dd7..000000000 --- a/pkg/kubernetes/client/deployment.go +++ /dev/null @@ -1,34 +0,0 @@ -package client - -import ( - "context" - - appsv1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetDeployment returns deployment by name. -func (c *Client) GetDeployment(ctx context.Context, name string, namespace string) (*appsv1.Deployment, error) { - if namespace == "" { - namespace = c.namespace - } - return c.clientset.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListDeployments returns deployment by name. -func (c *Client) ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) { - if namespace == "" { - namespace = c.namespace - } - return c.clientset.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{}) -} - -// UpdateDeployment updates a deployment and returns the updated object. -func (c *Client) UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) { - return c.clientset.AppsV1().Deployments(deployment.GetNamespace()).Update(ctx, deployment, metav1.UpdateOptions{}) -} - -// DeleteDeployment deletes a deployment. -func (c *Client) DeleteDeployment(ctx context.Context, name, namespace string) error { - return c.clientset.AppsV1().Deployments(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/gen.go b/pkg/kubernetes/client/gen.go deleted file mode 100644 index a06402adf..000000000 --- a/pkg/kubernetes/client/gen.go +++ /dev/null @@ -1,19 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client - -//go:generate ../../../bin/ifacemaker -f backup_storage.go -f catalog_source.go -f configmap.go -f client.go -f cluster_service_version.go -f ctl.go -f database_cluster.go -f database_cluster_backup.go -f database_cluster_restore.go -f database_engine.go -f deployment.go -f install_plan.go -f monitoring.go -f monitoring_config.go -f namespace.go -f olm.go -f node.go -f pod.go -f secret.go -f storage.go -f writer -s Client -i KubeClientConnector -p client -o kubeclient_interface.gen.go -//go:generate ../../../bin/mockery --name=KubeClientConnector --case=snake --inpackage diff --git a/pkg/kubernetes/client/install_plan.go b/pkg/kubernetes/client/install_plan.go deleted file mode 100644 index 692be4620..000000000 --- a/pkg/kubernetes/client/install_plan.go +++ /dev/null @@ -1,36 +0,0 @@ -package client - -import ( - "context" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetInstallPlan retrieves an OLM install plan by namespace and name. -func (c *Client) GetInstallPlan(ctx context.Context, namespace string, name string) (*v1alpha1.InstallPlan, error) { - c.rcLock.Lock() - defer c.rcLock.Unlock() - - return c.olmClientset.OperatorsV1alpha1().InstallPlans(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListInstallPlans lists install plans. -func (c *Client) ListInstallPlans(ctx context.Context, namespace string) (*v1alpha1.InstallPlanList, error) { - c.rcLock.Lock() - defer c.rcLock.Unlock() - - return c.olmClientset.OperatorsV1alpha1().InstallPlans(namespace).List(ctx, metav1.ListOptions{}) -} - -// UpdateInstallPlan updates the existing install plan in the specified namespace. -func (c *Client) UpdateInstallPlan( - ctx context.Context, - namespace string, - installPlan *v1alpha1.InstallPlan, -) (*v1alpha1.InstallPlan, error) { - c.rcLock.Lock() - defer c.rcLock.Unlock() - - return c.olmClientset.OperatorsV1alpha1().InstallPlans(namespace).Update(ctx, installPlan, metav1.UpdateOptions{}) -} diff --git a/pkg/kubernetes/client/kubeclient_interface.gen.go b/pkg/kubernetes/client/kubeclient_interface.gen.go deleted file mode 100644 index 7e9e18ee8..000000000 --- a/pkg/kubernetes/client/kubeclient_interface.gen.go +++ /dev/null @@ -1,233 +0,0 @@ -// Code generated by ifacemaker; DO NOT EDIT. - -package client - -import ( - "context" - - v1 "github.com/operator-framework/api/pkg/operators/v1" - "github.com/operator-framework/api/pkg/operators/v1alpha1" - versioned "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - packagev1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - storagev1 "k8s.io/api/storage/v1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/version" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// KubeClientConnector ... -type KubeClientConnector interface { - // CreateBackupStorage creates an backupStorage. - CreateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) - // UpdateBackupStorage updates an backupStorage. - UpdateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) - // GetBackupStorage returns the backupStorage. - GetBackupStorage(ctx context.Context, namespace, name string) (*everestv1alpha1.BackupStorage, error) - // ListBackupStorages returns the backupStorage. - ListBackupStorages(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.BackupStorageList, error) - // DeleteBackupStorage deletes the backupStorage. - DeleteBackupStorage(ctx context.Context, namespace, name string) error - // DeleteCatalogSource deletes a catalog source. - DeleteCatalogSource(ctx context.Context, namespace string, name string) error - // GetConfigMap returns config map by name and namespace. - GetConfigMap(ctx context.Context, namespace, name string) (*corev1.ConfigMap, error) - // CreateConfigMap creates the provided ConfigMap. - CreateConfigMap(ctx context.Context, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error) - // UpdateConfigMap updates the provided ConfigMap. - UpdateConfigMap(ctx context.Context, configMap *corev1.ConfigMap) (*corev1.ConfigMap, error) - // Config returns restConfig to the pkg/kubernetes.Kubernetes client. - Config() *rest.Config - // Clientset returns the k8s clientset. - // - //nolint:ireturn - Clientset() kubernetes.Interface - // ClusterName returns the name of the k8s cluster. - ClusterName() string - // Namespace returns the namespace of the k8s cluster. - Namespace() string - // GetSecretsForServiceAccount returns secret by given service account name. - GetSecretsForServiceAccount(ctx context.Context, accountName string) (*corev1.Secret, error) - // GenerateKubeConfigWithToken generates kubeconfig with a user and token provided as a secret. - GenerateKubeConfigWithToken(user string, secret *corev1.Secret) ([]byte, error) - // GetServerVersion returns server version. - GetServerVersion() (*version.Info, error) - // ApplyObject applies object. - ApplyObject(obj runtime.Object) error - // DeleteObject deletes object from the k8s cluster. - DeleteObject(obj runtime.Object) error - // ListObjects lists objects by provided group, version, kind. - ListObjects(gvk schema.GroupVersionKind, into runtime.Object) error - // GetObject retrieves an object by provided group, version, kind and name. - GetObject(gvk schema.GroupVersionKind, name string, into runtime.Object) error - // GetLogs returns logs for pod. - GetLogs(ctx context.Context, pod, container string) (string, error) - // GetEvents retrieves events from a pod by a name. - GetEvents(ctx context.Context, name string) (string, error) - // ApplyFile accepts manifest file contents, parses into []runtime.Object - // and applies them against the cluster. - ApplyFile(fileBytes []byte) error - // ApplyManifestFile accepts manifest file contents, parses into []runtime.Object - // and applies them against the cluster. - ApplyManifestFile(fileBytes []byte, namespace string, ignoreObjects ...client.Object) error - // DeleteManifestFile accepts manifest file contents, parses into []runtime.Object - // and deletes them from the cluster. - DeleteManifestFile(fileBytes []byte, namespace string) error - // DoCSVWait waits until for a CSV to be applied. - DoCSVWait(ctx context.Context, key types.NamespacedName) error - // GetSubscriptionCSV retrieves a subscription CSV. - GetSubscriptionCSV(ctx context.Context, subKey types.NamespacedName) (types.NamespacedName, error) - // DoRolloutWait waits until a deployment has been rolled out susccessfully or there is an error. - DoRolloutWait(ctx context.Context, key types.NamespacedName) error - // GetOperatorGroup retrieves an operator group details by namespace and name. - GetOperatorGroup(ctx context.Context, namespace, name string) (*v1.OperatorGroup, error) - // CreateOperatorGroup creates an operator group to be used as part of a subscription. - CreateOperatorGroup(ctx context.Context, namespace, name string, targetNamespaces []string) (*v1.OperatorGroup, error) - // CreateSubscription creates an OLM subscription. - CreateSubscription(ctx context.Context, namespace string, subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) - // UpdateSubscription updates an OLM subscription. - UpdateSubscription(ctx context.Context, namespace string, subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) - // CreateSubscriptionForCatalog creates an OLM subscription. - CreateSubscriptionForCatalog(ctx context.Context, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV string, approval v1alpha1.Approval) (*v1alpha1.Subscription, error) - // GetSubscription retrieves an OLM subscription by namespace and name. - GetSubscription(ctx context.Context, namespace, name string) (*v1alpha1.Subscription, error) - // ListSubscriptions all the subscriptions in the namespace. - ListSubscriptions(ctx context.Context, namespace string) (*v1alpha1.SubscriptionList, error) - // DoPackageWait for the package to be available in OLM. - DoPackageWait(ctx context.Context, namespace, name string) error - // GetPackageManifest returns a package manifest by given name. - GetPackageManifest(ctx context.Context, namespace, name string) (*packagev1.PackageManifest, error) - // ListCRDs returns a list of CRDs. - ListCRDs(ctx context.Context, labelSelector *metav1.LabelSelector) (*apiextv1.CustomResourceDefinitionList, error) - // DeleteCRD deletes a CRD by name. - DeleteCRD(ctx context.Context, name string) error - // ListCRs returns a list of CRs. - ListCRs(ctx context.Context, namespace string, gvr schema.GroupVersionResource, labelSelector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) - // GetClusterServiceVersion retrieve a CSV by namespaced name. - GetClusterServiceVersion(ctx context.Context, key types.NamespacedName) (*v1alpha1.ClusterServiceVersion, error) - // ListClusterServiceVersion list all CSVs for the given namespace. - ListClusterServiceVersion(ctx context.Context, namespace string) (*v1alpha1.ClusterServiceVersionList, error) - // UpdateClusterServiceVersion updates a CSV and returns the updated CSV. - UpdateClusterServiceVersion(ctx context.Context, csv *v1alpha1.ClusterServiceVersion) (*v1alpha1.ClusterServiceVersion, error) - // DeleteClusterServiceVersion deletes a CSV by namespaced name. - DeleteClusterServiceVersion(ctx context.Context, key types.NamespacedName) error - // DeleteSubscription deletes a subscription by namespaced name. - DeleteSubscription(ctx context.Context, key types.NamespacedName) error - // DeleteFile accepts manifest file contents parses into []runtime.Object - // and deletes them from the cluster. - DeleteFile(fileBytes []byte) error - // GetService returns k8s service by provided namespace and name. - GetService(ctx context.Context, namespace, name string) (*corev1.Service, error) - // GetClusterRoleBinding returns cluster role binding by given name. - GetClusterRoleBinding(ctx context.Context, name string) (*rbacv1.ClusterRoleBinding, error) - // ListDatabaseClusters returns list of managed database clusters. - ListDatabaseClusters(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterList, error) - // GetDatabaseCluster returns database clusters by provided name. - GetDatabaseCluster(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseCluster, error) - // CreateDatabaseCluster creates a new database cluster. - CreateDatabaseCluster(ctx context.Context, namespace string, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) - // UpdateDatabaseCluster updates a database cluster. - UpdateDatabaseCluster(ctx context.Context, namespace string, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) - // DeleteDatabaseCluster deletes a database cluster. - DeleteDatabaseCluster(ctx context.Context, namespace, name string) error - // ListDatabaseClusterBackups returns list of managed database cluster backups. - ListDatabaseClusterBackups(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) - // GetDatabaseClusterBackup returns database cluster backups by provided name. - GetDatabaseClusterBackup(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterBackup, error) - // UpdateDatabaseClusterBackup updates the provided database cluster backup. - UpdateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) - // CreateDatabaseClusterBackup creates a new database cluster backup. - CreateDatabaseClusterBackup(ctx context.Context, namespace string, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) - // DeleteDatabaseClusterBackup deletes a database cluster backup. - DeleteDatabaseClusterBackup(ctx context.Context, namespace, name string) error - // ListDatabaseClusterRestores returns list of managed database clusters. - ListDatabaseClusterRestores(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterRestoreList, error) - // GetDatabaseClusterRestore returns database clusters by provided name. - GetDatabaseClusterRestore(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterRestore, error) - // CreateDatabaseClusterRestore creates a new database cluster. - CreateDatabaseClusterRestore(ctx context.Context, namespace string, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) - // UpdateDatabaseClusterRestore updates a database cluster. - UpdateDatabaseClusterRestore(ctx context.Context, namespace string, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) - // DeleteDatabaseClusterRestore deletes a database cluster. - DeleteDatabaseClusterRestore(ctx context.Context, namespace, name string) error - // ListDatabaseEngines returns list of managed database clusters. - ListDatabaseEngines(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseEngineList, error) - // GetDatabaseEngine returns database clusters by provided name. - GetDatabaseEngine(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseEngine, error) - // UpdateDatabaseEngine updates a database engine and returns the updated object. - UpdateDatabaseEngine(ctx context.Context, namespace string, engine *everestv1alpha1.DatabaseEngine) (*everestv1alpha1.DatabaseEngine, error) - // GetDeployment returns deployment by name. - GetDeployment(ctx context.Context, name string, namespace string) (*appsv1.Deployment, error) - // ListDeployments returns deployment by name. - ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) - // UpdateDeployment updates a deployment and returns the updated object. - UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) - // DeleteDeployment deletes a deployment. - DeleteDeployment(ctx context.Context, name, namespace string) error - // GetInstallPlan retrieves an OLM install plan by namespace and name. - GetInstallPlan(ctx context.Context, namespace string, name string) (*v1alpha1.InstallPlan, error) - // ListInstallPlans lists install plans. - ListInstallPlans(ctx context.Context, namespace string) (*v1alpha1.InstallPlanList, error) - // UpdateInstallPlan updates the existing install plan in the specified namespace. - UpdateInstallPlan(ctx context.Context, namespace string, installPlan *v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) - // DeleteAllMonitoringResources deletes all resources related to monitoring from k8s cluster. - DeleteAllMonitoringResources(ctx context.Context, namespace string) error - // CreateMonitoringConfig creates an monitoringConfig. - CreateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) - // UpdateMonitoringConfig updates an monitoringConfig. - UpdateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) - // GetMonitoringConfig returns the monitoringConfig. - GetMonitoringConfig(ctx context.Context, namespace, name string) (*everestv1alpha1.MonitoringConfig, error) - // ListMonitoringConfigs returns the monitoringConfig. - ListMonitoringConfigs(ctx context.Context, namespace string) (*everestv1alpha1.MonitoringConfigList, error) - // DeleteMonitoringConfig deletes the monitoringConfig. - DeleteMonitoringConfig(ctx context.Context, namespace, name string) error - // CreateNamespace creates the given namespace. - CreateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) - // GetNamespace returns a namespace. - GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) - // DeleteNamespace deletes a namespace. - DeleteNamespace(ctx context.Context, name string) error - // ListNamespaces returns a list of namespaces. - ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) - // UpdateNamespace updates the given namespace. - UpdateNamespace(ctx context.Context, namespace *corev1.Namespace, opts metav1.UpdateOptions) (*corev1.Namespace, error) - // OLM returns OLM client set. - // - //nolint:ireturn - OLM() versioned.Interface - // GetNodes returns list of nodes. - GetNodes(ctx context.Context) (*corev1.NodeList, error) - // GetPods returns list of pods. - GetPods(ctx context.Context, namespace string, labelSelector *metav1.LabelSelector) (*corev1.PodList, error) - // ListPods lists pods. - ListPods(ctx context.Context, namespace string, options metav1.ListOptions) (*corev1.PodList, error) - // DeletePod deletes a pod by given name in the given namespace. - DeletePod(ctx context.Context, namespace, name string) error - // ListSecrets returns secrets. - ListSecrets(ctx context.Context, namespace string) (*corev1.SecretList, error) - // GetSecret returns secret by name. - GetSecret(ctx context.Context, namespace, name string) (*corev1.Secret, error) - // UpdateSecret updates k8s Secret. - UpdateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) - // CreateSecret creates k8s Secret. - CreateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) - // DeleteSecret deletes the k8s Secret. - DeleteSecret(ctx context.Context, namespace, name string) error - // GetStorageClasses returns all storage classes available in the cluster. - GetStorageClasses(ctx context.Context) (*storagev1.StorageClassList, error) - // GetPersistentVolumes returns Persistent Volumes available in the cluster. - GetPersistentVolumes(ctx context.Context) (*corev1.PersistentVolumeList, error) -} diff --git a/pkg/kubernetes/client/mock_kube_client_connector.go b/pkg/kubernetes/client/mock_kube_client_connector.go deleted file mode 100644 index cdead40ca..000000000 --- a/pkg/kubernetes/client/mock_kube_client_connector.go +++ /dev/null @@ -1,2576 +0,0 @@ -// Code generated by mockery v2.53.3. DO NOT EDIT. - -package client - -import ( - context "context" - - operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - versioned "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - apisoperatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" - mock "github.com/stretchr/testify/mock" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - storagev1 "k8s.io/api/storage/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - version "k8s.io/apimachinery/pkg/version" - kubernetes "k8s.io/client-go/kubernetes" - rest "k8s.io/client-go/rest" - pkgclient "sigs.k8s.io/controller-runtime/pkg/client" - - v1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// MockKubeClientConnector is an autogenerated mock type for the KubeClientConnector type -type MockKubeClientConnector struct { - mock.Mock -} - -// ApplyFile provides a mock function with given fields: fileBytes -func (_m *MockKubeClientConnector) ApplyFile(fileBytes []byte) error { - ret := _m.Called(fileBytes) - - if len(ret) == 0 { - panic("no return value specified for ApplyFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte) error); ok { - r0 = rf(fileBytes) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ApplyManifestFile provides a mock function with given fields: fileBytes, namespace, ignoreObjects -func (_m *MockKubeClientConnector) ApplyManifestFile(fileBytes []byte, namespace string, ignoreObjects ...pkgclient.Object) error { - _va := make([]interface{}, len(ignoreObjects)) - for _i := range ignoreObjects { - _va[_i] = ignoreObjects[_i] - } - var _ca []interface{} - _ca = append(_ca, fileBytes, namespace) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for ApplyManifestFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte, string, ...pkgclient.Object) error); ok { - r0 = rf(fileBytes, namespace, ignoreObjects...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ApplyObject provides a mock function with given fields: obj -func (_m *MockKubeClientConnector) ApplyObject(obj runtime.Object) error { - ret := _m.Called(obj) - - if len(ret) == 0 { - panic("no return value specified for ApplyObject") - } - - var r0 error - if rf, ok := ret.Get(0).(func(runtime.Object) error); ok { - r0 = rf(obj) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Clientset provides a mock function with no fields -func (_m *MockKubeClientConnector) Clientset() kubernetes.Interface { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Clientset") - } - - var r0 kubernetes.Interface - if rf, ok := ret.Get(0).(func() kubernetes.Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(kubernetes.Interface) - } - } - - return r0 -} - -// ClusterName provides a mock function with no fields -func (_m *MockKubeClientConnector) ClusterName() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ClusterName") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// Config provides a mock function with no fields -func (_m *MockKubeClientConnector) Config() *rest.Config { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Config") - } - - var r0 *rest.Config - if rf, ok := ret.Get(0).(func() *rest.Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*rest.Config) - } - } - - return r0 -} - -// CreateBackupStorage provides a mock function with given fields: ctx, storage -func (_m *MockKubeClientConnector) CreateBackupStorage(ctx context.Context, storage *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for CreateBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.BackupStorage) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateConfigMap provides a mock function with given fields: ctx, configMap -func (_m *MockKubeClientConnector) CreateConfigMap(ctx context.Context, configMap *v1.ConfigMap) (*v1.ConfigMap, error) { - ret := _m.Called(ctx, configMap) - - if len(ret) == 0 { - panic("no return value specified for CreateConfigMap") - } - - var r0 *v1.ConfigMap - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.ConfigMap) (*v1.ConfigMap, error)); ok { - return rf(ctx, configMap) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.ConfigMap) *v1.ConfigMap); ok { - r0 = rf(ctx, configMap) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.ConfigMap) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.ConfigMap) error); ok { - r1 = rf(ctx, configMap) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateDatabaseCluster provides a mock function with given fields: ctx, namespace, cluster -func (_m *MockKubeClientConnector) CreateDatabaseCluster(ctx context.Context, namespace string, cluster *v1alpha1.DatabaseCluster) (*v1alpha1.DatabaseCluster, error) { - ret := _m.Called(ctx, namespace, cluster) - - if len(ret) == 0 { - panic("no return value specified for CreateDatabaseCluster") - } - - var r0 *v1alpha1.DatabaseCluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseCluster) (*v1alpha1.DatabaseCluster, error)); ok { - return rf(ctx, namespace, cluster) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseCluster) *v1alpha1.DatabaseCluster); ok { - r0 = rf(ctx, namespace, cluster) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseCluster) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseCluster) error); ok { - r1 = rf(ctx, namespace, cluster) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateDatabaseClusterBackup provides a mock function with given fields: ctx, namespace, backup -func (_m *MockKubeClientConnector) CreateDatabaseClusterBackup(ctx context.Context, namespace string, backup *v1alpha1.DatabaseClusterBackup) (*v1alpha1.DatabaseClusterBackup, error) { - ret := _m.Called(ctx, namespace, backup) - - if len(ret) == 0 { - panic("no return value specified for CreateDatabaseClusterBackup") - } - - var r0 *v1alpha1.DatabaseClusterBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterBackup) (*v1alpha1.DatabaseClusterBackup, error)); ok { - return rf(ctx, namespace, backup) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterBackup) *v1alpha1.DatabaseClusterBackup); ok { - r0 = rf(ctx, namespace, backup) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseClusterBackup) error); ok { - r1 = rf(ctx, namespace, backup) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateDatabaseClusterRestore provides a mock function with given fields: ctx, namespace, restore -func (_m *MockKubeClientConnector) CreateDatabaseClusterRestore(ctx context.Context, namespace string, restore *v1alpha1.DatabaseClusterRestore) (*v1alpha1.DatabaseClusterRestore, error) { - ret := _m.Called(ctx, namespace, restore) - - if len(ret) == 0 { - panic("no return value specified for CreateDatabaseClusterRestore") - } - - var r0 *v1alpha1.DatabaseClusterRestore - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) (*v1alpha1.DatabaseClusterRestore, error)); ok { - return rf(ctx, namespace, restore) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) *v1alpha1.DatabaseClusterRestore); ok { - r0 = rf(ctx, namespace, restore) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterRestore) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) error); ok { - r1 = rf(ctx, namespace, restore) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateMonitoringConfig provides a mock function with given fields: ctx, config -func (_m *MockKubeClientConnector) CreateMonitoringConfig(ctx context.Context, config *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, config) - - if len(ret) == 0 { - panic("no return value specified for CreateMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, config) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, config) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.MonitoringConfig) error); ok { - r1 = rf(ctx, config) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateNamespace provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) CreateNamespace(ctx context.Context, namespace *v1.Namespace) (*v1.Namespace, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for CreateNamespace") - } - - var r0 *v1.Namespace - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace) (*v1.Namespace, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace) *v1.Namespace); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Namespace) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Namespace) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateOperatorGroup provides a mock function with given fields: ctx, namespace, name, targetNamespaces -func (_m *MockKubeClientConnector) CreateOperatorGroup(ctx context.Context, namespace string, name string, targetNamespaces []string) (*operatorsv1.OperatorGroup, error) { - ret := _m.Called(ctx, namespace, name, targetNamespaces) - - if len(ret) == 0 { - panic("no return value specified for CreateOperatorGroup") - } - - var r0 *operatorsv1.OperatorGroup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) (*operatorsv1.OperatorGroup, error)); ok { - return rf(ctx, namespace, name, targetNamespaces) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) *operatorsv1.OperatorGroup); ok { - r0 = rf(ctx, namespace, name, targetNamespaces) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1.OperatorGroup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, []string) error); ok { - r1 = rf(ctx, namespace, name, targetNamespaces) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateSecret provides a mock function with given fields: ctx, secret -func (_m *MockKubeClientConnector) CreateSecret(ctx context.Context, secret *v1.Secret) (*v1.Secret, error) { - ret := _m.Called(ctx, secret) - - if len(ret) == 0 { - panic("no return value specified for CreateSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) (*v1.Secret, error)); ok { - return rf(ctx, secret) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) *v1.Secret); ok { - r0 = rf(ctx, secret) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Secret) error); ok { - r1 = rf(ctx, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateSubscription provides a mock function with given fields: ctx, namespace, subscription -func (_m *MockKubeClientConnector) CreateSubscription(ctx context.Context, namespace string, subscription *operatorsv1alpha1.Subscription) (*operatorsv1alpha1.Subscription, error) { - ret := _m.Called(ctx, namespace, subscription) - - if len(ret) == 0 { - panic("no return value specified for CreateSubscription") - } - - var r0 *operatorsv1alpha1.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.Subscription) (*operatorsv1alpha1.Subscription, error)); ok { - return rf(ctx, namespace, subscription) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.Subscription) *operatorsv1alpha1.Subscription); ok { - r0 = rf(ctx, namespace, subscription) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *operatorsv1alpha1.Subscription) error); ok { - r1 = rf(ctx, namespace, subscription) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateSubscriptionForCatalog provides a mock function with given fields: ctx, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV, approval -func (_m *MockKubeClientConnector) CreateSubscriptionForCatalog(ctx context.Context, namespace string, name string, catalogNamespace string, catalog string, packageName string, channel string, startingCSV string, approval operatorsv1alpha1.Approval) (*operatorsv1alpha1.Subscription, error) { - ret := _m.Called(ctx, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV, approval) - - if len(ret) == 0 { - panic("no return value specified for CreateSubscriptionForCatalog") - } - - var r0 *operatorsv1alpha1.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, string, string, operatorsv1alpha1.Approval) (*operatorsv1alpha1.Subscription, error)); ok { - return rf(ctx, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV, approval) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, string, string, operatorsv1alpha1.Approval) *operatorsv1alpha1.Subscription); ok { - r0 = rf(ctx, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV, approval) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, string, string, string, operatorsv1alpha1.Approval) error); ok { - r1 = rf(ctx, namespace, name, catalogNamespace, catalog, packageName, channel, startingCSV, approval) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DeleteAllMonitoringResources provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) DeleteAllMonitoringResources(ctx context.Context, namespace string) error { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteAllMonitoringResources") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteBackupStorage provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteBackupStorage(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteBackupStorage") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCRD provides a mock function with given fields: ctx, name -func (_m *MockKubeClientConnector) DeleteCRD(ctx context.Context, name string) error { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteCRD") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCatalogSource provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteCatalogSource(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteCatalogSource") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteClusterServiceVersion provides a mock function with given fields: ctx, key -func (_m *MockKubeClientConnector) DeleteClusterServiceVersion(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DeleteClusterServiceVersion") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDatabaseCluster provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteDatabaseCluster(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteDatabaseCluster") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDatabaseClusterBackup provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteDatabaseClusterBackup(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteDatabaseClusterBackup") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDatabaseClusterRestore provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteDatabaseClusterRestore(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteDatabaseClusterRestore") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeployment provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubeClientConnector) DeleteDeployment(ctx context.Context, name string, namespace string) error { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteDeployment") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, name, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteFile provides a mock function with given fields: fileBytes -func (_m *MockKubeClientConnector) DeleteFile(fileBytes []byte) error { - ret := _m.Called(fileBytes) - - if len(ret) == 0 { - panic("no return value specified for DeleteFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte) error); ok { - r0 = rf(fileBytes) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteManifestFile provides a mock function with given fields: fileBytes, namespace -func (_m *MockKubeClientConnector) DeleteManifestFile(fileBytes []byte, namespace string) error { - ret := _m.Called(fileBytes, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteManifestFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte, string) error); ok { - r0 = rf(fileBytes, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteMonitoringConfig provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteMonitoringConfig(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteMonitoringConfig") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNamespace provides a mock function with given fields: ctx, name -func (_m *MockKubeClientConnector) DeleteNamespace(ctx context.Context, name string) error { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteNamespace") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteObject provides a mock function with given fields: obj -func (_m *MockKubeClientConnector) DeleteObject(obj runtime.Object) error { - ret := _m.Called(obj) - - if len(ret) == 0 { - panic("no return value specified for DeleteObject") - } - - var r0 error - if rf, ok := ret.Get(0).(func(runtime.Object) error); ok { - r0 = rf(obj) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeletePod provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeletePod(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeletePod") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSecret provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DeleteSecret(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteSecret") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSubscription provides a mock function with given fields: ctx, key -func (_m *MockKubeClientConnector) DeleteSubscription(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DeleteSubscription") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DoCSVWait provides a mock function with given fields: ctx, key -func (_m *MockKubeClientConnector) DoCSVWait(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DoCSVWait") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DoPackageWait provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) DoPackageWait(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DoPackageWait") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DoRolloutWait provides a mock function with given fields: ctx, key -func (_m *MockKubeClientConnector) DoRolloutWait(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DoRolloutWait") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GenerateKubeConfigWithToken provides a mock function with given fields: user, secret -func (_m *MockKubeClientConnector) GenerateKubeConfigWithToken(user string, secret *v1.Secret) ([]byte, error) { - ret := _m.Called(user, secret) - - if len(ret) == 0 { - panic("no return value specified for GenerateKubeConfigWithToken") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(string, *v1.Secret) ([]byte, error)); ok { - return rf(user, secret) - } - if rf, ok := ret.Get(0).(func(string, *v1.Secret) []byte); ok { - r0 = rf(user, secret) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(string, *v1.Secret) error); ok { - r1 = rf(user, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetBackupStorage provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetBackupStorage(ctx context.Context, namespace string, name string) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetClusterRoleBinding provides a mock function with given fields: ctx, name -func (_m *MockKubeClientConnector) GetClusterRoleBinding(ctx context.Context, name string) (*rbacv1.ClusterRoleBinding, error) { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for GetClusterRoleBinding") - } - - var r0 *rbacv1.ClusterRoleBinding - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*rbacv1.ClusterRoleBinding, error)); ok { - return rf(ctx, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *rbacv1.ClusterRoleBinding); ok { - r0 = rf(ctx, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*rbacv1.ClusterRoleBinding) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetClusterServiceVersion provides a mock function with given fields: ctx, key -func (_m *MockKubeClientConnector) GetClusterServiceVersion(ctx context.Context, key types.NamespacedName) (*operatorsv1alpha1.ClusterServiceVersion, error) { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for GetClusterServiceVersion") - } - - var r0 *operatorsv1alpha1.ClusterServiceVersion - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) (*operatorsv1alpha1.ClusterServiceVersion, error)); ok { - return rf(ctx, key) - } - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) *operatorsv1alpha1.ClusterServiceVersion); ok { - r0 = rf(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.ClusterServiceVersion) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, types.NamespacedName) error); ok { - r1 = rf(ctx, key) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetConfigMap provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetConfigMap(ctx context.Context, namespace string, name string) (*v1.ConfigMap, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetConfigMap") - } - - var r0 *v1.ConfigMap - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.ConfigMap, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.ConfigMap); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.ConfigMap) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDatabaseCluster provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetDatabaseCluster(ctx context.Context, namespace string, name string) (*v1alpha1.DatabaseCluster, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseCluster") - } - - var r0 *v1alpha1.DatabaseCluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.DatabaseCluster, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.DatabaseCluster); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseCluster) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDatabaseClusterBackup provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetDatabaseClusterBackup(ctx context.Context, namespace string, name string) (*v1alpha1.DatabaseClusterBackup, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseClusterBackup") - } - - var r0 *v1alpha1.DatabaseClusterBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.DatabaseClusterBackup, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.DatabaseClusterBackup); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDatabaseClusterRestore provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetDatabaseClusterRestore(ctx context.Context, namespace string, name string) (*v1alpha1.DatabaseClusterRestore, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseClusterRestore") - } - - var r0 *v1alpha1.DatabaseClusterRestore - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.DatabaseClusterRestore, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.DatabaseClusterRestore); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterRestore) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDatabaseEngine provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetDatabaseEngine(ctx context.Context, namespace string, name string) (*v1alpha1.DatabaseEngine, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseEngine") - } - - var r0 *v1alpha1.DatabaseEngine - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.DatabaseEngine, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.DatabaseEngine); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngine) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeployment provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubeClientConnector) GetDeployment(ctx context.Context, name string, namespace string) (*appsv1.Deployment, error) { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for GetDeployment") - } - - var r0 *appsv1.Deployment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*appsv1.Deployment, error)); ok { - return rf(ctx, name, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *appsv1.Deployment); ok { - r0 = rf(ctx, name, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.Deployment) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, name, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetEvents provides a mock function with given fields: ctx, name -func (_m *MockKubeClientConnector) GetEvents(ctx context.Context, name string) (string, error) { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for GetEvents") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (string, error)); ok { - return rf(ctx, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string) string); ok { - r0 = rf(ctx, name) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetInstallPlan provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetInstallPlan(ctx context.Context, namespace string, name string) (*operatorsv1alpha1.InstallPlan, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetInstallPlan") - } - - var r0 *operatorsv1alpha1.InstallPlan - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*operatorsv1alpha1.InstallPlan, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *operatorsv1alpha1.InstallPlan); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.InstallPlan) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetLogs provides a mock function with given fields: ctx, pod, container -func (_m *MockKubeClientConnector) GetLogs(ctx context.Context, pod string, container string) (string, error) { - ret := _m.Called(ctx, pod, container) - - if len(ret) == 0 { - panic("no return value specified for GetLogs") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { - return rf(ctx, pod, container) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { - r0 = rf(ctx, pod, container) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, pod, container) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetMonitoringConfig provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetMonitoringConfig(ctx context.Context, namespace string, name string) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNamespace provides a mock function with given fields: ctx, name -func (_m *MockKubeClientConnector) GetNamespace(ctx context.Context, name string) (*v1.Namespace, error) { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for GetNamespace") - } - - var r0 *v1.Namespace - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.Namespace, error)); ok { - return rf(ctx, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1.Namespace); ok { - r0 = rf(ctx, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Namespace) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNodes provides a mock function with given fields: ctx -func (_m *MockKubeClientConnector) GetNodes(ctx context.Context) (*v1.NodeList, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetNodes") - } - - var r0 *v1.NodeList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*v1.NodeList, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *v1.NodeList); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.NodeList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetObject provides a mock function with given fields: gvk, name, into -func (_m *MockKubeClientConnector) GetObject(gvk schema.GroupVersionKind, name string, into runtime.Object) error { - ret := _m.Called(gvk, name, into) - - if len(ret) == 0 { - panic("no return value specified for GetObject") - } - - var r0 error - if rf, ok := ret.Get(0).(func(schema.GroupVersionKind, string, runtime.Object) error); ok { - r0 = rf(gvk, name, into) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetOperatorGroup provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetOperatorGroup(ctx context.Context, namespace string, name string) (*operatorsv1.OperatorGroup, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetOperatorGroup") - } - - var r0 *operatorsv1.OperatorGroup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*operatorsv1.OperatorGroup, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *operatorsv1.OperatorGroup); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1.OperatorGroup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetPackageManifest provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetPackageManifest(ctx context.Context, namespace string, name string) (*apisoperatorsv1.PackageManifest, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetPackageManifest") - } - - var r0 *apisoperatorsv1.PackageManifest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*apisoperatorsv1.PackageManifest, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *apisoperatorsv1.PackageManifest); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*apisoperatorsv1.PackageManifest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetPersistentVolumes provides a mock function with given fields: ctx -func (_m *MockKubeClientConnector) GetPersistentVolumes(ctx context.Context) (*v1.PersistentVolumeList, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetPersistentVolumes") - } - - var r0 *v1.PersistentVolumeList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*v1.PersistentVolumeList, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *v1.PersistentVolumeList); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PersistentVolumeList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetPods provides a mock function with given fields: ctx, namespace, labelSelector -func (_m *MockKubeClientConnector) GetPods(ctx context.Context, namespace string, labelSelector *metav1.LabelSelector) (*v1.PodList, error) { - ret := _m.Called(ctx, namespace, labelSelector) - - if len(ret) == 0 { - panic("no return value specified for GetPods") - } - - var r0 *v1.PodList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *metav1.LabelSelector) (*v1.PodList, error)); ok { - return rf(ctx, namespace, labelSelector) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *metav1.LabelSelector) *v1.PodList); ok { - r0 = rf(ctx, namespace, labelSelector) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *metav1.LabelSelector) error); ok { - r1 = rf(ctx, namespace, labelSelector) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSecret provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetSecret(ctx context.Context, namespace string, name string) (*v1.Secret, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.Secret, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.Secret); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSecretsForServiceAccount provides a mock function with given fields: ctx, accountName -func (_m *MockKubeClientConnector) GetSecretsForServiceAccount(ctx context.Context, accountName string) (*v1.Secret, error) { - ret := _m.Called(ctx, accountName) - - if len(ret) == 0 { - panic("no return value specified for GetSecretsForServiceAccount") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.Secret, error)); ok { - return rf(ctx, accountName) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1.Secret); ok { - r0 = rf(ctx, accountName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, accountName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetServerVersion provides a mock function with no fields -func (_m *MockKubeClientConnector) GetServerVersion() (*version.Info, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetServerVersion") - } - - var r0 *version.Info - var r1 error - if rf, ok := ret.Get(0).(func() (*version.Info, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *version.Info); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*version.Info) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetService provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetService(ctx context.Context, namespace string, name string) (*v1.Service, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetService") - } - - var r0 *v1.Service - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.Service, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.Service); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Service) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetStorageClasses provides a mock function with given fields: ctx -func (_m *MockKubeClientConnector) GetStorageClasses(ctx context.Context) (*storagev1.StorageClassList, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetStorageClasses") - } - - var r0 *storagev1.StorageClassList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*storagev1.StorageClassList, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *storagev1.StorageClassList); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*storagev1.StorageClassList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscription provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubeClientConnector) GetSubscription(ctx context.Context, namespace string, name string) (*operatorsv1alpha1.Subscription, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetSubscription") - } - - var r0 *operatorsv1alpha1.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*operatorsv1alpha1.Subscription, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *operatorsv1alpha1.Subscription); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscriptionCSV provides a mock function with given fields: ctx, subKey -func (_m *MockKubeClientConnector) GetSubscriptionCSV(ctx context.Context, subKey types.NamespacedName) (types.NamespacedName, error) { - ret := _m.Called(ctx, subKey) - - if len(ret) == 0 { - panic("no return value specified for GetSubscriptionCSV") - } - - var r0 types.NamespacedName - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) (types.NamespacedName, error)); ok { - return rf(ctx, subKey) - } - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) types.NamespacedName); ok { - r0 = rf(ctx, subKey) - } else { - r0 = ret.Get(0).(types.NamespacedName) - } - - if rf, ok := ret.Get(1).(func(context.Context, types.NamespacedName) error); ok { - r1 = rf(ctx, subKey) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListBackupStorages provides a mock function with given fields: ctx, namespace, options -func (_m *MockKubeClientConnector) ListBackupStorages(ctx context.Context, namespace string, options metav1.ListOptions) (*v1alpha1.BackupStorageList, error) { - ret := _m.Called(ctx, namespace, options) - - if len(ret) == 0 { - panic("no return value specified for ListBackupStorages") - } - - var r0 *v1alpha1.BackupStorageList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) (*v1alpha1.BackupStorageList, error)); ok { - return rf(ctx, namespace, options) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) *v1alpha1.BackupStorageList); ok { - r0 = rf(ctx, namespace, options) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorageList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.ListOptions) error); ok { - r1 = rf(ctx, namespace, options) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListCRDs provides a mock function with given fields: ctx, labelSelector -func (_m *MockKubeClientConnector) ListCRDs(ctx context.Context, labelSelector *metav1.LabelSelector) (*apiextensionsv1.CustomResourceDefinitionList, error) { - ret := _m.Called(ctx, labelSelector) - - if len(ret) == 0 { - panic("no return value specified for ListCRDs") - } - - var r0 *apiextensionsv1.CustomResourceDefinitionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *metav1.LabelSelector) (*apiextensionsv1.CustomResourceDefinitionList, error)); ok { - return rf(ctx, labelSelector) - } - if rf, ok := ret.Get(0).(func(context.Context, *metav1.LabelSelector) *apiextensionsv1.CustomResourceDefinitionList); ok { - r0 = rf(ctx, labelSelector) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*apiextensionsv1.CustomResourceDefinitionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *metav1.LabelSelector) error); ok { - r1 = rf(ctx, labelSelector) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListCRs provides a mock function with given fields: ctx, namespace, gvr, labelSelector -func (_m *MockKubeClientConnector) ListCRs(ctx context.Context, namespace string, gvr schema.GroupVersionResource, labelSelector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) { - ret := _m.Called(ctx, namespace, gvr, labelSelector) - - if len(ret) == 0 { - panic("no return value specified for ListCRs") - } - - var r0 *unstructured.UnstructuredList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, schema.GroupVersionResource, *metav1.LabelSelector) (*unstructured.UnstructuredList, error)); ok { - return rf(ctx, namespace, gvr, labelSelector) - } - if rf, ok := ret.Get(0).(func(context.Context, string, schema.GroupVersionResource, *metav1.LabelSelector) *unstructured.UnstructuredList); ok { - r0 = rf(ctx, namespace, gvr, labelSelector) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*unstructured.UnstructuredList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, schema.GroupVersionResource, *metav1.LabelSelector) error); ok { - r1 = rf(ctx, namespace, gvr, labelSelector) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListClusterServiceVersion provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListClusterServiceVersion(ctx context.Context, namespace string) (*operatorsv1alpha1.ClusterServiceVersionList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListClusterServiceVersion") - } - - var r0 *operatorsv1alpha1.ClusterServiceVersionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*operatorsv1alpha1.ClusterServiceVersionList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *operatorsv1alpha1.ClusterServiceVersionList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.ClusterServiceVersionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDatabaseClusterBackups provides a mock function with given fields: ctx, namespace, options -func (_m *MockKubeClientConnector) ListDatabaseClusterBackups(ctx context.Context, namespace string, options metav1.ListOptions) (*v1alpha1.DatabaseClusterBackupList, error) { - ret := _m.Called(ctx, namespace, options) - - if len(ret) == 0 { - panic("no return value specified for ListDatabaseClusterBackups") - } - - var r0 *v1alpha1.DatabaseClusterBackupList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) (*v1alpha1.DatabaseClusterBackupList, error)); ok { - return rf(ctx, namespace, options) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) *v1alpha1.DatabaseClusterBackupList); ok { - r0 = rf(ctx, namespace, options) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterBackupList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.ListOptions) error); ok { - r1 = rf(ctx, namespace, options) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDatabaseClusterRestores provides a mock function with given fields: ctx, namespace, options -func (_m *MockKubeClientConnector) ListDatabaseClusterRestores(ctx context.Context, namespace string, options metav1.ListOptions) (*v1alpha1.DatabaseClusterRestoreList, error) { - ret := _m.Called(ctx, namespace, options) - - if len(ret) == 0 { - panic("no return value specified for ListDatabaseClusterRestores") - } - - var r0 *v1alpha1.DatabaseClusterRestoreList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) (*v1alpha1.DatabaseClusterRestoreList, error)); ok { - return rf(ctx, namespace, options) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) *v1alpha1.DatabaseClusterRestoreList); ok { - r0 = rf(ctx, namespace, options) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterRestoreList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.ListOptions) error); ok { - r1 = rf(ctx, namespace, options) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDatabaseClusters provides a mock function with given fields: ctx, namespace, options -func (_m *MockKubeClientConnector) ListDatabaseClusters(ctx context.Context, namespace string, options metav1.ListOptions) (*v1alpha1.DatabaseClusterList, error) { - ret := _m.Called(ctx, namespace, options) - - if len(ret) == 0 { - panic("no return value specified for ListDatabaseClusters") - } - - var r0 *v1alpha1.DatabaseClusterList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) (*v1alpha1.DatabaseClusterList, error)); ok { - return rf(ctx, namespace, options) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) *v1alpha1.DatabaseClusterList); ok { - r0 = rf(ctx, namespace, options) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.ListOptions) error); ok { - r1 = rf(ctx, namespace, options) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDatabaseEngines provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListDatabaseEngines(ctx context.Context, namespace string) (*v1alpha1.DatabaseEngineList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListDatabaseEngines") - } - - var r0 *v1alpha1.DatabaseEngineList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.DatabaseEngineList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.DatabaseEngineList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngineList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDeployments provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListDeployments") - } - - var r0 *appsv1.DeploymentList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*appsv1.DeploymentList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *appsv1.DeploymentList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.DeploymentList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListInstallPlans provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListInstallPlans(ctx context.Context, namespace string) (*operatorsv1alpha1.InstallPlanList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListInstallPlans") - } - - var r0 *operatorsv1alpha1.InstallPlanList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*operatorsv1alpha1.InstallPlanList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *operatorsv1alpha1.InstallPlanList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.InstallPlanList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListMonitoringConfigs provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListMonitoringConfigs(ctx context.Context, namespace string) (*v1alpha1.MonitoringConfigList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListMonitoringConfigs") - } - - var r0 *v1alpha1.MonitoringConfigList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.MonitoringConfigList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.MonitoringConfigList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfigList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListNamespaces provides a mock function with given fields: ctx, opts -func (_m *MockKubeClientConnector) ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*v1.NamespaceList, error) { - ret := _m.Called(ctx, opts) - - if len(ret) == 0 { - panic("no return value specified for ListNamespaces") - } - - var r0 *v1.NamespaceList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.NamespaceList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.NamespaceList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.NamespaceList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListObjects provides a mock function with given fields: gvk, into -func (_m *MockKubeClientConnector) ListObjects(gvk schema.GroupVersionKind, into runtime.Object) error { - ret := _m.Called(gvk, into) - - if len(ret) == 0 { - panic("no return value specified for ListObjects") - } - - var r0 error - if rf, ok := ret.Get(0).(func(schema.GroupVersionKind, runtime.Object) error); ok { - r0 = rf(gvk, into) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ListPods provides a mock function with given fields: ctx, namespace, options -func (_m *MockKubeClientConnector) ListPods(ctx context.Context, namespace string, options metav1.ListOptions) (*v1.PodList, error) { - ret := _m.Called(ctx, namespace, options) - - if len(ret) == 0 { - panic("no return value specified for ListPods") - } - - var r0 *v1.PodList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) (*v1.PodList, error)); ok { - return rf(ctx, namespace, options) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.ListOptions) *v1.PodList); ok { - r0 = rf(ctx, namespace, options) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.ListOptions) error); ok { - r1 = rf(ctx, namespace, options) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListSecrets provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListSecrets(ctx context.Context, namespace string) (*v1.SecretList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListSecrets") - } - - var r0 *v1.SecretList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.SecretList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1.SecretList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.SecretList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListSubscriptions provides a mock function with given fields: ctx, namespace -func (_m *MockKubeClientConnector) ListSubscriptions(ctx context.Context, namespace string) (*operatorsv1alpha1.SubscriptionList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListSubscriptions") - } - - var r0 *operatorsv1alpha1.SubscriptionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*operatorsv1alpha1.SubscriptionList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *operatorsv1alpha1.SubscriptionList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.SubscriptionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Namespace provides a mock function with no fields -func (_m *MockKubeClientConnector) Namespace() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Namespace") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// OLM provides a mock function with no fields -func (_m *MockKubeClientConnector) OLM() versioned.Interface { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for OLM") - } - - var r0 versioned.Interface - if rf, ok := ret.Get(0).(func() versioned.Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(versioned.Interface) - } - } - - return r0 -} - -// UpdateBackupStorage provides a mock function with given fields: ctx, storage -func (_m *MockKubeClientConnector) UpdateBackupStorage(ctx context.Context, storage *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for UpdateBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.BackupStorage) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateClusterServiceVersion provides a mock function with given fields: ctx, csv -func (_m *MockKubeClientConnector) UpdateClusterServiceVersion(ctx context.Context, csv *operatorsv1alpha1.ClusterServiceVersion) (*operatorsv1alpha1.ClusterServiceVersion, error) { - ret := _m.Called(ctx, csv) - - if len(ret) == 0 { - panic("no return value specified for UpdateClusterServiceVersion") - } - - var r0 *operatorsv1alpha1.ClusterServiceVersion - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *operatorsv1alpha1.ClusterServiceVersion) (*operatorsv1alpha1.ClusterServiceVersion, error)); ok { - return rf(ctx, csv) - } - if rf, ok := ret.Get(0).(func(context.Context, *operatorsv1alpha1.ClusterServiceVersion) *operatorsv1alpha1.ClusterServiceVersion); ok { - r0 = rf(ctx, csv) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.ClusterServiceVersion) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *operatorsv1alpha1.ClusterServiceVersion) error); ok { - r1 = rf(ctx, csv) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateConfigMap provides a mock function with given fields: ctx, configMap -func (_m *MockKubeClientConnector) UpdateConfigMap(ctx context.Context, configMap *v1.ConfigMap) (*v1.ConfigMap, error) { - ret := _m.Called(ctx, configMap) - - if len(ret) == 0 { - panic("no return value specified for UpdateConfigMap") - } - - var r0 *v1.ConfigMap - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.ConfigMap) (*v1.ConfigMap, error)); ok { - return rf(ctx, configMap) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.ConfigMap) *v1.ConfigMap); ok { - r0 = rf(ctx, configMap) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.ConfigMap) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.ConfigMap) error); ok { - r1 = rf(ctx, configMap) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDatabaseCluster provides a mock function with given fields: ctx, namespace, cluster -func (_m *MockKubeClientConnector) UpdateDatabaseCluster(ctx context.Context, namespace string, cluster *v1alpha1.DatabaseCluster) (*v1alpha1.DatabaseCluster, error) { - ret := _m.Called(ctx, namespace, cluster) - - if len(ret) == 0 { - panic("no return value specified for UpdateDatabaseCluster") - } - - var r0 *v1alpha1.DatabaseCluster - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseCluster) (*v1alpha1.DatabaseCluster, error)); ok { - return rf(ctx, namespace, cluster) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseCluster) *v1alpha1.DatabaseCluster); ok { - r0 = rf(ctx, namespace, cluster) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseCluster) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseCluster) error); ok { - r1 = rf(ctx, namespace, cluster) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDatabaseClusterBackup provides a mock function with given fields: ctx, backup -func (_m *MockKubeClientConnector) UpdateDatabaseClusterBackup(ctx context.Context, backup *v1alpha1.DatabaseClusterBackup) (*v1alpha1.DatabaseClusterBackup, error) { - ret := _m.Called(ctx, backup) - - if len(ret) == 0 { - panic("no return value specified for UpdateDatabaseClusterBackup") - } - - var r0 *v1alpha1.DatabaseClusterBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.DatabaseClusterBackup) (*v1alpha1.DatabaseClusterBackup, error)); ok { - return rf(ctx, backup) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.DatabaseClusterBackup) *v1alpha1.DatabaseClusterBackup); ok { - r0 = rf(ctx, backup) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.DatabaseClusterBackup) error); ok { - r1 = rf(ctx, backup) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDatabaseClusterRestore provides a mock function with given fields: ctx, namespace, restore -func (_m *MockKubeClientConnector) UpdateDatabaseClusterRestore(ctx context.Context, namespace string, restore *v1alpha1.DatabaseClusterRestore) (*v1alpha1.DatabaseClusterRestore, error) { - ret := _m.Called(ctx, namespace, restore) - - if len(ret) == 0 { - panic("no return value specified for UpdateDatabaseClusterRestore") - } - - var r0 *v1alpha1.DatabaseClusterRestore - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) (*v1alpha1.DatabaseClusterRestore, error)); ok { - return rf(ctx, namespace, restore) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) *v1alpha1.DatabaseClusterRestore); ok { - r0 = rf(ctx, namespace, restore) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseClusterRestore) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseClusterRestore) error); ok { - r1 = rf(ctx, namespace, restore) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDatabaseEngine provides a mock function with given fields: ctx, namespace, engine -func (_m *MockKubeClientConnector) UpdateDatabaseEngine(ctx context.Context, namespace string, engine *v1alpha1.DatabaseEngine) (*v1alpha1.DatabaseEngine, error) { - ret := _m.Called(ctx, namespace, engine) - - if len(ret) == 0 { - panic("no return value specified for UpdateDatabaseEngine") - } - - var r0 *v1alpha1.DatabaseEngine - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseEngine) (*v1alpha1.DatabaseEngine, error)); ok { - return rf(ctx, namespace, engine) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *v1alpha1.DatabaseEngine) *v1alpha1.DatabaseEngine); ok { - r0 = rf(ctx, namespace, engine) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngine) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *v1alpha1.DatabaseEngine) error); ok { - r1 = rf(ctx, namespace, engine) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDeployment provides a mock function with given fields: ctx, deployment -func (_m *MockKubeClientConnector) UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) { - ret := _m.Called(ctx, deployment) - - if len(ret) == 0 { - panic("no return value specified for UpdateDeployment") - } - - var r0 *appsv1.Deployment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *appsv1.Deployment) (*appsv1.Deployment, error)); ok { - return rf(ctx, deployment) - } - if rf, ok := ret.Get(0).(func(context.Context, *appsv1.Deployment) *appsv1.Deployment); ok { - r0 = rf(ctx, deployment) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.Deployment) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *appsv1.Deployment) error); ok { - r1 = rf(ctx, deployment) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateInstallPlan provides a mock function with given fields: ctx, namespace, installPlan -func (_m *MockKubeClientConnector) UpdateInstallPlan(ctx context.Context, namespace string, installPlan *operatorsv1alpha1.InstallPlan) (*operatorsv1alpha1.InstallPlan, error) { - ret := _m.Called(ctx, namespace, installPlan) - - if len(ret) == 0 { - panic("no return value specified for UpdateInstallPlan") - } - - var r0 *operatorsv1alpha1.InstallPlan - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.InstallPlan) (*operatorsv1alpha1.InstallPlan, error)); ok { - return rf(ctx, namespace, installPlan) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.InstallPlan) *operatorsv1alpha1.InstallPlan); ok { - r0 = rf(ctx, namespace, installPlan) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.InstallPlan) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *operatorsv1alpha1.InstallPlan) error); ok { - r1 = rf(ctx, namespace, installPlan) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateMonitoringConfig provides a mock function with given fields: ctx, config -func (_m *MockKubeClientConnector) UpdateMonitoringConfig(ctx context.Context, config *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, config) - - if len(ret) == 0 { - panic("no return value specified for UpdateMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, config) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, config) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.MonitoringConfig) error); ok { - r1 = rf(ctx, config) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateNamespace provides a mock function with given fields: ctx, namespace, opts -func (_m *MockKubeClientConnector) UpdateNamespace(ctx context.Context, namespace *v1.Namespace, opts metav1.UpdateOptions) (*v1.Namespace, error) { - ret := _m.Called(ctx, namespace, opts) - - if len(ret) == 0 { - panic("no return value specified for UpdateNamespace") - } - - var r0 *v1.Namespace - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) (*v1.Namespace, error)); ok { - return rf(ctx, namespace, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) *v1.Namespace); ok { - r0 = rf(ctx, namespace, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Namespace) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, namespace, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateSecret provides a mock function with given fields: ctx, secret -func (_m *MockKubeClientConnector) UpdateSecret(ctx context.Context, secret *v1.Secret) (*v1.Secret, error) { - ret := _m.Called(ctx, secret) - - if len(ret) == 0 { - panic("no return value specified for UpdateSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) (*v1.Secret, error)); ok { - return rf(ctx, secret) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) *v1.Secret); ok { - r0 = rf(ctx, secret) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Secret) error); ok { - r1 = rf(ctx, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateSubscription provides a mock function with given fields: ctx, namespace, subscription -func (_m *MockKubeClientConnector) UpdateSubscription(ctx context.Context, namespace string, subscription *operatorsv1alpha1.Subscription) (*operatorsv1alpha1.Subscription, error) { - ret := _m.Called(ctx, namespace, subscription) - - if len(ret) == 0 { - panic("no return value specified for UpdateSubscription") - } - - var r0 *operatorsv1alpha1.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.Subscription) (*operatorsv1alpha1.Subscription, error)); ok { - return rf(ctx, namespace, subscription) - } - if rf, ok := ret.Get(0).(func(context.Context, string, *operatorsv1alpha1.Subscription) *operatorsv1alpha1.Subscription); ok { - r0 = rf(ctx, namespace, subscription) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, *operatorsv1alpha1.Subscription) error); ok { - r1 = rf(ctx, namespace, subscription) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewMockKubeClientConnector creates a new instance of MockKubeClientConnector. 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 NewMockKubeClientConnector(t interface { - mock.TestingT - Cleanup(func()) -}, -) *MockKubeClientConnector { - mock := &MockKubeClientConnector{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/kubernetes/client/monitoring.go b/pkg/kubernetes/client/monitoring.go deleted file mode 100644 index c30b0bbe5..000000000 --- a/pkg/kubernetes/client/monitoring.go +++ /dev/null @@ -1,116 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client ... -package client - -import ( - "context" - "errors" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/discovery" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// DeleteAllMonitoringResources deletes all resources related to monitoring from k8s cluster. -func (c *Client) DeleteAllMonitoringResources(ctx context.Context, namespace string) error { - cl, err := c.kubeClient() - if err != nil { - return err - } - - if namespace == "" { - namespace = c.namespace - } - crd := &unstructured.Unstructured{} - crd.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "apiextensions.k8s.io", - Kind: "CustomResourceDefinition", - Version: "v1", - }) - err = cl.Get(ctx, types.NamespacedName{Name: "vmagents.operator.victoriametrics.com"}, crd) - if err != nil && k8serrors.IsNotFound(err) { - // Nothing to do here. Monitoring does not exists in the cluster. - return nil - } - - opts := []client.DeleteAllOfOption{ - client.MatchingLabels{"everest.percona.com/type": "monitoring"}, - client.InNamespace(namespace), - } - - for _, o := range c.monitoringResourceTypesForRemoval() { - if err := cl.DeleteAllOf(ctx, o, opts...); err != nil { - var discoveryError *discovery.ErrGroupDiscoveryFailed - if ok := errors.As(err, &discoveryError); !ok { - return err - } - } - } - - return nil -} - -// monitoringResourceTypesForRemoval returns a list of object types in k8s cluster to be removed -// when deleting all monitoring resources from a k8s cluster. -func (c *Client) monitoringResourceTypesForRemoval() []client.Object { - vmNodeScrape := &unstructured.Unstructured{} - vmNodeScrape.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "operator.victoriametrics.com", - Kind: "VMNodeScrape", - Version: "v1beta1", - }) - - vmPodScrape := &unstructured.Unstructured{} - vmPodScrape.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "operator.victoriametrics.com", - Kind: "VMPodScrape", - Version: "v1beta1", - }) - - vmAgent := &unstructured.Unstructured{} - vmAgent.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "operator.victoriametrics.com", - Kind: "VMAgent", - Version: "v1beta1", - }) - - vmServiceScrape := &unstructured.Unstructured{} - vmServiceScrape.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "operator.victoriametrics.com", - Kind: "VMServiceScrape", - Version: "v1beta1", - }) - - return []client.Object{ - &corev1.ServiceAccount{}, - &corev1.Service{}, - &appsv1.Deployment{}, - &rbacv1.ClusterRole{}, - &rbacv1.ClusterRoleBinding{}, - - vmNodeScrape, - vmPodScrape, - vmServiceScrape, - vmAgent, - } -} diff --git a/pkg/kubernetes/client/monitoring_config.go b/pkg/kubernetes/client/monitoring_config.go deleted file mode 100644 index 9f75126bb..000000000 --- a/pkg/kubernetes/client/monitoring_config.go +++ /dev/null @@ -1,50 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client ... -package client - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" -) - -// CreateMonitoringConfig creates an monitoringConfig. -func (c *Client) CreateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { - return c.customClientSet.MonitoringConfig(config.Namespace).Create(ctx, config, metav1.CreateOptions{}) -} - -// UpdateMonitoringConfig updates an monitoringConfig. -func (c *Client) UpdateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { - return c.customClientSet.MonitoringConfig(config.Namespace).Update(ctx, config, metav1.UpdateOptions{}) -} - -// GetMonitoringConfig returns the monitoringConfig. -func (c *Client) GetMonitoringConfig(ctx context.Context, namespace, name string) (*everestv1alpha1.MonitoringConfig, error) { - return c.customClientSet.MonitoringConfig(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListMonitoringConfigs returns the monitoringConfig. -func (c *Client) ListMonitoringConfigs(ctx context.Context, namespace string) (*everestv1alpha1.MonitoringConfigList, error) { - return c.customClientSet.MonitoringConfig(namespace).List(ctx, metav1.ListOptions{}) -} - -// DeleteMonitoringConfig deletes the monitoringConfig. -func (c *Client) DeleteMonitoringConfig(ctx context.Context, namespace, name string) error { - return c.customClientSet.MonitoringConfig(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/namespace.go b/pkg/kubernetes/client/namespace.go deleted file mode 100644 index 9d390d613..000000000 --- a/pkg/kubernetes/client/namespace.go +++ /dev/null @@ -1,33 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CreateNamespace creates the given namespace. -func (c *Client) CreateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) { - return c.clientset.CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{}) -} - -// GetNamespace returns a namespace. -func (c *Client) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { - return c.clientset.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) -} - -// DeleteNamespace deletes a namespace. -func (c *Client) DeleteNamespace(ctx context.Context, name string) error { - return c.clientset.CoreV1().Namespaces().Delete(ctx, name, metav1.DeleteOptions{}) -} - -// ListNamespaces returns a list of namespaces. -func (c *Client) ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) { - return c.clientset.CoreV1().Namespaces().List(ctx, opts) -} - -// UpdateNamespace updates the given namespace. -func (c *Client) UpdateNamespace(ctx context.Context, namespace *corev1.Namespace, opts metav1.UpdateOptions) (*corev1.Namespace, error) { - return c.clientset.CoreV1().Namespaces().Update(ctx, namespace, opts) -} diff --git a/pkg/kubernetes/client/node.go b/pkg/kubernetes/client/node.go deleted file mode 100644 index 7a5004bf2..000000000 --- a/pkg/kubernetes/client/node.go +++ /dev/null @@ -1,13 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetNodes returns list of nodes. -func (c *Client) GetNodes(ctx context.Context) (*corev1.NodeList, error) { - return c.clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) -} diff --git a/pkg/kubernetes/client/olm.go b/pkg/kubernetes/client/olm.go deleted file mode 100644 index b994bd0ea..000000000 --- a/pkg/kubernetes/client/olm.go +++ /dev/null @@ -1,25 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client - -import versioned "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" - -// OLM returns OLM client set. -// -//nolint:ireturn -func (c *Client) OLM() versioned.Interface { - return c.olmClientset -} diff --git a/pkg/kubernetes/client/pod.go b/pkg/kubernetes/client/pod.go deleted file mode 100644 index ea20a7563..000000000 --- a/pkg/kubernetes/client/pod.go +++ /dev/null @@ -1,28 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetPods returns list of pods. -func (c *Client) GetPods(ctx context.Context, namespace string, labelSelector *metav1.LabelSelector) (*corev1.PodList, error) { - options := metav1.ListOptions{} - if labelSelector != nil && (labelSelector.MatchLabels != nil || labelSelector.MatchExpressions != nil) { - options.LabelSelector = metav1.FormatLabelSelector(labelSelector) - } - - return c.clientset.CoreV1().Pods(namespace).List(ctx, options) -} - -// ListPods lists pods. -func (c *Client) ListPods(ctx context.Context, namespace string, options metav1.ListOptions) (*corev1.PodList, error) { - return c.clientset.CoreV1().Pods(namespace).List(ctx, options) -} - -// DeletePod deletes a pod by given name in the given namespace. -func (c *Client) DeletePod(ctx context.Context, namespace, name string) error { - return c.clientset.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/secret.go b/pkg/kubernetes/client/secret.go deleted file mode 100644 index b27df7251..000000000 --- a/pkg/kubernetes/client/secret.go +++ /dev/null @@ -1,33 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ListSecrets returns secrets. -func (c *Client) ListSecrets(ctx context.Context, namespace string) (*corev1.SecretList, error) { - return c.clientset.CoreV1().Secrets(namespace).List(ctx, metav1.ListOptions{}) -} - -// GetSecret returns secret by name. -func (c *Client) GetSecret(ctx context.Context, namespace, name string) (*corev1.Secret, error) { - return c.clientset.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// UpdateSecret updates k8s Secret. -func (c *Client) UpdateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) { - return c.clientset.CoreV1().Secrets(secret.Namespace).Update(ctx, secret, metav1.UpdateOptions{}) -} - -// CreateSecret creates k8s Secret. -func (c *Client) CreateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) { - return c.clientset.CoreV1().Secrets(secret.Namespace).Create(ctx, secret, metav1.CreateOptions{}) -} - -// DeleteSecret deletes the k8s Secret. -func (c *Client) DeleteSecret(ctx context.Context, namespace, name string) error { - return c.clientset.CoreV1().Secrets(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} diff --git a/pkg/kubernetes/client/storage.go b/pkg/kubernetes/client/storage.go deleted file mode 100644 index 8dbec74b3..000000000 --- a/pkg/kubernetes/client/storage.go +++ /dev/null @@ -1,19 +0,0 @@ -package client - -import ( - "context" - - corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GetStorageClasses returns all storage classes available in the cluster. -func (c *Client) GetStorageClasses(ctx context.Context) (*storagev1.StorageClassList, error) { - return c.clientset.StorageV1().StorageClasses().List(ctx, metav1.ListOptions{}) -} - -// GetPersistentVolumes returns Persistent Volumes available in the cluster. -func (c *Client) GetPersistentVolumes(ctx context.Context) (*corev1.PersistentVolumeList, error) { - return c.clientset.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{}) -} diff --git a/pkg/kubernetes/client/writer.go b/pkg/kubernetes/client/writer.go deleted file mode 100644 index e405a082b..000000000 --- a/pkg/kubernetes/client/writer.go +++ /dev/null @@ -1,67 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 client - -import ( - "fmt" - "io" -) - -// PrefixWriter can write text at various indentation levels. -type PrefixWriter interface { - // Writef writes text with the specified indentation level. - Writef(level int, format string, a ...interface{}) - // WriteLine writes an entire line with no indentation level. - WriteLine(a ...interface{}) - // Flush forces indentation to be reset. - Flush() -} - -// prefixWriter implements PrefixWriter. -type prefixWriter struct { - out io.Writer -} - -var _ PrefixWriter = &prefixWriter{} - -// NewPrefixWriter creates a new PrefixWriter. -func NewPrefixWriter(out io.Writer) PrefixWriter { //nolint:ireturn,nolintlint - return &prefixWriter{out: out} -} - -func (pw *prefixWriter) Writef(level int, format string, a ...interface{}) { - levelSpace := " " - prefix := "" - for range level { - prefix += levelSpace - } - - fmt.Fprintf(pw.out, prefix+format, a...) -} - -func (pw *prefixWriter) WriteLine(a ...interface{}) { - fmt.Fprintln(pw.out, a...) -} - -func (pw *prefixWriter) Flush() { - if f, ok := pw.out.(flusher); ok { - f.Flush() - } -} - -type flusher interface { - Flush() -} diff --git a/pkg/kubernetes/configmap.go b/pkg/kubernetes/configmap.go index 4729d48d1..93c01ff1a 100644 --- a/pkg/kubernetes/configmap.go +++ b/pkg/kubernetes/configmap.go @@ -4,9 +4,30 @@ import ( "context" corev1 "k8s.io/api/core/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// GetConfigMap returns k8s configmap by provided name and namespace. -func (k *Kubernetes) GetConfigMap(ctx context.Context, namespace, name string) (*corev1.ConfigMap, error) { - return k.client.GetConfigMap(ctx, namespace, name) +// GetConfigMap returns k8s configmap that matches the criteria. +func (k *Kubernetes) GetConfigMap(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.ConfigMap, error) { + result := &corev1.ConfigMap{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// CreateConfigMap creates k8s configmap. +func (k *Kubernetes) CreateConfigMap(ctx context.Context, config *corev1.ConfigMap) (*corev1.ConfigMap, error) { + if err := k.k8sClient.Create(ctx, config); err != nil { + return nil, err + } + return config, nil +} + +// UpdateConfigMap updates k8s configmap. +func (k *Kubernetes) UpdateConfigMap(ctx context.Context, config *corev1.ConfigMap) (*corev1.ConfigMap, error) { + if err := k.k8sClient.Update(ctx, config); err != nil { + return nil, err + } + return config, nil } diff --git a/pkg/kubernetes/crd.go b/pkg/kubernetes/crd.go new file mode 100644 index 000000000..339372e31 --- /dev/null +++ b/pkg/kubernetes/crd.go @@ -0,0 +1,37 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "context" + + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ListCRDs lists all CRDs that match the criteria. +func (k *Kubernetes) ListCRDs(ctx context.Context, opts ...ctrlclient.ListOption) (*apiextv1.CustomResourceDefinitionList, error) { + result := &apiextv1.CustomResourceDefinitionList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// DeleteCRD deletes a CRD that matches the criteria. +func (k *Kubernetes) DeleteCRD(ctx context.Context, obj *apiextv1.CustomResourceDefinition) error { + return k.k8sClient.Delete(ctx, obj) +} diff --git a/pkg/kubernetes/database_cluster.go b/pkg/kubernetes/database_cluster.go index bf341dba0..0c90f980a 100644 --- a/pkg/kubernetes/database_cluster.go +++ b/pkg/kubernetes/database_cluster.go @@ -27,106 +27,117 @@ import ( everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) -// ListDatabaseClusters returns list of managed database clusters. -func (k *Kubernetes) ListDatabaseClusters(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseClusterList, error) { - return k.client.ListDatabaseClusters(ctx, namespace, metav1.ListOptions{}) +// ListDatabaseClusters returns list of managed database clusters that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListDatabaseClusters(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterList, error) { + result := &everestv1alpha1.DatabaseClusterList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// ListDatabaseClusters returns list of managed database clusters that match the criteria. +// This method returns a list of simplified objects (meta only). +func (k *Kubernetes) listDatabaseClustersMeta(ctx context.Context, opts ...ctrlclient.ListOption) (*metav1.PartialObjectMetadataList, error) { + result := &metav1.PartialObjectMetadataList{} + result.SetGroupVersionKind(everestv1alpha1.GroupVersion.WithKind("DatabaseClusterList")) + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetDatabaseCluster returns database clusters by provided name. -func (k *Kubernetes) GetDatabaseCluster(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseCluster, error) { - return k.client.GetDatabaseCluster(ctx, namespace, name) +// GetDatabaseCluster returns database cluster that matches the criteria. +func (k *Kubernetes) GetDatabaseCluster(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseCluster, error) { + result := &everestv1alpha1.DatabaseCluster{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } -// DeleteDatabaseCluster deletes database cluster. -func (k *Kubernetes) DeleteDatabaseCluster(ctx context.Context, namespace, name string) error { - return k.client.DeleteDatabaseCluster(ctx, namespace, name) +// DeleteDatabaseCluster deletes database cluster that matches the criteria. +func (k *Kubernetes) DeleteDatabaseCluster(ctx context.Context, obj *everestv1alpha1.DatabaseCluster) error { + return k.k8sClient.Delete(ctx, obj) } // CreateDatabaseCluster creates database cluster. func (k *Kubernetes) CreateDatabaseCluster(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return k.client.CreateDatabaseCluster(ctx, cluster.GetNamespace(), cluster) + if err := k.k8sClient.Create(ctx, cluster); err != nil { + return nil, err + } + return cluster, nil } // UpdateDatabaseCluster updates database cluster. func (k *Kubernetes) UpdateDatabaseCluster(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) { - return k.client.UpdateDatabaseCluster(ctx, cluster.GetNamespace(), cluster) + if err := k.k8sClient.Update(ctx, cluster); err != nil { + return nil, err + } + return cluster, nil } -// DeleteDatabaseClusters deletes all database clusters in provided namespace. +// DeleteDatabaseClusters deletes all database clusters that match the criteria. // This function will wait until all clusters are deleted. -func (k *Kubernetes) DeleteDatabaseClusters(ctx context.Context, namespace string) error { - timeout := pollTimeout - list, err := k.ListDatabaseClusters(ctx, namespace) +func (k *Kubernetes) DeleteDatabaseClusters(ctx context.Context, opts ...ctrlclient.ListOption) error { + // No need to fetch full objects, we only need the fact there are objects that match the criteria(opts). + delList, err := k.listDatabaseClustersMeta(ctx, opts...) if err != nil { - k.l.Debugf("Could not list db clusters: %s", err) + k.l.Errorf("Could not list DB clusters: %s", err) + return err } - if list != nil && len(list.Items) > 0 { - // We increase the timeout if there's too many DB clusters. - const dbsCountTimeoutMultiply = 3 - newTimeout := pollTimeout * time.Duration(len(list.Items)/dbsCountTimeoutMultiply) - if newTimeout > timeout { - timeout = newTimeout - } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return nil } - k.l.Debugf("Setting DB cluster removal timeout to %s", timeout) + // We increase the timeout if there's too many DB clusters. + const countTimeoutMultiply = 3 + timeout := max(pollTimeout, pollTimeout*time.Duration(len(delList.Items)/countTimeoutMultiply)) + // need to convert ListOptions to DeleteAllOfOptions + delOpts := &ctrlclient.DeleteAllOfOptions{} + for _, opt := range opts { + opt.ApplyToList(&delOpts.ListOptions) + } + + k.l.Debugf("Setting DB clusters removal timeout to %s", timeout) return wait.PollUntilContextTimeout(ctx, pollInterval, timeout, true, func(ctx context.Context) (bool, error) { - list, err := k.ListDatabaseClusters(ctx, namespace) - if err != nil { - return false, err - } - if len(list.Items) == 0 { - return true, nil - } - for _, cluster := range list.Items { - if err := k.DeleteDatabaseCluster(ctx, cluster.GetNamespace(), cluster.GetName()); ctrlclient.IgnoreNotFound(err) != nil { + // Skip fetching the list of objects to delete again, we already have it (see code above). + if delList == nil { + var err error + if delList, err = k.listDatabaseClustersMeta(ctx, opts...); err != nil { + k.l.Errorf("Could not list DB clusters in polling: %s", err) return false, err } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return true, nil + } + } + + // Reset the list to nil to fetch it again on the next iteration. + delList = nil + + if err := k.k8sClient.DeleteAllOf(ctx, &everestv1alpha1.DatabaseCluster{}, delOpts); err != nil { + return false, err } return false, nil }) } -// PatchDatabaseCluster patches CR of managed Database cluster. -func (k *Kubernetes) PatchDatabaseCluster(cluster *everestv1alpha1.DatabaseCluster) error { - return k.client.ApplyObject(cluster) -} - -// DatabasesExist checks if databases exist in provided at least one of the provided namespaces. -// If namespaces are not provided, it checks in all namespaces. -func (k *Kubernetes) DatabasesExist(ctx context.Context, namespaces ...string) (bool, error) { - all, err := k.getAllDatabases(ctx) +// DatabasesExist checks if there are databases that match criteria exist. +func (k *Kubernetes) DatabasesExist(ctx context.Context, opts ...ctrlclient.ListOption) (bool, error) { + list, err := k.listDatabaseClustersMeta(ctx, opts...) if err != nil { return false, err } - if len(namespaces) == 0 { - return len(all) > 0, nil - } - for _, ns := range namespaces { - if _, ok := all[ns]; ok { - return true, nil - } + if list != nil && len(list.Items) > 0 { + return true, nil } return false, nil } - -func (k *Kubernetes) getAllDatabases(ctx context.Context) (map[string]everestv1alpha1.DatabaseClusterList, error) { - res := make(map[string]everestv1alpha1.DatabaseClusterList) - namespaces, err := k.GetDBNamespaces(ctx) - if ctrlclient.IgnoreNotFound(err) != nil { - return nil, err - } - for _, ns := range namespaces { - clusters, err := k.ListDatabaseClusters(ctx, ns) - if err != nil { - return nil, err - } - if len(clusters.Items) == 0 { - continue - } - res[ns] = *clusters - } - return res, nil -} diff --git a/pkg/kubernetes/database_cluster_backup.go b/pkg/kubernetes/database_cluster_backup.go index 60ef715d8..4c5008f5b 100644 --- a/pkg/kubernetes/database_cluster_backup.go +++ b/pkg/kubernetes/database_cluster_backup.go @@ -18,32 +18,48 @@ package kubernetes import ( "context" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) -// GetDatabaseClusterBackup returns database cluster backup by name. -func (k *Kubernetes) GetDatabaseClusterBackup(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterBackup, error) { - return k.client.GetDatabaseClusterBackup(ctx, namespace, name) +// GetDatabaseClusterBackup returns database cluster backup that matches the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) GetDatabaseClusterBackup(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseClusterBackup, error) { + result := &everestv1alpha1.DatabaseClusterBackup{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } -// ListDatabaseClusterBackups returns database cluster backups. -func (k *Kubernetes) ListDatabaseClusterBackups(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterBackupList, error) { - return k.client.ListDatabaseClusterBackups(ctx, namespace, options) +// ListDatabaseClusterBackups returns database cluster backups that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListDatabaseClusterBackups(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterBackupList, error) { + result := &everestv1alpha1.DatabaseClusterBackupList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } // UpdateDatabaseClusterBackup updates database cluster backup. func (k *Kubernetes) UpdateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) { - return k.client.UpdateDatabaseClusterBackup(ctx, backup) + if err := k.k8sClient.Update(ctx, backup); err != nil { + return nil, err + } + return backup, nil } -// DeleteDatabaseClusterBackup deletes database cluster backup. -func (k *Kubernetes) DeleteDatabaseClusterBackup(ctx context.Context, namespace, name string) error { - return k.client.DeleteDatabaseClusterBackup(ctx, namespace, name) +// DeleteDatabaseClusterBackup deletes database cluster backup that matches the criteria. +func (k *Kubernetes) DeleteDatabaseClusterBackup(ctx context.Context, obj *everestv1alpha1.DatabaseClusterBackup) error { + return k.k8sClient.Delete(ctx, obj) } // CreateDatabaseClusterBackup creates database cluster backup. func (k *Kubernetes) CreateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) { - return k.client.CreateDatabaseClusterBackup(ctx, backup.GetNamespace(), backup) + if err := k.k8sClient.Create(ctx, backup); err != nil { + return nil, err + } + return backup, nil } diff --git a/pkg/kubernetes/database_cluster_restore.go b/pkg/kubernetes/database_cluster_restore.go index c8f751101..257caf4a3 100644 --- a/pkg/kubernetes/database_cluster_restore.go +++ b/pkg/kubernetes/database_cluster_restore.go @@ -18,32 +18,47 @@ package kubernetes import ( "context" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) -// GetDatabaseClusterRestore returns database cluster restore by name. -func (k *Kubernetes) GetDatabaseClusterRestore(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseClusterRestore, error) { - return k.client.GetDatabaseClusterRestore(ctx, namespace, name) +// GetDatabaseClusterRestore returns database cluster restore that matches the criteria. +func (k *Kubernetes) GetDatabaseClusterRestore(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseClusterRestore, error) { + result := &everestv1alpha1.DatabaseClusterRestore{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } -// ListDatabaseClusterRestores returns database cluster restores. -func (k *Kubernetes) ListDatabaseClusterRestores(ctx context.Context, namespace string, options metav1.ListOptions) (*everestv1alpha1.DatabaseClusterRestoreList, error) { - return k.client.ListDatabaseClusterRestores(ctx, namespace, options) +// ListDatabaseClusterRestores returns database cluster restores that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListDatabaseClusterRestores(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterRestoreList, error) { + result := &everestv1alpha1.DatabaseClusterRestoreList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } // UpdateDatabaseClusterRestore updates database cluster restore. func (k *Kubernetes) UpdateDatabaseClusterRestore(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - return k.client.UpdateDatabaseClusterRestore(ctx, restore.GetNamespace(), restore) + if err := k.k8sClient.Update(ctx, restore); err != nil { + return nil, err + } + return restore, nil } -// DeleteDatabaseClusterRestore deletes database cluster restore. -func (k *Kubernetes) DeleteDatabaseClusterRestore(ctx context.Context, namespace, name string) error { - return k.client.DeleteDatabaseClusterRestore(ctx, namespace, name) +// DeleteDatabaseClusterRestore deletes database cluster restore that matches the criteria. +func (k *Kubernetes) DeleteDatabaseClusterRestore(ctx context.Context, obj *everestv1alpha1.DatabaseClusterRestore) error { + return k.k8sClient.Delete(ctx, obj) } // CreateDatabaseClusterRestore creates database cluster restore. func (k *Kubernetes) CreateDatabaseClusterRestore(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) { - return k.client.CreateDatabaseClusterRestore(ctx, restore.GetNamespace(), restore) + if err := k.k8sClient.Create(ctx, restore); err != nil { + return nil, err + } + return restore, nil } diff --git a/pkg/kubernetes/database_engine.go b/pkg/kubernetes/database_engine.go index 54f750b45..5c8cec53e 100644 --- a/pkg/kubernetes/database_engine.go +++ b/pkg/kubernetes/database_engine.go @@ -21,35 +21,48 @@ import ( "time" backoff "github.com/cenkalti/backoff/v4" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" ) -// ListDatabaseEngines returns list of managed database clusters. -func (k *Kubernetes) ListDatabaseEngines(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseEngineList, error) { - return k.client.ListDatabaseEngines(ctx, namespace) +// ListDatabaseEngines returns list of managed database engines that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListDatabaseEngines(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseEngineList, error) { + result := &everestv1alpha1.DatabaseEngineList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetDatabaseEngine returns database clusters by provided name. -func (k *Kubernetes) GetDatabaseEngine(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseEngine, error) { - return k.client.GetDatabaseEngine(ctx, namespace, name) +// GetDatabaseEngine returns database engine that matches the criteria. +func (k *Kubernetes) GetDatabaseEngine(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseEngine, error) { + result := &everestv1alpha1.DatabaseEngine{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } // UpdateDatabaseEngine updates the provided database engine. func (k *Kubernetes) UpdateDatabaseEngine(ctx context.Context, engine *everestv1alpha1.DatabaseEngine) (*everestv1alpha1.DatabaseEngine, error) { - return k.client.UpdateDatabaseEngine(ctx, engine.GetNamespace(), engine) + if err := k.k8sClient.Update(ctx, engine); err != nil { + return nil, err + } + return engine, nil } -// SetDatabaseEngineLock sets the lock on the database engine. +// SetDatabaseEngineLock sets the lock on the database engine that matches the criteria. // The lock is automatically set to false once everest-operator completes its upgrade. -func (k *Kubernetes) SetDatabaseEngineLock(ctx context.Context, namespace, name string, locked bool) error { +func (k *Kubernetes) SetDatabaseEngineLock(ctx context.Context, key ctrlclient.ObjectKey, locked bool) error { // We wrap this logic into a retry block to reduce the chances of conflicts. var b backoff.BackOff b = backoff.NewConstantBackOff(backoffInterval) b = backoff.WithMaxRetries(b, backoffMaxRetries) b = backoff.WithContext(b, ctx) return backoff.Retry(func() error { - engine, err := k.client.GetDatabaseEngine(ctx, namespace, name) + engine, err := k.GetDatabaseEngine(ctx, key) if err != nil { return err } @@ -62,7 +75,7 @@ func (k *Kubernetes) SetDatabaseEngineLock(ctx context.Context, namespace, name delete(annotations, everestv1alpha1.DatabaseOperatorUpgradeLockAnnotation) } engine.SetAnnotations(annotations) - _, err = k.client.UpdateDatabaseEngine(ctx, namespace, engine) + _, err = k.UpdateDatabaseEngine(ctx, engine) return err }, b, diff --git a/pkg/kubernetes/deployment.go b/pkg/kubernetes/deployment.go index 74eb576e7..d53bac266 100644 --- a/pkg/kubernetes/deployment.go +++ b/pkg/kubernetes/deployment.go @@ -2,26 +2,129 @@ package kubernetes import ( "context" + "errors" + "fmt" + "time" + "github.com/cenkalti/backoff/v4" appsv1 "k8s.io/api/apps/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/wait" + deploymentutil "k8s.io/kubectl/pkg/util/deployment" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// GetDeployment returns k8s deployment by provided name and namespace. -func (k *Kubernetes) GetDeployment(ctx context.Context, name, namespace string) (*appsv1.Deployment, error) { - return k.client.GetDeployment(ctx, name, namespace) +// GetDeployment returns k8s deployment that matches the criteria. +func (k *Kubernetes) GetDeployment(ctx context.Context, key ctrlclient.ObjectKey) (*appsv1.Deployment, error) { + result := &appsv1.Deployment{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } // UpdateDeployment updates a deployment and returns the updated object. func (k *Kubernetes) UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) { - return k.client.UpdateDeployment(ctx, deployment) + if err := k.k8sClient.Update(ctx, deployment); err != nil { + return nil, err + } + return deployment, nil } -// ListDeployments returns a list of deployments in the provided namespace. -func (k *Kubernetes) ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) { - return k.client.ListDeployments(ctx, namespace) +// ListDeployments returns a list of deployments that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListDeployments(ctx context.Context, opts ...ctrlclient.ListOption) (*appsv1.DeploymentList, error) { + result := &appsv1.DeploymentList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// DeleteDeployment deletes a deployment by provided name and namespace. -func (k *Kubernetes) DeleteDeployment(ctx context.Context, name, namespace string) error { - return k.client.DeleteDeployment(ctx, name, namespace) +// DeleteDeployment deletes a deployment that matches the criteria. +func (k *Kubernetes) DeleteDeployment(ctx context.Context, obj *appsv1.Deployment) error { + return k.k8sClient.Delete(ctx, obj) +} + +// RestartDeployment restarts deployment that matches the criteria. +func (k *Kubernetes) RestartDeployment(ctx context.Context, key ctrlclient.ObjectKey) error { + // Get the Deployment and add restart annotation to pod template. + // We retry this operatation since there may be update conflicts. + var b backoff.BackOff + b = backoff.NewConstantBackOff(backoffInterval) + b = backoff.WithMaxRetries(b, backoffMaxRetries) + b = backoff.WithContext(b, ctx) + if err := backoff.Retry(func() error { + // Get the deployment. + deployment, err := k.GetDeployment(ctx, key) + if err != nil { + return err + } + // Set restart annotation. + annotations := deployment.Spec.Template.Annotations + if annotations == nil { + annotations = make(map[string]string) + } + annotations[deploymentRestartAnnotation] = time.Now().Format(time.RFC3339) + deployment.Spec.Template.SetAnnotations(annotations) + // Update deployment. + if _, err := k.UpdateDeployment(ctx, deployment); err != nil { + return err + } + return nil + }, b, + ); err != nil { + return errors.Join(err, fmt.Errorf("cannot add restart annotation to deployment='%s' in namespace='%s'", key.Name, key.Namespace)) + } + // Wait for pods to be ready. + return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { + deployment, err := k.GetDeployment(ctx, key) + if err != nil { + return false, err + } + ready := deployment.Status.ReadyReplicas == deployment.Status.Replicas && + deployment.Status.Replicas == deployment.Status.UpdatedReplicas && + deployment.Status.UnavailableReplicas == 0 && + deployment.GetGeneration() == deployment.Status.ObservedGeneration + + return ready, nil + }) +} + +// WaitForRollout waits for rollout of deployment that matches the criteria. +func (k *Kubernetes) WaitForRollout(ctx context.Context, key ctrlclient.ObjectKey) error { + rolloutComplete := func(ctx context.Context) (bool, error) { + deployment, err := k.GetDeployment(ctx, key) + if err != nil { + if apierrors.IsNotFound(err) { + // Waiting for Deployment to appear + return false, nil + } + return false, err + } + + if deployment.Generation <= deployment.Status.ObservedGeneration { + cond := deploymentutil.GetDeploymentCondition(deployment.Status, appsv1.DeploymentProgressing) + if cond != nil && cond.Reason == deploymentutil.TimedOutReason { + return false, errors.New("progress deadline exceeded") + } + if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { + // Waiting for Deployment to roll out. Not all replicas have been updated + return false, nil + } + if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { + // Waiting for Deployment to roll out. Old replicas are pending termination + return false, nil + } + if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { + // Waiting for Deployment to roll out. Not all updated replicas are available + return false, nil + } + // Deployment successfully rolled out + return true, nil + } + // Waiting for Deployment to roll out: waiting for deployment spec update to be observed + return false, nil + } + return wait.PollUntilContextCancel(ctx, time.Second, true, rolloutComplete) } diff --git a/pkg/kubernetes/gen.go b/pkg/kubernetes/gen.go index c49f5b054..e560af876 100644 --- a/pkg/kubernetes/gen.go +++ b/pkg/kubernetes/gen.go @@ -1,5 +1,5 @@ // everest -// Copyright (C) 2023 Percona LLC +// Copyright (C) 2025 Percona LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,5 +15,4 @@ package kubernetes -//go:generate ../../bin/ifacemaker -f accounts.go -f backup_storage.go -f configmap.go -f database_engine.go -f deployment.go -f install_plan.go -f kubernetes.go -f monitoring_config.go -f namespace.go -f operator.go -f jwt.go -f oidc.go -f secret.go -s Kubernetes -i KubernetesConnector -p kubernetes -o kubernetes_interface.gen.go -//go:generate ../../bin/mockery --name=KubernetesConnector --case=snake --inpackage +//go:generate ../../bin/ifacemaker -f accounts.go -f backup_storage.go -f olm_catalog_source.go -f configmap.go -f olm_cluster_service_version.go -f crd.go -f database_cluster.go -f database_cluster_backup.go -f database_cluster_restore.go -f database_engine.go -f deployment.go -f olm_install_plan.go -f kubernetes.go -f monitoring_config.go -f namespace.go -f object.go -f operator.go -f jwt.go -f oidc.go -f resources.go -f secret.go -f service.go -f storage.go -f olm_subscription.go -f pod.go -s Kubernetes -i KubernetesConnector -p kubernetes -o kubernetes_interface.gen.go diff --git a/pkg/kubernetes/install_plan.go b/pkg/kubernetes/install_plan.go deleted file mode 100644 index d71e4d7bc..000000000 --- a/pkg/kubernetes/install_plan.go +++ /dev/null @@ -1,34 +0,0 @@ -package kubernetes - -import ( - "context" - "errors" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ApproveInstallPlan approves an install plan. -func (k *Kubernetes) ApproveInstallPlan(ctx context.Context, namespace, installPlanName string) (bool, error) { - ip, err := k.client.GetInstallPlan(ctx, namespace, installPlanName) - if err != nil { - return false, err - } - - k.l.Debugf("Approving install plan %s/%s", namespace, installPlanName) - - ip.Spec.Approved = true - _, err = k.client.UpdateInstallPlan(ctx, namespace, ip) - if err != nil { - var sErr *apierrors.StatusError - if ok := errors.As(err, &sErr); ok && sErr.Status().Reason == metav1.StatusReasonConflict { - // The install plan has changed. We retry to get an updated install plan. - k.l.Debugf("Retrying install plan update due to a version conflict. Error: %s", err) - return false, nil - } - - return false, err - } - - return true, nil -} diff --git a/pkg/kubernetes/jwt.go b/pkg/kubernetes/jwt.go index 87b0c45fb..0aa6734f5 100644 --- a/pkg/kubernetes/jwt.go +++ b/pkg/kubernetes/jwt.go @@ -11,6 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "github.com/percona/everest/pkg/common" ) @@ -74,7 +75,7 @@ func (k *Kubernetes) CreateRSAKeyPair(ctx context.Context) error { // Check if the secret exists? exists := false - secret, err := k.GetSecret(ctx, common.SystemNamespace, common.EverestJWTSecretName) + secret, err := k.GetSecret(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestJWTSecretName}) if err == nil { exists = true } else if !k8serrors.IsNotFound(err) { @@ -97,7 +98,7 @@ func (k *Kubernetes) CreateRSAKeyPair(ctx context.Context) error { return err } // Restart the deployment to pick up the new secret. - return k.RestartDeployment(ctx, common.PerconaEverestDeploymentName, common.SystemNamespace) + return k.RestartDeployment(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.PerconaEverestDeploymentName}) } // Otherwise, update the secret. @@ -107,5 +108,5 @@ func (k *Kubernetes) CreateRSAKeyPair(ctx context.Context) error { return err } // Restart the deployment to pick up the new secret. - return k.RestartDeployment(ctx, common.PerconaEverestDeploymentName, common.SystemNamespace) + return k.RestartDeployment(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.PerconaEverestDeploymentName}) } diff --git a/pkg/kubernetes/kubeconfig.go b/pkg/kubernetes/kubeconfig.go deleted file mode 100644 index a23d78c70..000000000 --- a/pkg/kubernetes/kubeconfig.go +++ /dev/null @@ -1,30 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 kubernetes ... -package kubernetes - -import corev1 "k8s.io/api/core/v1" - -// GenerateKubeConfigWithToken returns a kubeconfig with the token as provided in the secret. -func (k *Kubernetes) GenerateKubeConfigWithToken(user string, secret *corev1.Secret) (string, error) { - kubeConfig, err := k.client.GenerateKubeConfigWithToken(user, secret) - if err != nil { - k.l.Errorf("failed generating kubeconfig: %v", err) - return "", err - } - - return string(kubeConfig), nil -} diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 4507b3415..b34a4efa0 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -19,25 +19,29 @@ package kubernetes import ( "context" "errors" - "net/http" - "slices" + "fmt" + "os" + "path/filepath" "sort" "strings" + "sync" "time" - "github.com/cenkalti/backoff/v4" olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "go.uber.org/zap" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apimachinery/pkg/version" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/discovery" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/pkg/common" - "github.com/percona/everest/pkg/kubernetes/client" ) type ( @@ -59,19 +63,11 @@ const ( // ClusterTypeGeneric is a generic type. ClusterTypeGeneric ClusterType = "generic" - // EverestDBNamespacesEnvVar is the name of the environment variable that - // contains the list of monitored namespaces. - EverestDBNamespacesEnvVar = "DB_NAMESPACES" - // OLMNamespace is the namespace where OLM is installed. - OLMNamespace = "everest-olm" - olmOperatorName = "olm-operator" + OLMNamespace = "everest-olm" openShiftCatalogNamespace = "openshift-marketplace" - // APIVersionCoreosV1 constant for some API requests. - APIVersionCoreosV1 = "operators.coreos.com/v1" - pollInterval = 5 * time.Second pollTimeout = 15 * time.Minute @@ -80,130 +76,158 @@ const ( backoffInterval = 5 * time.Second backoffMaxRetries = 5 - requestTimeout = 5 * time.Second - maxIdleConns = 1 - idleConnTimeout = 10 * time.Second + defaultQPSLimit = 100 + defaultBurstLimit = 150 ) -// ErrEmptyVersionTag Got an empty version tag from GitHub API. -var ErrEmptyVersionTag = errors.New("got an empty version tag from Github") +var once sync.Once // Kubernetes is a client for Kubernetes. type Kubernetes struct { - client client.KubeClientConnector + k8sClient ctrlclient.Client l *zap.SugaredLogger - namespace string - httpClient *http.Client + restConfig *rest.Config kubeconfig string + // it is required for handling plain runtime.Objects (ApplyManifestFile) + // WARNING: do not access this field directly, use getDiscoveryClient() instead. + // This field is lazy initialized because it is not always needed. + discoveryClient discovery.DiscoveryInterface } // Kubeconfig returns the path to the kubeconfig. +// This value is available only if the client was created with New() function. func (k *Kubernetes) Kubeconfig() string { return k.kubeconfig } -// NodeSummaryNode holds information about Node inside Node's summary. -type NodeSummaryNode struct { - FileSystem NodeFileSystemSummary `json:"fs,omitempty"` -} - -// NodeSummary holds summary of the Node. -// One gets this by requesting Kubernetes API endpoint: -// /v1/nodes//proxy/stats/summary. -type NodeSummary struct { - Node NodeSummaryNode `json:"node,omitempty"` -} +// New returns new Kubernetes object based on provided kubeconfig. +func New(kubeconfigPath string, l *zap.SugaredLogger) (KubernetesConnector, error) { + home := os.Getenv("HOME") + path := strings.ReplaceAll(kubeconfigPath, "~", home) + path = filepath.Clean(path) + fileData, err := os.ReadFile(path) + if err != nil { + return nil, errors.Join(err, errors.New("could not read kubeconfig file")) + } -// NodeFileSystemSummary holds a summary of Node's filesystem. -type NodeFileSystemSummary struct { - UsedBytes uint64 `json:"usedBytes,omitempty"` -} + restConfig, err := clientcmd.RESTConfigFromKubeConfig(fileData) + if err != nil { + return nil, err + } -// New returns new Kubernetes object. -func New(kubeconfigPath string, l *zap.SugaredLogger) (*Kubernetes, error) { - client, err := client.NewFromKubeConfig(kubeconfigPath, l) + restConfig.QPS = defaultQPSLimit + restConfig.Burst = defaultBurstLimit + k8client, err := ctrlclient.New(restConfig, getKubernetesClientOptions()) if err != nil { return nil, err } return &Kubernetes{ - client: client, - l: l.With("component", "kubernetes"), - httpClient: &http.Client{ - Timeout: requestTimeout, - Transport: &http.Transport{ - MaxIdleConns: maxIdleConns, - IdleConnTimeout: idleConnTimeout, - }, - }, - kubeconfig: kubeconfigPath, + k8sClient: k8client, + l: l.With("component", "kubernetes"), + restConfig: restConfig, + kubeconfig: path, }, nil } // NewInCluster creates a new kubernetes client using incluster authentication. -func NewInCluster(l *zap.SugaredLogger) (*Kubernetes, error) { - client, err := client.NewInCluster() +func NewInCluster(l *zap.SugaredLogger) (KubernetesConnector, error) { + restConfig := ctrl.GetConfigOrDie() + restConfig.QPS = defaultQPSLimit + restConfig.Burst = defaultBurstLimit + + k8sclient, err := ctrlclient.New(restConfig, getKubernetesClientOptions()) if err != nil { return nil, err } + return &Kubernetes{ - client: client, - l: l, - namespace: client.Namespace(), + k8sClient: k8sclient, + l: l.With("component", "kubernetes"), + restConfig: restConfig, }, nil } +// CreateScheme creates a new runtime.Scheme. +// It registers all necessary types: +// - standard client-go types +// - Everest CRDs +// - OLM CRDs +// - API extensions +func CreateScheme() *runtime.Scheme { + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(everestv1alpha1.AddToScheme(scheme)) + utilruntime.Must(olmv1alpha1.AddToScheme(scheme)) + utilruntime.Must(apiextv1.AddToScheme(scheme)) + return scheme +} + +func getKubernetesClientOptions() ctrlclient.Options { + return ctrlclient.Options{ + Scheme: CreateScheme(), + Cache: nil, // disable cache + } +} + +func (k *Kubernetes) getDiscoveryClient() discovery.DiscoveryInterface { + once.Do(func() { + httpClient, err := rest.HTTPClientFor(k.restConfig) + if err != nil { + panic(err) + } + + k.discoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(k.restConfig, httpClient) + if err != nil { + panic(err) + } + }) + return k.discoveryClient +} + // Config returns *rest.Config. func (k *Kubernetes) Config() *rest.Config { - return k.client.Config() + return k.restConfig } -// NewEmpty returns new Kubernetes object. +// NewEmpty returns new empty Kubernetes object. +// useful for testing. func NewEmpty(l *zap.SugaredLogger) *Kubernetes { return &Kubernetes{ - client: &client.Client{}, - l: l.With("component", "kubernetes"), - httpClient: &http.Client{ - Timeout: requestTimeout, - Transport: &http.Transport{ - MaxIdleConns: maxIdleConns, - IdleConnTimeout: idleConnTimeout, - }, - }, + l: l.With("component", "kubernetes"), } } -// WithClient sets the client connector. -func (k *Kubernetes) WithClient(c client.KubeClientConnector) *Kubernetes { - k.client = c +// WithKubernetesClient sets the k8s client. +func (k *Kubernetes) WithKubernetesClient(c ctrlclient.Client) *Kubernetes { + k.k8sClient = c return k } -// Namespace returns the current namespace. +// Namespace returns the Everest system namespace. func (k *Kubernetes) Namespace() string { - return k.namespace -} - -// ClusterName returns the name of the k8s cluster. -func (k *Kubernetes) ClusterName() string { - return k.client.ClusterName() + return common.SystemNamespace } // GetEverestID returns the ID of the namespace where everest is deployed. func (k *Kubernetes) GetEverestID(ctx context.Context) (string, error) { - namespace, err := k.client.GetNamespace(ctx, k.namespace) + namespace, err := k.GetNamespace(ctx, types.NamespacedName{Name: k.Namespace()}) if err != nil { - return "", err + return "", errors.Join(err, fmt.Errorf("can't get namespace='%s'", k.Namespace())) + } + + if namespace == nil { + return "", fmt.Errorf("can't get namespace='%s'", k.Namespace()) } return string(namespace.UID), nil } func (k *Kubernetes) isOpenshift(ctx context.Context) (bool, error) { - _, err := k.client.GetNamespace(ctx, openShiftCatalogNamespace) - if err == nil { - return true, nil + ns, err := k.GetNamespace(ctx, types.NamespacedName{Name: openShiftCatalogNamespace}) + if err != nil { + return false, ctrlclient.IgnoreNotFound(err) } - return false, ctrlclient.IgnoreNotFound(err) + return ns != nil, nil } // GetClusterType tries to guess the underlying kubernetes cluster based on storage class. @@ -215,7 +239,7 @@ func (k *Kubernetes) GetClusterType(ctx context.Context) (ClusterType, error) { } // For other types, we will check the storage classes. - storageClasses, err := k.client.GetStorageClasses(ctx) + storageClasses, err := k.ListStorageClasses(ctx) if err != nil { return ClusterTypeUnknown, err } @@ -235,40 +259,6 @@ func (k *Kubernetes) GetClusterType(ctx context.Context) (ClusterType, error) { return ClusterTypeGeneric, nil } -// GetCatalogSource returns catalog source. -func (k *Kubernetes) GetCatalogSource(ctx context.Context, name, namespace string) (*olmv1alpha1.CatalogSource, error) { - return k.client.OLM().OperatorsV1alpha1().CatalogSources(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// DeleteCatalogSource deletes catalog source. -func (k *Kubernetes) DeleteCatalogSource(ctx context.Context, name, namespace string) error { - return k.client.OLM().OperatorsV1alpha1().CatalogSources(namespace).Delete(ctx, name, metav1.DeleteOptions{}) -} - -// GetSubscription returns subscription. -func (k *Kubernetes) GetSubscription(ctx context.Context, name, namespace string) (*olmv1alpha1.Subscription, error) { - return k.client.OLM().OperatorsV1alpha1().Subscriptions(namespace).Get(ctx, name, metav1.GetOptions{}) -} - -// ListSubscriptions lists subscriptions. -func (k *Kubernetes) ListSubscriptions(ctx context.Context, namespace string) (*olmv1alpha1.SubscriptionList, error) { - return k.client.OLM().OperatorsV1alpha1().Subscriptions(namespace).List(ctx, metav1.ListOptions{}) -} - -// InstallOperatorRequest holds the fields to make an operator install request. -type InstallOperatorRequest struct { - Namespace string - Name string - OperatorGroup string - CatalogSource string - CatalogSourceNamespace string - Channel string - InstallPlanApproval olmv1alpha1.Approval - StartingCSV string - TargetNamespaces []string - SubscriptionConfig *olmv1alpha1.SubscriptionConfig -} - func mergeNamespacesEnvVar(str1, str2 string) string { ns1 := strings.Split(str1, ",") ns2 := strings.Split(str2, ",") @@ -297,141 +287,3 @@ func mergeNamespacesEnvVar(str1, str2 string) string { return strings.Join(namespaces, ",") } - -// GetServerVersion returns server version. -func (k *Kubernetes) GetServerVersion() (*version.Info, error) { - return k.client.GetServerVersion() -} - -// GetClusterServiceVersion retrieves a ClusterServiceVersion by namespaced name. -func (k *Kubernetes) GetClusterServiceVersion( - ctx context.Context, - key types.NamespacedName, -) (*olmv1alpha1.ClusterServiceVersion, error) { - return k.client.GetClusterServiceVersion(ctx, key) -} - -// ListClusterServiceVersion list all CSVs for the given namespace. -func (k *Kubernetes) ListClusterServiceVersion( - ctx context.Context, - namespace string, -) (*olmv1alpha1.ClusterServiceVersionList, error) { - return k.client.ListClusterServiceVersion(ctx, namespace) -} - -// ListCRDs lists all CRDs. -func (k *Kubernetes) ListCRDs( - ctx context.Context, -) (*apiextv1.CustomResourceDefinitionList, error) { - return k.client.ListCRDs(ctx, &metav1.LabelSelector{}) -} - -// DeleteCRD deletes a CRD by name. -func (k *Kubernetes) DeleteCRD( - ctx context.Context, - name string, -) error { - return k.client.DeleteCRD(ctx, name) -} - -// DeleteClusterServiceVersion deletes a ClusterServiceVersion. -func (k *Kubernetes) DeleteClusterServiceVersion( - ctx context.Context, - key types.NamespacedName, -) error { - return k.client.DeleteClusterServiceVersion(ctx, key) -} - -// DeleteSubscription deletes a subscription by namespaced name. -func (k *Kubernetes) DeleteSubscription( - ctx context.Context, - key types.NamespacedName, -) error { - return k.client.DeleteSubscription(ctx, key) -} - -// RestartDeployment restarts the given deployment. -func (k *Kubernetes) RestartDeployment(ctx context.Context, name, namespace string) error { - // Get the Deployment and add restart annotation to pod template. - // We retry this operatation since there may be update conflicts. - var b backoff.BackOff - b = backoff.NewConstantBackOff(backoffInterval) - b = backoff.WithMaxRetries(b, backoffMaxRetries) - b = backoff.WithContext(b, ctx) - if err := backoff.Retry(func() error { - // Get the deployment. - deployment, err := k.GetDeployment(ctx, name, namespace) - if err != nil { - return err - } - // Set restart annotation. - annotations := deployment.Spec.Template.Annotations - if annotations == nil { - annotations = make(map[string]string) - } - annotations[deploymentRestartAnnotation] = time.Now().Format(time.RFC3339) - deployment.Spec.Template.SetAnnotations(annotations) - // Update deployment. - if _, err := k.client.UpdateDeployment(ctx, deployment); err != nil { - return err - } - return nil - }, b, - ); err != nil { - return errors.Join(err, errors.New("cannot add restart annotation to deployment")) - } - // Wait for pods to be ready. - return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - deployment, err := k.GetDeployment(ctx, name, namespace) - if err != nil { - return false, err - } - ready := deployment.Status.ReadyReplicas == deployment.Status.Replicas && - deployment.Status.Replicas == deployment.Status.UpdatedReplicas && - deployment.Status.UnavailableReplicas == 0 && - deployment.GetGeneration() == deployment.Status.ObservedGeneration - - return ready, nil - }) -} - -// ApplyManifestFile accepts manifest file contents, parses into []runtime.Object -// and applies them against the cluster. -func (k *Kubernetes) ApplyManifestFile(files []byte, namespace string) error { - return k.client.ApplyManifestFile(files, namespace) -} - -// GetDBNamespaces returns a list of namespaces that are monitored by the Everest operator. -func (k *Kubernetes) GetDBNamespaces(ctx context.Context) ([]string, error) { - // List all namespaces managed by everest. - namespaceList, err := k.ListNamespaces(ctx, metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - common.KubernetesManagedByLabel: common.Everest, - }, - }), - }) - if err != nil { - return nil, errors.Join(err, errors.New("failed to get watched namespaces")) - } - internalNs := []string{common.SystemNamespace, common.MonitoringNamespace} - result := make([]string, 0, len(namespaceList.Items)) - for _, ns := range namespaceList.Items { - if slices.Contains(internalNs, ns.GetName()) { - continue - } - result = append(result, ns.GetName()) - } - return result, nil -} - -// WaitForRollout waits for rollout of a provided deployment in the provided namespace. -func (k *Kubernetes) WaitForRollout(ctx context.Context, name, namespace string) error { - return k.client.DoRolloutWait(ctx, types.NamespacedName{Name: name, Namespace: namespace}) -} - -// DeleteManifestFile accepts manifest file contents, parses into []runtime.Object -// and deletes them from the cluster. -func (k *Kubernetes) DeleteManifestFile(fileBytes []byte, namespace string) error { - return k.client.DeleteManifestFile(fileBytes, namespace) -} diff --git a/pkg/kubernetes/kubernetes_interface.gen.go b/pkg/kubernetes/kubernetes_interface.gen.go index 7f90b66df..fdcbab817 100644 --- a/pkg/kubernetes/kubernetes_interface.gen.go +++ b/pkg/kubernetes/kubernetes_interface.gen.go @@ -9,157 +9,221 @@ import ( olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/version" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" everestv1alpha1 "github.com/percona/everest-operator/api/v1alpha1" "github.com/percona/everest/pkg/accounts" "github.com/percona/everest/pkg/common" - "github.com/percona/everest/pkg/kubernetes/client" ) // KubernetesConnector ... type KubernetesConnector interface { - // Accounts returns a new client for managing everest user accounts. - // - //nolint:ireturn,stylecheck + // Accounts returns an implementation of the accounts interface that + // manages everest accounts directly via ConfigMaps. Accounts() accounts.Interface - // ListBackupStorages returns list of managed backup storages. - ListBackupStorages(ctx context.Context, namespace string) (*everestv1alpha1.BackupStorageList, error) - // GetBackupStorage returns backup storages by provided name. - GetBackupStorage(ctx context.Context, namespace, name string) (*everestv1alpha1.BackupStorage, error) - // CreateBackupStorage returns backup storages by provided name. + // ListBackupStorages returns list of managed backup storages in a given namespace. + // This method returns a list of full objects (meta and spec). + ListBackupStorages(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.BackupStorageList, error) + // GetBackupStorage returns backup storages by provided name and namespace. + GetBackupStorage(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.BackupStorage, error) + // CreateBackupStorage creates backup storages by provided object. CreateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) - // UpdateBackupStorage returns backup storages by provided name. + // UpdateBackupStorage updates backup storages by provided new object. UpdateBackupStorage(ctx context.Context, storage *everestv1alpha1.BackupStorage) (*everestv1alpha1.BackupStorage, error) - // DeleteBackupStorage returns backup storages by provided name. - DeleteBackupStorage(ctx context.Context, namespace, name string) error + // DeleteBackupStorage returns backup storages by provided name and namespace. + DeleteBackupStorage(ctx context.Context, obj *everestv1alpha1.BackupStorage) error // DeleteBackupStorages deletes all backup storages in provided namespace. // This function will wait until all storages are deleted. - DeleteBackupStorages(ctx context.Context, namespace string) error - // IsBackupStorageUsed checks if a backup storage in a given namespace is used by any clusters - // in that namespace. - // - //nolint:cyclop - IsBackupStorageUsed(ctx context.Context, namespace, name string) (bool, error) - // GetConfigMap returns k8s configmap by provided name and namespace. - GetConfigMap(ctx context.Context, namespace, name string) (*corev1.ConfigMap, error) - // ListDatabaseEngines returns list of managed database clusters. - ListDatabaseEngines(ctx context.Context, namespace string) (*everestv1alpha1.DatabaseEngineList, error) - // GetDatabaseEngine returns database clusters by provided name. - GetDatabaseEngine(ctx context.Context, namespace, name string) (*everestv1alpha1.DatabaseEngine, error) + DeleteBackupStorages(ctx context.Context, opts ...ctrlclient.ListOption) error + // IsBackupStorageUsed checks if a backup storage that matches the criteria is used by any DB clusters. + IsBackupStorageUsed(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) + // GetCatalogSource returns catalog source that matches the criteria. + GetCatalogSource(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.CatalogSource, error) + // DeleteCatalogSource deletes catalog source that matches the criteria. + DeleteCatalogSource(ctx context.Context, obj *olmv1alpha1.CatalogSource) error + // GetConfigMap returns k8s configmap that matches the criteria. + GetConfigMap(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.ConfigMap, error) + // CreateConfigMap creates k8s configmap. + CreateConfigMap(ctx context.Context, config *corev1.ConfigMap) (*corev1.ConfigMap, error) + // UpdateConfigMap updates k8s configmap. + UpdateConfigMap(ctx context.Context, config *corev1.ConfigMap) (*corev1.ConfigMap, error) + // GetClusterServiceVersion retrieves a ClusterServiceVersion that matches the criteria. + GetClusterServiceVersion(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.ClusterServiceVersion, error) + // ListClusterServiceVersion list all CSVs that match the criteria. + // This method returns a list of full objects (meta and spec). + ListClusterServiceVersion(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.ClusterServiceVersionList, error) + // DeleteClusterServiceVersion deletes a ClusterServiceVersion that matches the criteria. + DeleteClusterServiceVersion(ctx context.Context, obj *olmv1alpha1.ClusterServiceVersion) error + // DeleteClusterServiceVersions deletes all ClusterServiceVersion that match the criteria. + // This function will wait until all ClusterServiceVersion are deleted. + DeleteClusterServiceVersions(ctx context.Context, opts ...ctrlclient.ListOption) error + // ListCRDs lists all CRDs that match the criteria. + ListCRDs(ctx context.Context, opts ...ctrlclient.ListOption) (*apiextv1.CustomResourceDefinitionList, error) + // DeleteCRD deletes a CRD that matches the criteria. + DeleteCRD(ctx context.Context, obj *apiextv1.CustomResourceDefinition) error + // ListDatabaseClusters returns list of managed database clusters that match the criteria. + // This method returns a list of full objects (meta and spec). + ListDatabaseClusters(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterList, error) + // GetDatabaseCluster returns database cluster that matches the criteria. + GetDatabaseCluster(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseCluster, error) + // DeleteDatabaseCluster deletes database cluster that matches the criteria. + DeleteDatabaseCluster(ctx context.Context, obj *everestv1alpha1.DatabaseCluster) error + // CreateDatabaseCluster creates database cluster. + CreateDatabaseCluster(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) + // UpdateDatabaseCluster updates database cluster. + UpdateDatabaseCluster(ctx context.Context, cluster *everestv1alpha1.DatabaseCluster) (*everestv1alpha1.DatabaseCluster, error) + // DeleteDatabaseClusters deletes all database clusters that match the criteria. + // This function will wait until all clusters are deleted. + DeleteDatabaseClusters(ctx context.Context, opts ...ctrlclient.ListOption) error + // DatabasesExist checks if there are databases that match criteria exist. + DatabasesExist(ctx context.Context, opts ...ctrlclient.ListOption) (bool, error) + // GetDatabaseClusterBackup returns database cluster backup that matches the criteria. + // This method returns a list of full objects (meta and spec). + GetDatabaseClusterBackup(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseClusterBackup, error) + // ListDatabaseClusterBackups returns database cluster backups that match the criteria. + // This method returns a list of full objects (meta and spec). + ListDatabaseClusterBackups(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterBackupList, error) + // UpdateDatabaseClusterBackup updates database cluster backup. + UpdateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) + // DeleteDatabaseClusterBackup deletes database cluster backup that matches the criteria. + DeleteDatabaseClusterBackup(ctx context.Context, obj *everestv1alpha1.DatabaseClusterBackup) error + // CreateDatabaseClusterBackup creates database cluster backup. + CreateDatabaseClusterBackup(ctx context.Context, backup *everestv1alpha1.DatabaseClusterBackup) (*everestv1alpha1.DatabaseClusterBackup, error) + // GetDatabaseClusterRestore returns database cluster restore that matches the criteria. + GetDatabaseClusterRestore(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseClusterRestore, error) + // ListDatabaseClusterRestores returns database cluster restores that match the criteria. + // This method returns a list of full objects (meta and spec). + ListDatabaseClusterRestores(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseClusterRestoreList, error) + // UpdateDatabaseClusterRestore updates database cluster restore. + UpdateDatabaseClusterRestore(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) + // DeleteDatabaseClusterRestore deletes database cluster restore that matches the criteria. + DeleteDatabaseClusterRestore(ctx context.Context, obj *everestv1alpha1.DatabaseClusterRestore) error + // CreateDatabaseClusterRestore creates database cluster restore. + CreateDatabaseClusterRestore(ctx context.Context, restore *everestv1alpha1.DatabaseClusterRestore) (*everestv1alpha1.DatabaseClusterRestore, error) + // ListDatabaseEngines returns list of managed database engines that match the criteria. + // This method returns a list of full objects (meta and spec). + ListDatabaseEngines(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.DatabaseEngineList, error) + // GetDatabaseEngine returns database engine that matches the criteria. + GetDatabaseEngine(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.DatabaseEngine, error) // UpdateDatabaseEngine updates the provided database engine. UpdateDatabaseEngine(ctx context.Context, engine *everestv1alpha1.DatabaseEngine) (*everestv1alpha1.DatabaseEngine, error) - // SetDatabaseEngineLock sets the lock on the database engine. + // SetDatabaseEngineLock sets the lock on the database engine that matches the criteria. // The lock is automatically set to false once everest-operator completes its upgrade. - SetDatabaseEngineLock(ctx context.Context, namespace, name string, locked bool) error - // GetDeployment returns k8s deployment by provided name and namespace. - GetDeployment(ctx context.Context, name, namespace string) (*appsv1.Deployment, error) + SetDatabaseEngineLock(ctx context.Context, key ctrlclient.ObjectKey, locked bool) error + // GetDeployment returns k8s deployment that matches the criteria. + GetDeployment(ctx context.Context, key ctrlclient.ObjectKey) (*appsv1.Deployment, error) // UpdateDeployment updates a deployment and returns the updated object. UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) - // ListDeployments returns a list of deployments in the provided namespace. - ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) - // DeleteDeployment deletes a deployment by provided name and namespace. - DeleteDeployment(ctx context.Context, name, namespace string) error - // ApproveInstallPlan approves an install plan. - ApproveInstallPlan(ctx context.Context, namespace, installPlanName string) (bool, error) + // ListDeployments returns a list of deployments that match the criteria. + // This method returns a list of full objects (meta and spec). + ListDeployments(ctx context.Context, opts ...ctrlclient.ListOption) (*appsv1.DeploymentList, error) + // DeleteDeployment deletes a deployment that matches the criteria. + DeleteDeployment(ctx context.Context, obj *appsv1.Deployment) error + // RestartDeployment restarts deployment that matches the criteria. + RestartDeployment(ctx context.Context, key ctrlclient.ObjectKey) error + // WaitForRollout waits for rollout of deployment that matches the criteria. + WaitForRollout(ctx context.Context, key ctrlclient.ObjectKey) error + // GetInstallPlan retrieves an OLM install plan that matches the criteria. + GetInstallPlan(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.InstallPlan, error) + // UpdateInstallPlan updates OLM install plan. + UpdateInstallPlan(ctx context.Context, installPlan *olmv1alpha1.InstallPlan) (*olmv1alpha1.InstallPlan, error) + // ApproveInstallPlan approves OLM install plan that matches the criteria. + ApproveInstallPlan(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) // Kubeconfig returns the path to the kubeconfig. + // This value is available only if the client was created with New() function. Kubeconfig() string // Config returns *rest.Config. Config() *rest.Config - // WithClient sets the client connector. - WithClient(c client.KubeClientConnector) *Kubernetes - // Namespace returns the current namespace. + // WithKubernetesClient sets the k8s client. + WithKubernetesClient(c ctrlclient.Client) *Kubernetes + // Namespace returns the Everest system namespace. Namespace() string - // ClusterName returns the name of the k8s cluster. - ClusterName() string // GetEverestID returns the ID of the namespace where everest is deployed. GetEverestID(ctx context.Context) (string, error) // GetClusterType tries to guess the underlying kubernetes cluster based on storage class. GetClusterType(ctx context.Context) (ClusterType, error) - // GetCatalogSource returns catalog source. - GetCatalogSource(ctx context.Context, name, namespace string) (*olmv1alpha1.CatalogSource, error) - // DeleteCatalogSource deletes catalog source. - DeleteCatalogSource(ctx context.Context, name, namespace string) error - // GetSubscription returns subscription. - GetSubscription(ctx context.Context, name, namespace string) (*olmv1alpha1.Subscription, error) - // ListSubscriptions lists subscriptions. - ListSubscriptions(ctx context.Context, namespace string) (*olmv1alpha1.SubscriptionList, error) - // GetServerVersion returns server version. - GetServerVersion() (*version.Info, error) - // GetClusterServiceVersion retrieves a ClusterServiceVersion by namespaced name. - GetClusterServiceVersion(ctx context.Context, key types.NamespacedName) (*olmv1alpha1.ClusterServiceVersion, error) - // ListClusterServiceVersion list all CSVs for the given namespace. - ListClusterServiceVersion(ctx context.Context, namespace string) (*olmv1alpha1.ClusterServiceVersionList, error) - // ListCRDs lists all CRDs. - ListCRDs(ctx context.Context) (*apiextv1.CustomResourceDefinitionList, error) - // DeleteCRD deletes a CRD by name. - DeleteCRD(ctx context.Context, name string) error - // DeleteClusterServiceVersion deletes a ClusterServiceVersion. - DeleteClusterServiceVersion(ctx context.Context, key types.NamespacedName) error - // DeleteSubscription deletes a subscription by namespaced name. - DeleteSubscription(ctx context.Context, key types.NamespacedName) error - // RestartDeployment restarts the given deployment. - RestartDeployment(ctx context.Context, name, namespace string) error - // ApplyManifestFile accepts manifest file contents, parses into []runtime.Object - // and applies them against the cluster. - ApplyManifestFile(files []byte, namespace string) error - // GetDBNamespaces returns a list of namespaces that are monitored by the Everest operator. - GetDBNamespaces(ctx context.Context) ([]string, error) - // WaitForRollout waits for rollout of a provided deployment in the provided namespace. - WaitForRollout(ctx context.Context, name, namespace string) error - // DeleteManifestFile accepts manifest file contents, parses into []runtime.Object - // and deletes them from the cluster. - DeleteManifestFile(fileBytes []byte, namespace string) error - // ListMonitoringConfigs returns list of managed monitoring configs. - ListMonitoringConfigs(ctx context.Context, namespace string) (*everestv1alpha1.MonitoringConfigList, error) - // GetMonitoringConfig returns monitoring configs by provided name. - GetMonitoringConfig(ctx context.Context, namespace, name string) (*everestv1alpha1.MonitoringConfig, error) - // CreateMonitoringConfig returns monitoring configs by provided name. - CreateMonitoringConfig(ctx context.Context, storage *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) - // UpdateMonitoringConfig returns monitoring configs by provided name. - UpdateMonitoringConfig(ctx context.Context, storage *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) - // DeleteMonitoringConfig returns monitoring configs by provided name. - DeleteMonitoringConfig(ctx context.Context, namespace, name string) error - // DeleteMonitoringConfigs deletes all monitoring configs in provided namespace. + // ListMonitoringConfigs returns list of managed monitoring configs that match the criteria. + // This method returns a list of full objects (meta and spec). + ListMonitoringConfigs(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.MonitoringConfigList, error) + // GetMonitoringConfig returns monitoring configs that matches the criteria. + GetMonitoringConfig(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.MonitoringConfig, error) + // CreateMonitoringConfig creates monitoring config. + CreateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) + // UpdateMonitoringConfig updates monitoring config. + UpdateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) + // DeleteMonitoringConfig deletes monitoring config that matches the criteria. + DeleteMonitoringConfig(ctx context.Context, obj *everestv1alpha1.MonitoringConfig) error + // DeleteMonitoringConfigs deletes monitoring configs that matches the criteria. // This function will wait until all configs are deleted. - DeleteMonitoringConfigs(ctx context.Context, namespace string) error - // IsMonitoringConfigUsed checks if a monitoring config is used by any database cluster in the provided namespace. - IsMonitoringConfigUsed(ctx context.Context, namespace, name string) (bool, error) - // GetMonitoringConfigsBySecretName returns a list of monitoring configs which use - // the provided secret name. - GetMonitoringConfigsBySecretName(ctx context.Context, namespace, secretName string) ([]*everestv1alpha1.MonitoringConfig, error) + DeleteMonitoringConfigs(ctx context.Context, opts ...ctrlclient.ListOption) error + // IsMonitoringConfigUsed checks if a monitoring config that matches the criteria is used by any database cluster. + IsMonitoringConfigUsed(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) // CreateNamespace creates the given namespace. - CreateNamespace(ctx context.Context, namespace *corev1.Namespace) error - // GetNamespace returns a namespace. - GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) - // DeleteNamespace deletes a namespace. - DeleteNamespace(ctx context.Context, name string) error - // ListNamespaces lists all namespaces. - ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) + CreateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) + // GetNamespace returns a namespace that matches the criteria. + GetNamespace(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Namespace, error) + // GetDBNamespaces returns a list of DB namespaces that managed by the Everest and match the criteria. + GetDBNamespaces(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.NamespaceList, error) + // DeleteNamespace deletes a namespace that matches the criteria. + DeleteNamespace(ctx context.Context, obj *corev1.Namespace) error + // ListNamespaces lists all namespaces that match the criteria. + // This method returns a list of full objects (meta and spec). + ListNamespaces(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.NamespaceList, error) // UpdateNamespace updates the given namespace. - UpdateNamespace(ctx context.Context, namespace *corev1.Namespace, opts metav1.UpdateOptions) (*corev1.Namespace, error) - // OperatorInstalledVersion returns the installed version of operator by name. - OperatorInstalledVersion(ctx context.Context, namespace, name string) (*goversion.Version, error) + UpdateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) + // ApplyManifestFile accepts manifest file contents, parses into []runtime.Object + // and applies them against the cluster. + ApplyManifestFile(ctx context.Context, fileBytes []byte, namespace string, ignoreObjects ...ctrlclient.Object) error + // ApplyObject applies object. + ApplyObject(obj runtime.Object) error + // GetInstalledOperatorVersion returns the version of installed operator that matches the criteria. + GetInstalledOperatorVersion(ctx context.Context, key ctrlclient.ObjectKey) (*goversion.Version, error) + // ListInstalledOperators returns the list of installed operators that match the criteria. + ListInstalledOperators(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.SubscriptionList, error) // CreateRSAKeyPair creates a new RSA key pair and stores it in a secret. CreateRSAKeyPair(ctx context.Context) error // UpdateEverestSettings accepts the full list of Everest settings and updates the settings. UpdateEverestSettings(ctx context.Context, settings common.EverestSettings) error // GetEverestSettings returns Everest settings. GetEverestSettings(ctx context.Context) (common.EverestSettings, error) - // ListSecrets returns secret by name. - ListSecrets(ctx context.Context, namespace string) (*corev1.SecretList, error) - // GetSecret returns a secret by name. - GetSecret(ctx context.Context, namespace, name string) (*corev1.Secret, error) + // GetAllClusterResources goes through all cluster nodes and sums their allocatable resources. + GetAllClusterResources(ctx context.Context, clusterType ClusterType, volumes *corev1.PersistentVolumeList) (uint64, uint64, uint64, error) + // GetConsumedCPUAndMemory returns consumed CPU and Memory in given namespace. If namespace + // is empty, it tries to get them from all namespaces. + GetConsumedCPUAndMemory(ctx context.Context, namespace string) (cpuMillis uint64, memoryBytes uint64, err error) + // GetConsumedDiskBytes returns consumed bytes. The strategy differs based on k8s cluster type. + GetConsumedDiskBytes(_ context.Context, clusterType ClusterType, volumes *corev1.PersistentVolumeList) (uint64, error) + // ListSecrets returns list of secrets that match the criteria. + // This method returns a list of full objects (meta and spec). + ListSecrets(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.SecretList, error) + // GetSecret returns a secret that matches the criteria. + GetSecret(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Secret, error) // CreateSecret creates a secret. CreateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) - // SetSecret creates or updates an existing secret. - SetSecret(secret *corev1.Secret) error // UpdateSecret updates a secret. UpdateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) - // DeleteSecret deletes a secret. - DeleteSecret(ctx context.Context, namespace, name string) error + // DeleteSecret deletes a secret that matches the criteria. + DeleteSecret(ctx context.Context, obj *corev1.Secret) error + // GetService returns service that matches the criteria. + GetService(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Service, error) + // ListPersistentVolumes returns list of persistent volumes that match the criteria. + // This method returns a list of full objects (meta and spec). + ListPersistentVolumes(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.PersistentVolumeList, error) + // ListStorageClasses returns list of storage classes that match the criteria. + // This method returns a list of full objects (meta and spec). + ListStorageClasses(ctx context.Context, opts ...ctrlclient.ListOption) (*storagev1.StorageClassList, error) + // GetSubscription returns OLM subscription that matches the criteria. + GetSubscription(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.Subscription, error) + // ListSubscriptions lists OLM subscriptions that match the criteria. + // This method returns a list of full objects (meta and spec). + ListSubscriptions(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.SubscriptionList, error) + // DeleteSubscription deletes OLM subscription that matches the criteria. + DeleteSubscription(ctx context.Context, obj *olmv1alpha1.Subscription) error + // ListPods returns list of pods that match the criteria. + // This method returns a list of full objects (meta and spec). + ListPods(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.PodList, error) } diff --git a/pkg/kubernetes/mock_kubernetes_connector.go b/pkg/kubernetes/mock_kubernetes_connector.go deleted file mode 100644 index 1cd9b9635..000000000 --- a/pkg/kubernetes/mock_kubernetes_connector.go +++ /dev/null @@ -1,1645 +0,0 @@ -// Code generated by mockery v2.53.3. DO NOT EDIT. - -package kubernetes - -import ( - context "context" - - go_version "github.com/hashicorp/go-version" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - mock "github.com/stretchr/testify/mock" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - version "k8s.io/apimachinery/pkg/version" - rest "k8s.io/client-go/rest" - - v1alpha1 "github.com/percona/everest-operator/api/v1alpha1" - accounts "github.com/percona/everest/pkg/accounts" - common "github.com/percona/everest/pkg/common" - client "github.com/percona/everest/pkg/kubernetes/client" -) - -// MockKubernetesConnector is an autogenerated mock type for the KubernetesConnector type -type MockKubernetesConnector struct { - mock.Mock -} - -// Accounts provides a mock function with no fields -func (_m *MockKubernetesConnector) Accounts() accounts.Interface { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Accounts") - } - - var r0 accounts.Interface - if rf, ok := ret.Get(0).(func() accounts.Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(accounts.Interface) - } - } - - return r0 -} - -// ApplyManifestFile provides a mock function with given fields: files, namespace -func (_m *MockKubernetesConnector) ApplyManifestFile(files []byte, namespace string) error { - ret := _m.Called(files, namespace) - - if len(ret) == 0 { - panic("no return value specified for ApplyManifestFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte, string) error); ok { - r0 = rf(files, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ApproveInstallPlan provides a mock function with given fields: ctx, namespace, installPlanName -func (_m *MockKubernetesConnector) ApproveInstallPlan(ctx context.Context, namespace string, installPlanName string) (bool, error) { - ret := _m.Called(ctx, namespace, installPlanName) - - if len(ret) == 0 { - panic("no return value specified for ApproveInstallPlan") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { - return rf(ctx, namespace, installPlanName) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { - r0 = rf(ctx, namespace, installPlanName) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, installPlanName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ClusterName provides a mock function with no fields -func (_m *MockKubernetesConnector) ClusterName() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ClusterName") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// Config provides a mock function with no fields -func (_m *MockKubernetesConnector) Config() *rest.Config { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Config") - } - - var r0 *rest.Config - if rf, ok := ret.Get(0).(func() *rest.Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*rest.Config) - } - } - - return r0 -} - -// CreateBackupStorage provides a mock function with given fields: ctx, storage -func (_m *MockKubernetesConnector) CreateBackupStorage(ctx context.Context, storage *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for CreateBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.BackupStorage) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateMonitoringConfig provides a mock function with given fields: ctx, storage -func (_m *MockKubernetesConnector) CreateMonitoringConfig(ctx context.Context, storage *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for CreateMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.MonitoringConfig) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// CreateNamespace provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) CreateNamespace(ctx context.Context, namespace *v1.Namespace) error { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for CreateNamespace") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace) error); ok { - r0 = rf(ctx, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CreateRSAKeyPair provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) CreateRSAKeyPair(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for CreateRSAKeyPair") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CreateSecret provides a mock function with given fields: ctx, secret -func (_m *MockKubernetesConnector) CreateSecret(ctx context.Context, secret *v1.Secret) (*v1.Secret, error) { - ret := _m.Called(ctx, secret) - - if len(ret) == 0 { - panic("no return value specified for CreateSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) (*v1.Secret, error)); ok { - return rf(ctx, secret) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) *v1.Secret); ok { - r0 = rf(ctx, secret) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Secret) error); ok { - r1 = rf(ctx, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DeleteBackupStorage provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) DeleteBackupStorage(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteBackupStorage") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteBackupStorages provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) DeleteBackupStorages(ctx context.Context, namespace string) error { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteBackupStorages") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCRD provides a mock function with given fields: ctx, name -func (_m *MockKubernetesConnector) DeleteCRD(ctx context.Context, name string) error { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteCRD") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCatalogSource provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) DeleteCatalogSource(ctx context.Context, name string, namespace string) error { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteCatalogSource") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, name, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteClusterServiceVersion provides a mock function with given fields: ctx, key -func (_m *MockKubernetesConnector) DeleteClusterServiceVersion(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DeleteClusterServiceVersion") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteDeployment provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) DeleteDeployment(ctx context.Context, name string, namespace string) error { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteDeployment") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, name, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteManifestFile provides a mock function with given fields: fileBytes, namespace -func (_m *MockKubernetesConnector) DeleteManifestFile(fileBytes []byte, namespace string) error { - ret := _m.Called(fileBytes, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteManifestFile") - } - - var r0 error - if rf, ok := ret.Get(0).(func([]byte, string) error); ok { - r0 = rf(fileBytes, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteMonitoringConfig provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) DeleteMonitoringConfig(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteMonitoringConfig") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteMonitoringConfigs provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) DeleteMonitoringConfigs(ctx context.Context, namespace string) error { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for DeleteMonitoringConfigs") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteNamespace provides a mock function with given fields: ctx, name -func (_m *MockKubernetesConnector) DeleteNamespace(ctx context.Context, name string) error { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteNamespace") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSecret provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) DeleteSecret(ctx context.Context, namespace string, name string) error { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for DeleteSecret") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteSubscription provides a mock function with given fields: ctx, key -func (_m *MockKubernetesConnector) DeleteSubscription(ctx context.Context, key types.NamespacedName) error { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for DeleteSubscription") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) error); ok { - r0 = rf(ctx, key) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetBackupStorage provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) GetBackupStorage(ctx context.Context, namespace string, name string) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetCatalogSource provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) GetCatalogSource(ctx context.Context, name string, namespace string) (*operatorsv1alpha1.CatalogSource, error) { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for GetCatalogSource") - } - - var r0 *operatorsv1alpha1.CatalogSource - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*operatorsv1alpha1.CatalogSource, error)); ok { - return rf(ctx, name, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *operatorsv1alpha1.CatalogSource); ok { - r0 = rf(ctx, name, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.CatalogSource) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, name, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetClusterServiceVersion provides a mock function with given fields: ctx, key -func (_m *MockKubernetesConnector) GetClusterServiceVersion(ctx context.Context, key types.NamespacedName) (*operatorsv1alpha1.ClusterServiceVersion, error) { - ret := _m.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for GetClusterServiceVersion") - } - - var r0 *operatorsv1alpha1.ClusterServiceVersion - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) (*operatorsv1alpha1.ClusterServiceVersion, error)); ok { - return rf(ctx, key) - } - if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName) *operatorsv1alpha1.ClusterServiceVersion); ok { - r0 = rf(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.ClusterServiceVersion) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, types.NamespacedName) error); ok { - r1 = rf(ctx, key) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetClusterType provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) GetClusterType(ctx context.Context) (ClusterType, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetClusterType") - } - - var r0 ClusterType - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (ClusterType, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) ClusterType); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(ClusterType) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetConfigMap provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) GetConfigMap(ctx context.Context, namespace string, name string) (*v1.ConfigMap, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetConfigMap") - } - - var r0 *v1.ConfigMap - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.ConfigMap, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.ConfigMap); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.ConfigMap) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDBNamespaces provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) GetDBNamespaces(ctx context.Context) ([]string, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetDBNamespaces") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) []string); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDatabaseEngine provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) GetDatabaseEngine(ctx context.Context, namespace string, name string) (*v1alpha1.DatabaseEngine, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseEngine") - } - - var r0 *v1alpha1.DatabaseEngine - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.DatabaseEngine, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.DatabaseEngine); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngine) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetDeployment provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) GetDeployment(ctx context.Context, name string, namespace string) (*appsv1.Deployment, error) { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for GetDeployment") - } - - var r0 *appsv1.Deployment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*appsv1.Deployment, error)); ok { - return rf(ctx, name, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *appsv1.Deployment); ok { - r0 = rf(ctx, name, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.Deployment) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, name, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetEverestID provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) GetEverestID(ctx context.Context) (string, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetEverestID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetEverestSettings provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) GetEverestSettings(ctx context.Context) (common.EverestSettings, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetEverestSettings") - } - - var r0 common.EverestSettings - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (common.EverestSettings, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) common.EverestSettings); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(common.EverestSettings) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetMonitoringConfig provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) GetMonitoringConfig(ctx context.Context, namespace string, name string) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetMonitoringConfigsBySecretName provides a mock function with given fields: ctx, namespace, secretName -func (_m *MockKubernetesConnector) GetMonitoringConfigsBySecretName(ctx context.Context, namespace string, secretName string) ([]*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, namespace, secretName) - - if len(ret) == 0 { - panic("no return value specified for GetMonitoringConfigsBySecretName") - } - - var r0 []*v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, namespace, secretName) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) []*v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, namespace, secretName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, secretName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNamespace provides a mock function with given fields: ctx, name -func (_m *MockKubernetesConnector) GetNamespace(ctx context.Context, name string) (*v1.Namespace, error) { - ret := _m.Called(ctx, name) - - if len(ret) == 0 { - panic("no return value specified for GetNamespace") - } - - var r0 *v1.Namespace - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.Namespace, error)); ok { - return rf(ctx, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1.Namespace); ok { - r0 = rf(ctx, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Namespace) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSecret provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) GetSecret(ctx context.Context, namespace string, name string) (*v1.Secret, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for GetSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.Secret, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.Secret); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetServerVersion provides a mock function with no fields -func (_m *MockKubernetesConnector) GetServerVersion() (*version.Info, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetServerVersion") - } - - var r0 *version.Info - var r1 error - if rf, ok := ret.Get(0).(func() (*version.Info, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *version.Info); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*version.Info) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSubscription provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) GetSubscription(ctx context.Context, name string, namespace string) (*operatorsv1alpha1.Subscription, error) { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for GetSubscription") - } - - var r0 *operatorsv1alpha1.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*operatorsv1alpha1.Subscription, error)); ok { - return rf(ctx, name, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *operatorsv1alpha1.Subscription); ok { - r0 = rf(ctx, name, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, name, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IsBackupStorageUsed provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) IsBackupStorageUsed(ctx context.Context, namespace string, name string) (bool, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for IsBackupStorageUsed") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// IsMonitoringConfigUsed provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) IsMonitoringConfigUsed(ctx context.Context, namespace string, name string) (bool, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for IsMonitoringConfigUsed") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { - r0 = rf(ctx, namespace, name) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Kubeconfig provides a mock function with no fields -func (_m *MockKubernetesConnector) Kubeconfig() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Kubeconfig") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// ListBackupStorages provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListBackupStorages(ctx context.Context, namespace string) (*v1alpha1.BackupStorageList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListBackupStorages") - } - - var r0 *v1alpha1.BackupStorageList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.BackupStorageList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.BackupStorageList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorageList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListCRDs provides a mock function with given fields: ctx -func (_m *MockKubernetesConnector) ListCRDs(ctx context.Context) (*apiextensionsv1.CustomResourceDefinitionList, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ListCRDs") - } - - var r0 *apiextensionsv1.CustomResourceDefinitionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*apiextensionsv1.CustomResourceDefinitionList, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *apiextensionsv1.CustomResourceDefinitionList); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*apiextensionsv1.CustomResourceDefinitionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListClusterServiceVersion provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListClusterServiceVersion(ctx context.Context, namespace string) (*operatorsv1alpha1.ClusterServiceVersionList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListClusterServiceVersion") - } - - var r0 *operatorsv1alpha1.ClusterServiceVersionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*operatorsv1alpha1.ClusterServiceVersionList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *operatorsv1alpha1.ClusterServiceVersionList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.ClusterServiceVersionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDatabaseEngines provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListDatabaseEngines(ctx context.Context, namespace string) (*v1alpha1.DatabaseEngineList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListDatabaseEngines") - } - - var r0 *v1alpha1.DatabaseEngineList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.DatabaseEngineList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.DatabaseEngineList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngineList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListDeployments provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListDeployments(ctx context.Context, namespace string) (*appsv1.DeploymentList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListDeployments") - } - - var r0 *appsv1.DeploymentList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*appsv1.DeploymentList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *appsv1.DeploymentList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.DeploymentList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListMonitoringConfigs provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListMonitoringConfigs(ctx context.Context, namespace string) (*v1alpha1.MonitoringConfigList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListMonitoringConfigs") - } - - var r0 *v1alpha1.MonitoringConfigList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.MonitoringConfigList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.MonitoringConfigList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfigList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListNamespaces provides a mock function with given fields: ctx, opts -func (_m *MockKubernetesConnector) ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*v1.NamespaceList, error) { - ret := _m.Called(ctx, opts) - - if len(ret) == 0 { - panic("no return value specified for ListNamespaces") - } - - var r0 *v1.NamespaceList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.NamespaceList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.NamespaceList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.NamespaceList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListSecrets provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListSecrets(ctx context.Context, namespace string) (*v1.SecretList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListSecrets") - } - - var r0 *v1.SecretList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.SecretList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *v1.SecretList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.SecretList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListSubscriptions provides a mock function with given fields: ctx, namespace -func (_m *MockKubernetesConnector) ListSubscriptions(ctx context.Context, namespace string) (*operatorsv1alpha1.SubscriptionList, error) { - ret := _m.Called(ctx, namespace) - - if len(ret) == 0 { - panic("no return value specified for ListSubscriptions") - } - - var r0 *operatorsv1alpha1.SubscriptionList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*operatorsv1alpha1.SubscriptionList, error)); ok { - return rf(ctx, namespace) - } - if rf, ok := ret.Get(0).(func(context.Context, string) *operatorsv1alpha1.SubscriptionList); ok { - r0 = rf(ctx, namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*operatorsv1alpha1.SubscriptionList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, namespace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Namespace provides a mock function with no fields -func (_m *MockKubernetesConnector) Namespace() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Namespace") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// OperatorInstalledVersion provides a mock function with given fields: ctx, namespace, name -func (_m *MockKubernetesConnector) OperatorInstalledVersion(ctx context.Context, namespace string, name string) (*go_version.Version, error) { - ret := _m.Called(ctx, namespace, name) - - if len(ret) == 0 { - panic("no return value specified for OperatorInstalledVersion") - } - - var r0 *go_version.Version - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) (*go_version.Version, error)); ok { - return rf(ctx, namespace, name) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string) *go_version.Version); ok { - r0 = rf(ctx, namespace, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*go_version.Version) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, namespace, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RestartDeployment provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) RestartDeployment(ctx context.Context, name string, namespace string) error { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for RestartDeployment") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, name, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SetDatabaseEngineLock provides a mock function with given fields: ctx, namespace, name, locked -func (_m *MockKubernetesConnector) SetDatabaseEngineLock(ctx context.Context, namespace string, name string, locked bool) error { - ret := _m.Called(ctx, namespace, name, locked) - - if len(ret) == 0 { - panic("no return value specified for SetDatabaseEngineLock") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) error); ok { - r0 = rf(ctx, namespace, name, locked) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// SetSecret provides a mock function with given fields: secret -func (_m *MockKubernetesConnector) SetSecret(secret *v1.Secret) error { - ret := _m.Called(secret) - - if len(ret) == 0 { - panic("no return value specified for SetSecret") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*v1.Secret) error); ok { - r0 = rf(secret) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateBackupStorage provides a mock function with given fields: ctx, storage -func (_m *MockKubernetesConnector) UpdateBackupStorage(ctx context.Context, storage *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for UpdateBackupStorage") - } - - var r0 *v1alpha1.BackupStorage - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) (*v1alpha1.BackupStorage, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.BackupStorage) *v1alpha1.BackupStorage); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.BackupStorage) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.BackupStorage) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDatabaseEngine provides a mock function with given fields: ctx, engine -func (_m *MockKubernetesConnector) UpdateDatabaseEngine(ctx context.Context, engine *v1alpha1.DatabaseEngine) (*v1alpha1.DatabaseEngine, error) { - ret := _m.Called(ctx, engine) - - if len(ret) == 0 { - panic("no return value specified for UpdateDatabaseEngine") - } - - var r0 *v1alpha1.DatabaseEngine - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.DatabaseEngine) (*v1alpha1.DatabaseEngine, error)); ok { - return rf(ctx, engine) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.DatabaseEngine) *v1alpha1.DatabaseEngine); ok { - r0 = rf(ctx, engine) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.DatabaseEngine) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.DatabaseEngine) error); ok { - r1 = rf(ctx, engine) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateDeployment provides a mock function with given fields: ctx, deployment -func (_m *MockKubernetesConnector) UpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) (*appsv1.Deployment, error) { - ret := _m.Called(ctx, deployment) - - if len(ret) == 0 { - panic("no return value specified for UpdateDeployment") - } - - var r0 *appsv1.Deployment - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *appsv1.Deployment) (*appsv1.Deployment, error)); ok { - return rf(ctx, deployment) - } - if rf, ok := ret.Get(0).(func(context.Context, *appsv1.Deployment) *appsv1.Deployment); ok { - r0 = rf(ctx, deployment) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*appsv1.Deployment) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *appsv1.Deployment) error); ok { - r1 = rf(ctx, deployment) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateEverestSettings provides a mock function with given fields: ctx, settings -func (_m *MockKubernetesConnector) UpdateEverestSettings(ctx context.Context, settings common.EverestSettings) error { - ret := _m.Called(ctx, settings) - - if len(ret) == 0 { - panic("no return value specified for UpdateEverestSettings") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, common.EverestSettings) error); ok { - r0 = rf(ctx, settings) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UpdateMonitoringConfig provides a mock function with given fields: ctx, storage -func (_m *MockKubernetesConnector) UpdateMonitoringConfig(ctx context.Context, storage *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error) { - ret := _m.Called(ctx, storage) - - if len(ret) == 0 { - panic("no return value specified for UpdateMonitoringConfig") - } - - var r0 *v1alpha1.MonitoringConfig - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) (*v1alpha1.MonitoringConfig, error)); ok { - return rf(ctx, storage) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.MonitoringConfig) *v1alpha1.MonitoringConfig); ok { - r0 = rf(ctx, storage) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1alpha1.MonitoringConfig) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.MonitoringConfig) error); ok { - r1 = rf(ctx, storage) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateNamespace provides a mock function with given fields: ctx, namespace, opts -func (_m *MockKubernetesConnector) UpdateNamespace(ctx context.Context, namespace *v1.Namespace, opts metav1.UpdateOptions) (*v1.Namespace, error) { - ret := _m.Called(ctx, namespace, opts) - - if len(ret) == 0 { - panic("no return value specified for UpdateNamespace") - } - - var r0 *v1.Namespace - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) (*v1.Namespace, error)); ok { - return rf(ctx, namespace, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) *v1.Namespace); ok { - r0 = rf(ctx, namespace, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Namespace) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Namespace, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, namespace, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateSecret provides a mock function with given fields: ctx, secret -func (_m *MockKubernetesConnector) UpdateSecret(ctx context.Context, secret *v1.Secret) (*v1.Secret, error) { - ret := _m.Called(ctx, secret) - - if len(ret) == 0 { - panic("no return value specified for UpdateSecret") - } - - var r0 *v1.Secret - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) (*v1.Secret, error)); ok { - return rf(ctx, secret) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Secret) *v1.Secret); ok { - r0 = rf(ctx, secret) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Secret) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Secret) error); ok { - r1 = rf(ctx, secret) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// WaitForRollout provides a mock function with given fields: ctx, name, namespace -func (_m *MockKubernetesConnector) WaitForRollout(ctx context.Context, name string, namespace string) error { - ret := _m.Called(ctx, name, namespace) - - if len(ret) == 0 { - panic("no return value specified for WaitForRollout") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, name, namespace) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// WithClient provides a mock function with given fields: c -func (_m *MockKubernetesConnector) WithClient(c client.KubeClientConnector) *Kubernetes { - ret := _m.Called(c) - - if len(ret) == 0 { - panic("no return value specified for WithClient") - } - - var r0 *Kubernetes - if rf, ok := ret.Get(0).(func(client.KubeClientConnector) *Kubernetes); ok { - r0 = rf(c) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*Kubernetes) - } - } - - return r0 -} - -// NewMockKubernetesConnector creates a new instance of MockKubernetesConnector. 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 NewMockKubernetesConnector(t interface { - mock.TestingT - Cleanup(func()) -}, -) *MockKubernetesConnector { - mock := &MockKubernetesConnector{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/kubernetes/monitoring_config.go b/pkg/kubernetes/monitoring_config.go index d7671a138..910ba0a46 100644 --- a/pkg/kubernetes/monitoring_config.go +++ b/pkg/kubernetes/monitoring_config.go @@ -30,94 +30,118 @@ const ( monitoringConfigNameLabel = "monitoringConfigName" ) -// ListMonitoringConfigs returns list of managed monitoring configs. -func (k *Kubernetes) ListMonitoringConfigs(ctx context.Context, namespace string) (*everestv1alpha1.MonitoringConfigList, error) { - return k.client.ListMonitoringConfigs(ctx, namespace) +// ListMonitoringConfigs returns list of managed monitoring configs that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListMonitoringConfigs(ctx context.Context, opts ...ctrlclient.ListOption) (*everestv1alpha1.MonitoringConfigList, error) { + result := &everestv1alpha1.MonitoringConfigList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// listMonitoringConfigsMeta returns list of managed monitoring configs that match the criteria. +// This method returns a list of simplified objects (meta only). +func (k *Kubernetes) listMonitoringConfigsMeta(ctx context.Context, opts ...ctrlclient.ListOption) (*metav1.PartialObjectMetadataList, error) { + result := &metav1.PartialObjectMetadataList{} + result.SetGroupVersionKind(everestv1alpha1.GroupVersion.WithKind("MonitoringConfigList")) + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetMonitoringConfig returns monitoring configs by provided name. -func (k *Kubernetes) GetMonitoringConfig(ctx context.Context, namespace, name string) (*everestv1alpha1.MonitoringConfig, error) { - return k.client.GetMonitoringConfig(ctx, namespace, name) +// GetMonitoringConfig returns monitoring configs that matches the criteria. +func (k *Kubernetes) GetMonitoringConfig(ctx context.Context, key ctrlclient.ObjectKey) (*everestv1alpha1.MonitoringConfig, error) { + result := &everestv1alpha1.MonitoringConfig{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } -// CreateMonitoringConfig returns monitoring configs by provided name. -func (k *Kubernetes) CreateMonitoringConfig(ctx context.Context, storage *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { - return k.client.CreateMonitoringConfig(ctx, storage) +// CreateMonitoringConfig creates monitoring config. +func (k *Kubernetes) CreateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { + if err := k.k8sClient.Create(ctx, config); err != nil { + return nil, err + } + return config, nil } -// UpdateMonitoringConfig returns monitoring configs by provided name. -func (k *Kubernetes) UpdateMonitoringConfig(ctx context.Context, storage *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { - return k.client.UpdateMonitoringConfig(ctx, storage) +// UpdateMonitoringConfig updates monitoring config. +func (k *Kubernetes) UpdateMonitoringConfig(ctx context.Context, config *everestv1alpha1.MonitoringConfig) (*everestv1alpha1.MonitoringConfig, error) { + if err := k.k8sClient.Update(ctx, config); err != nil { + return nil, err + } + return config, nil } -// DeleteMonitoringConfig returns monitoring configs by provided name. -func (k *Kubernetes) DeleteMonitoringConfig(ctx context.Context, namespace, name string) error { - return k.client.DeleteMonitoringConfig(ctx, namespace, name) +// DeleteMonitoringConfig deletes monitoring config that matches the criteria. +func (k *Kubernetes) DeleteMonitoringConfig(ctx context.Context, obj *everestv1alpha1.MonitoringConfig) error { + return k.k8sClient.Delete(ctx, obj) } -// DeleteMonitoringConfigs deletes all monitoring configs in provided namespace. +// DeleteMonitoringConfigs deletes monitoring configs that matches the criteria. // This function will wait until all configs are deleted. -func (k *Kubernetes) DeleteMonitoringConfigs(ctx context.Context, namespace string) error { +func (k *Kubernetes) DeleteMonitoringConfigs(ctx context.Context, opts ...ctrlclient.ListOption) error { + // No need to fetch full objects, we only need the fact there are objects that match the criteria(opts). + delList, err := k.listMonitoringConfigsMeta(ctx, opts...) + if err != nil { + k.l.Errorf("Could not list monitoring configs: %s", err) + return err + } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return nil + } + + // need to convert ListOptions to DeleteAllOfOptions + delOpts := &ctrlclient.DeleteAllOfOptions{} + for _, opt := range opts { + opt.ApplyToList(&delOpts.ListOptions) + } + + k.l.Debugf("Setting monitoring configs removal timeout to %s", pollTimeout) return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { - list, err := k.ListMonitoringConfigs(ctx, namespace) - if err != nil { - return false, err - } - if len(list.Items) == 0 { - return true, nil - } - for _, storage := range list.Items { - if err := k.DeleteMonitoringConfig(ctx, storage.GetNamespace(), storage.GetName()); ctrlclient.IgnoreNotFound(err) != nil { + // Skip fetching the list of objects to delete again, we already have it (see code above). + if delList == nil { + var err error + if delList, err = k.listMonitoringConfigsMeta(ctx, opts...); err != nil { + k.l.Errorf("Could not list monitoring configs in polling: %s", err) return false, err } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return true, nil + } + } + + // Reset the list to nil to fetch it again on the next iteration. + delList = nil + + if err := k.k8sClient.DeleteAllOf(ctx, &everestv1alpha1.MonitoringConfig{}, delOpts); err != nil { + return false, err } return false, nil }) } -// IsMonitoringConfigUsed checks if a monitoring config is used by any database cluster in the provided namespace. -func (k *Kubernetes) IsMonitoringConfigUsed(ctx context.Context, namespace, name string) (bool, error) { - _, err := k.client.GetMonitoringConfig(ctx, namespace, name) +// IsMonitoringConfigUsed checks if a monitoring config that matches the criteria is used by any database cluster. +func (k *Kubernetes) IsMonitoringConfigUsed(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) { + _, err := k.GetMonitoringConfig(ctx, key) if err != nil { return false, err } - options := metav1.ListOptions{ - LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{ - MatchLabels: map[string]string{ - monitoringConfigNameLabel: name, - }, - }), - } - - list, err := k.client.ListDatabaseClusters(ctx, namespace, options) + list, err := k.listDatabaseClustersMeta(ctx, ctrlclient.InNamespace(key.Namespace), ctrlclient.MatchingLabels{monitoringConfigNameLabel: key.Name}) if err != nil { return false, err } - if len(list.Items) > 0 { + if list != nil && len(list.Items) > 0 { return true, nil } return false, nil } - -// GetMonitoringConfigsBySecretName returns a list of monitoring configs which use -// the provided secret name. -func (k *Kubernetes) GetMonitoringConfigsBySecretName( - ctx context.Context, namespace, secretName string, -) ([]*everestv1alpha1.MonitoringConfig, error) { - mcs, err := k.client.ListMonitoringConfigs(ctx, namespace) - if err != nil { - return nil, err - } - - res := make([]*everestv1alpha1.MonitoringConfig, 0, 1) - for _, mc := range mcs.Items { - if mc.Spec.CredentialsSecretName == secretName { - //nolint:exportloopref - res = append(res, &mc) - } - } - - return res, nil -} diff --git a/pkg/kubernetes/namespace.go b/pkg/kubernetes/namespace.go index 7fcbb160e..e917269cb 100644 --- a/pkg/kubernetes/namespace.go +++ b/pkg/kubernetes/namespace.go @@ -1,36 +1,83 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes import ( "context" + "errors" + "slices" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/percona/everest/pkg/common" ) // CreateNamespace creates the given namespace. -func (k *Kubernetes) CreateNamespace(ctx context.Context, namespace *corev1.Namespace) error { - if _, err := k.client.CreateNamespace(ctx, namespace); err != nil { - return err +func (k *Kubernetes) CreateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) { + if err := k.k8sClient.Create(ctx, namespace); err != nil { + return nil, err } - return nil + return namespace, nil } -// GetNamespace returns a namespace. -func (k *Kubernetes) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { - return k.client.GetNamespace(ctx, name) +// GetNamespace returns a namespace that matches the criteria. +func (k *Kubernetes) GetNamespace(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Namespace, error) { + result := &corev1.Namespace{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } -// DeleteNamespace deletes a namespace. -func (k *Kubernetes) DeleteNamespace(ctx context.Context, name string) error { - return k.client.DeleteNamespace(ctx, name) +// GetDBNamespaces returns a list of DB namespaces that managed by the Everest and match the criteria. +func (k *Kubernetes) GetDBNamespaces(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.NamespaceList, error) { + opts = append(opts, ctrlclient.MatchingLabels{common.KubernetesManagedByLabel: common.Everest}) + result, err := k.ListNamespaces(ctx, opts...) + if err != nil { + return nil, errors.Join(err, errors.New("failed to get managed namespaces")) + } + + internalNs := []string{common.SystemNamespace, common.MonitoringNamespace} + // filter out Everest system and monitoring namespaces. + slices.DeleteFunc(result.Items, func(ns corev1.Namespace) bool { + return slices.Contains(internalNs, ns.Name) + }) + return result, nil } -// ListNamespaces lists all namespaces. -func (k *Kubernetes) ListNamespaces(ctx context.Context, opts metav1.ListOptions) (*corev1.NamespaceList, error) { - return k.client.ListNamespaces(ctx, opts) +// DeleteNamespace deletes a namespace that matches the criteria. +func (k *Kubernetes) DeleteNamespace(ctx context.Context, obj *corev1.Namespace) error { + return k.k8sClient.Delete(ctx, obj) +} + +// ListNamespaces lists all namespaces that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListNamespaces(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.NamespaceList, error) { + result := &corev1.NamespaceList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } // UpdateNamespace updates the given namespace. -func (k *Kubernetes) UpdateNamespace(ctx context.Context, namespace *corev1.Namespace, opts metav1.UpdateOptions) (*corev1.Namespace, error) { - return k.client.UpdateNamespace(ctx, namespace, opts) +func (k *Kubernetes) UpdateNamespace(ctx context.Context, namespace *corev1.Namespace) (*corev1.Namespace, error) { + if err := k.k8sClient.Update(ctx, namespace); err != nil { + return nil, err + } + return namespace, nil } diff --git a/pkg/kubernetes/node.go b/pkg/kubernetes/node.go index 1492fd7e8..709d1783f 100644 --- a/pkg/kubernetes/node.go +++ b/pkg/kubernetes/node.go @@ -2,40 +2,44 @@ package kubernetes import ( "context" - "errors" + "slices" corev1 "k8s.io/api/core/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// GetWorkerNodes returns list of cluster workers nodes. -func (k *Kubernetes) GetWorkerNodes(ctx context.Context) ([]corev1.Node, error) { - nodes, err := k.client.GetNodes(ctx) - if err != nil { - return nil, errors.Join(err, errors.New("could not get nodes of Kubernetes cluster")) +// ListWorkerNodes returns list of cluster workers nodes. +// This method returns a list of full objects (meta and spec). +// It filters out nodes with the following taints: +// - node.cloudprovider.kubernetes.io/uninitialized=NoSchedule +// - node.kubernetes.io/unschedulable=NoSchedule +// - node-role.kubernetes.io/master=NoSchedule +func (k *Kubernetes) ListWorkerNodes(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.NodeList, error) { + result := &corev1.NodeList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err } + forbidenTaints := map[string]corev1.TaintEffect{ "node.cloudprovider.kubernetes.io/uninitialized": corev1.TaintEffectNoSchedule, "node.kubernetes.io/unschedulable": corev1.TaintEffectNoSchedule, "node-role.kubernetes.io/master": corev1.TaintEffectNoSchedule, } - workers := make([]corev1.Node, 0, len(nodes.Items)) - for _, node := range nodes.Items { - if len(node.Spec.Taints) == 0 { - workers = append(workers, node) - continue - } + slices.DeleteFunc(result.Items, func(node corev1.Node) bool { for _, taint := range node.Spec.Taints { - effect, keyFound := forbidenTaints[taint.Key] - if !keyFound || effect != taint.Effect { - workers = append(workers, node) + effect, ok := forbidenTaints[taint.Key] + if ok && effect == taint.Effect { + return true } } - } - return workers, nil + return false + }) + + return result, nil } // IsNodeInCondition returns true if node's condition given as an argument has -// status "True". Otherwise it returns false. +// status "True". Otherwise, it returns false. func IsNodeInCondition(node corev1.Node, conditionType corev1.NodeConditionType) bool { for _, condition := range node.Status.Conditions { if condition.Status == corev1.ConditionTrue && condition.Type == conditionType { diff --git a/pkg/kubernetes/object.go b/pkg/kubernetes/object.go new file mode 100644 index 000000000..5cacba154 --- /dev/null +++ b/pkg/kubernetes/object.go @@ -0,0 +1,236 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "bytes" + "context" + "slices" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + yamlSerializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + "k8s.io/apimachinery/pkg/types" + yamlutil "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/cli-runtime/pkg/resource" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + defaultAPIURIPath = "/api" + defaultAPIsURIPath = "/apis" + objectsBufferSize = 100 +) + +// ApplyManifestFile accepts manifest file contents, parses into []runtime.Object +// and applies them against the cluster. +func (k *Kubernetes) ApplyManifestFile(ctx context.Context, fileBytes []byte, namespace string, ignoreObjects ...ctrlclient.Object) error { + objs, err := k.getObjects(fileBytes) + if err != nil { + return err + } + for i := range objs { + o := objs[i] + + // Check if this object should be ignored? + if slices.ContainsFunc(ignoreObjects, func(ign ctrlclient.Object) bool { + return o.GetKind() == ign.GetObjectKind().GroupVersionKind().Kind && + o.GetName() == ign.GetName() && + ign.GetNamespace() == namespace + }) { + continue + } + + if err := k.applyTemplateCustomization(ctx, o, namespace); err != nil { + return err + } + err := k.ApplyObject(o) + if err != nil { + return err + } + } + return nil +} + +func (k *Kubernetes) getObjects(f []byte) ([]*unstructured.Unstructured, error) { + var objs []*unstructured.Unstructured + decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(f), objectsBufferSize) + var err error + for { + var rawObj runtime.RawExtension + if err = decoder.Decode(&rawObj); err != nil { + break + } + + obj, _, err := yamlSerializer.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil) + if err != nil { + return nil, err + } + + unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + if err != nil { + return nil, err + } + + objs = append(objs, &unstructured.Unstructured{Object: unstructuredMap}) + } + + return objs, nil +} + +// ApplyObject applies object. +func (k *Kubernetes) ApplyObject(obj runtime.Object) error { + // Instantiate a new restmapper so we discover any new resources before applying object. + groupResources, err := restmapper.GetAPIGroupResources(k.getDiscoveryClient()) + if err != nil { + return err + } + mapper := restmapper.NewDiscoveryRESTMapper(groupResources) + + gvk := obj.GetObjectKind().GroupVersionKind() + gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind} + mapping, err := mapper.RESTMapping(gk, gvk.Version) + if err != nil { + return err + } + namespace, name, err := k.retrieveMetaFromObject(obj) + if err != nil { + return err + } + cli, err := k.resourceClient(mapping.GroupVersionKind.GroupVersion()) + if err != nil { + return err + } + helper := resource.NewHelper(cli, mapping) + return k.applyObject(helper, namespace, name, obj) +} + +func (k *Kubernetes) applyObject(helper *resource.Helper, namespace, name string, obj runtime.Object) error { + if _, err := helper.Get(namespace, name); err != nil { + _, err = helper.Create(namespace, false, obj) + if err != nil { + return err + } + } else { + _, err = helper.Replace(namespace, name, true, obj) + if err != nil { + return err + } + } + return nil +} + +func (k *Kubernetes) applyTemplateCustomization(ctx context.Context, u *unstructured.Unstructured, namespace string) error { + if err := unstructured.SetNestedField(u.Object, namespace, "metadata", "namespace"); err != nil { + return err + } + + kind, ok, err := unstructured.NestedString(u.Object, "kind") + if err != nil { + return err + } + + if ok && kind == "ClusterRoleBinding" { + if err := k.updateClusterRoleBinding(u, namespace); err != nil { + return err + } + } + if ok && kind == "Service" { + // During installation or upgrading of the everest API Server + // CLI should keep spec.type untouched to prevent overriding of it. + if err := k.setEverestServiceType(ctx, u, namespace); err != nil { + return err + } + } + + return nil +} + +func (k *Kubernetes) updateClusterRoleBinding(u *unstructured.Unstructured, namespace string) error { + sub, ok, err := unstructured.NestedFieldNoCopy(u.Object, "subjects") + if err != nil { + return err + } + + if !ok { + return nil + } + + subjects, ok := sub.([]interface{}) + if !ok { + return nil + } + + for _, s := range subjects { + sub, ok := s.(map[string]interface{}) + if !ok { + continue + } + + if err := unstructured.SetNestedField(sub, namespace, "namespace"); err != nil { + return err + } + } + return unstructured.SetNestedSlice(u.Object, subjects, "subjects") +} + +func (k *Kubernetes) setEverestServiceType(ctx context.Context, u *unstructured.Unstructured, namespace string) error { + s, err := k.GetService(ctx, types.NamespacedName{Namespace: namespace, Name: "everest"}) + if err != nil && !apierrors.IsNotFound(err) { + return err + } + if err != nil && apierrors.IsNotFound(err) { + return nil + } + if s != nil && s.Name != "" { + if err := unstructured.SetNestedField(u.Object, string(s.Spec.Type), "spec", "type"); err != nil { + return err + } + } + return nil +} + +func (k *Kubernetes) retrieveMetaFromObject(obj runtime.Object) (string, string, error) { + name, err := meta.NewAccessor().Name(obj) + if err != nil { + return "", name, err + } + namespace, err := meta.NewAccessor().Namespace(obj) + if err != nil { + return namespace, name, err + } + if namespace == "" { + namespace = k.Namespace() + } + return namespace, name, nil +} + +func (k *Kubernetes) resourceClient(gv schema.GroupVersion) (rest.Interface, error) { + cfg := k.restConfig + cfg.ContentConfig = resource.UnstructuredPlusDefaultContentConfig() + cfg.GroupVersion = &gv + if len(gv.Group) == 0 { + cfg.APIPath = defaultAPIURIPath + } else { + cfg.APIPath = defaultAPIsURIPath + } + return rest.RESTClientFor(cfg) +} diff --git a/pkg/kubernetes/oidc.go b/pkg/kubernetes/oidc.go index fe1524c1b..fa2ec8ef3 100644 --- a/pkg/kubernetes/oidc.go +++ b/pkg/kubernetes/oidc.go @@ -4,8 +4,9 @@ import ( "context" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "github.com/percona/everest/pkg/common" ) @@ -17,8 +18,8 @@ func (k *Kubernetes) UpdateEverestSettings(ctx context.Context, settings common. return err } - _, getErr := k.client.GetConfigMap(ctx, common.SystemNamespace, common.EverestSettingsConfigMapName) - if getErr != nil && !errors.IsNotFound(getErr) { + _, getErr := k.GetConfigMap(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestSettingsConfigMapName}) + if getErr != nil && !k8serrors.IsNotFound(getErr) { return getErr } @@ -30,19 +31,19 @@ func (k *Kubernetes) UpdateEverestSettings(ctx context.Context, settings common. }, Data: configMapData, } - if errors.IsNotFound(getErr) { - _, err = k.client.CreateConfigMap(ctx, c) + if k8serrors.IsNotFound(getErr) { + _, err = k.CreateConfigMap(ctx, c) return err } - _, err = k.client.UpdateConfigMap(ctx, c) + _, err = k.UpdateConfigMap(ctx, c) return err } // GetEverestSettings returns Everest settings. func (k *Kubernetes) GetEverestSettings(ctx context.Context) (common.EverestSettings, error) { settings := common.EverestSettings{} - m, err := k.client.GetConfigMap(ctx, common.SystemNamespace, common.EverestSettingsConfigMapName) + m, err := k.GetConfigMap(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.EverestSettingsConfigMapName}) if err != nil { return settings, err } diff --git a/pkg/kubernetes/olm_catalog_source.go b/pkg/kubernetes/olm_catalog_source.go new file mode 100644 index 000000000..b1bd9ddaa --- /dev/null +++ b/pkg/kubernetes/olm_catalog_source.go @@ -0,0 +1,37 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "context" + + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// GetCatalogSource returns catalog source that matches the criteria. +func (k *Kubernetes) GetCatalogSource(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.CatalogSource, error) { + result := &olmv1alpha1.CatalogSource{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// DeleteCatalogSource deletes catalog source that matches the criteria. +func (k *Kubernetes) DeleteCatalogSource(ctx context.Context, obj *olmv1alpha1.CatalogSource) error { + return k.k8sClient.Delete(ctx, obj) +} diff --git a/pkg/kubernetes/olm_cluster_service_version.go b/pkg/kubernetes/olm_cluster_service_version.go new file mode 100644 index 000000000..f35db6c3a --- /dev/null +++ b/pkg/kubernetes/olm_cluster_service_version.go @@ -0,0 +1,107 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "context" + + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// GetClusterServiceVersion retrieves a ClusterServiceVersion that matches the criteria. +func (k *Kubernetes) GetClusterServiceVersion(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.ClusterServiceVersion, error) { + result := &olmv1alpha1.ClusterServiceVersion{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// ListClusterServiceVersion list all CSVs that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListClusterServiceVersion(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.ClusterServiceVersionList, error) { + result := &olmv1alpha1.ClusterServiceVersionList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// listClusterServiceVersionMeta list all CSVs that match the criteria. +// This method returns a list of simplified objects (meta only). +func (k *Kubernetes) listClusterServiceVersionMeta(ctx context.Context, opts ...ctrlclient.ListOption) (*metav1.PartialObjectMetadataList, error) { + result := &metav1.PartialObjectMetadataList{} + result.SetGroupVersionKind(olmv1alpha1.SchemeGroupVersion.WithKind("ClusterServiceVersionList")) + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// DeleteClusterServiceVersion deletes a ClusterServiceVersion that matches the criteria. +func (k *Kubernetes) DeleteClusterServiceVersion(ctx context.Context, obj *olmv1alpha1.ClusterServiceVersion) error { + return k.k8sClient.Delete(ctx, obj) +} + +// DeleteClusterServiceVersions deletes all ClusterServiceVersion that match the criteria. +// This function will wait until all ClusterServiceVersion are deleted. +func (k *Kubernetes) DeleteClusterServiceVersions(ctx context.Context, opts ...ctrlclient.ListOption) error { + // No need to fetch full objects, we only need the fact there are objects that match the criteria(opts). + delList, err := k.listClusterServiceVersionMeta(ctx, opts...) + if err != nil { + k.l.Errorf("Could not list cluster service versions: %s", err) + return err + } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return nil + } + + // need to convert ListOptions to DeleteAllOfOptions + delOpts := &ctrlclient.DeleteAllOfOptions{} + for _, opt := range opts { + opt.ApplyToList(&delOpts.ListOptions) + } + + k.l.Debugf("Setting cluster service versions removal timeout to %s", pollTimeout) + return wait.PollUntilContextTimeout(ctx, pollInterval, pollTimeout, true, func(ctx context.Context) (bool, error) { + // Skip fetching the list of objects to delete again, we already have it (see code above). + if delList == nil { + var err error + if delList, err = k.listClusterServiceVersionMeta(ctx, opts...); err != nil { + k.l.Errorf("Could not list cluster service versions in polling: %s", err) + return false, err + } + + if delList == nil || len(delList.Items) == 0 { + // Nothing to delete. + return true, nil + } + } + + // Reset the list to nil to fetch it again on the next iteration. + delList = nil + + if err := k.k8sClient.DeleteAllOf(ctx, &olmv1alpha1.ClusterServiceVersion{}, delOpts); err != nil { + return false, err + } + return false, nil + }) +} diff --git a/pkg/kubernetes/olm_install_plan.go b/pkg/kubernetes/olm_install_plan.go new file mode 100644 index 000000000..f9a36e753 --- /dev/null +++ b/pkg/kubernetes/olm_install_plan.go @@ -0,0 +1,53 @@ +package kubernetes + +import ( + "context" + "errors" + + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// GetInstallPlan retrieves an OLM install plan that matches the criteria. +func (k *Kubernetes) GetInstallPlan(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.InstallPlan, error) { + result := &olmv1alpha1.InstallPlan{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// UpdateInstallPlan updates OLM install plan. +func (k *Kubernetes) UpdateInstallPlan(ctx context.Context, installPlan *olmv1alpha1.InstallPlan) (*olmv1alpha1.InstallPlan, error) { + if err := k.k8sClient.Update(ctx, installPlan); err != nil { + return nil, err + } + return installPlan, nil +} + +// ApproveInstallPlan approves OLM install plan that matches the criteria. +func (k *Kubernetes) ApproveInstallPlan(ctx context.Context, key ctrlclient.ObjectKey) (bool, error) { + ip, err := k.GetInstallPlan(ctx, key) + if err != nil { + return false, err + } + + k.l.Debugf("Approving install plan='%s' in namespace='%s'", key.Name, key.Namespace) + + ip.Spec.Approved = true + _, err = k.UpdateInstallPlan(ctx, ip) + if err != nil { + var sErr *apierrors.StatusError + if ok := errors.As(err, &sErr); ok && sErr.Status().Reason == metav1.StatusReasonConflict { + // The installation plan has changed. We retry to get an updated install plan. + k.l.Debugf("Retrying install plan update due to a version conflict. Error: %s", err) + return false, nil + } + + return false, err + } + + return true, nil +} diff --git a/pkg/kubernetes/olm_subscription.go b/pkg/kubernetes/olm_subscription.go new file mode 100644 index 000000000..21d19bf30 --- /dev/null +++ b/pkg/kubernetes/olm_subscription.go @@ -0,0 +1,47 @@ +// everest +// Copyright (C) 2025 Percona LLC +// +// 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 kubernetes + +import ( + "context" + + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// GetSubscription returns OLM subscription that matches the criteria. +func (k *Kubernetes) GetSubscription(ctx context.Context, key ctrlclient.ObjectKey) (*olmv1alpha1.Subscription, error) { + result := &olmv1alpha1.Subscription{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil +} + +// ListSubscriptions lists OLM subscriptions that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListSubscriptions(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.SubscriptionList, error) { + result := &olmv1alpha1.SubscriptionList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} + +// DeleteSubscription deletes OLM subscription that matches the criteria. +func (k *Kubernetes) DeleteSubscription(ctx context.Context, obj *olmv1alpha1.Subscription) error { + return k.k8sClient.Delete(ctx, obj) +} diff --git a/pkg/kubernetes/operator.go b/pkg/kubernetes/operator.go index 559ddfbe7..45b8f1d36 100644 --- a/pkg/kubernetes/operator.go +++ b/pkg/kubernetes/operator.go @@ -21,32 +21,42 @@ import ( "fmt" goversion "github.com/hashicorp/go-version" + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) // ErrOperatorNotInstalled is returned when an operator is not installed. var ErrOperatorNotInstalled = fmt.Errorf("operatorNotInstalled") -// OperatorInstalledVersion returns the installed version of operator by name. -func (k *Kubernetes) OperatorInstalledVersion(ctx context.Context, namespace, name string) (*goversion.Version, error) { - sub, err := k.client.OLM().OperatorsV1alpha1().Subscriptions(namespace).Get(ctx, name, metav1.GetOptions{}) - if err != nil { +// GetInstalledOperatorVersion returns the version of installed operator that matches the criteria. +func (k *Kubernetes) GetInstalledOperatorVersion(ctx context.Context, key ctrlclient.ObjectKey) (*goversion.Version, error) { + sub := &olmv1alpha1.Subscription{} + if err := k.k8sClient.Get(ctx, key, sub); err != nil { if k8serrors.IsNotFound(err) { return nil, errors.Join(ErrOperatorNotInstalled, errors.New("could not retrieve subscription")) } return nil, errors.Join(err, errors.New("could not retrieve subscription")) } - csvName := sub.Status.InstalledCSV - if csvName == "" { + if sub.Status.InstalledCSV == "" { return nil, ErrOperatorNotInstalled } - csv, err := k.client.OLM().OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(ctx, csvName, metav1.GetOptions{}) + csv, err := k.GetClusterServiceVersion(ctx, types.NamespacedName{Name: sub.Status.InstalledCSV}) if err != nil { return nil, errors.Join(err, errors.New("could not retrieve cluster service version")) } return goversion.NewVersion(csv.Spec.Version.FinalizeVersion()) } + +// ListInstalledOperators returns the list of installed operators that match the criteria. +func (k *Kubernetes) ListInstalledOperators(ctx context.Context, opts ...ctrlclient.ListOption) (*olmv1alpha1.SubscriptionList, error) { + result := &olmv1alpha1.SubscriptionList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil +} diff --git a/pkg/kubernetes/pod.go b/pkg/kubernetes/pod.go index 0114f270c..4cc3b76c3 100644 --- a/pkg/kubernetes/pod.go +++ b/pkg/kubernetes/pod.go @@ -4,10 +4,15 @@ import ( "context" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// GetPods returns list of pods. -func (k *Kubernetes) GetPods(ctx context.Context, namespace string, labelSelector *metav1.LabelSelector) (*corev1.PodList, error) { - return k.client.GetPods(ctx, namespace, labelSelector) +// ListPods returns list of pods that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListPods(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.PodList, error) { + result := &corev1.PodList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } diff --git a/pkg/kubernetes/resources.go b/pkg/kubernetes/resources.go index e78c6e838..3704196cb 100644 --- a/pkg/kubernetes/resources.go +++ b/pkg/kubernetes/resources.go @@ -7,6 +7,7 @@ import ( "strings" corev1 "k8s.io/api/core/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/convertors" ) @@ -48,12 +49,12 @@ func (k *Kubernetes) GetAllClusterResources( func (k *Kubernetes) getResourcesFromNodes(ctx context.Context, clusterType ClusterType) (uint64, uint64, uint64, uint64, error) { var cpuMillis, memoryBytes, diskSizeBytes uint64 - nodes, err := k.GetWorkerNodes(ctx) - if err != nil { + nodes, err := k.ListWorkerNodes(ctx) + if err != nil || nodes == nil { return 0, 0, 0, 0, errors.Join(err, errors.New("could not get a list of nodes")) } var volumeCountEKS uint64 - for _, node := range nodes { + for _, node := range nodes.Items { cpu, memory, err := getResources(node.Status.Allocatable) if err != nil { return 0, 0, 0, 0, errors.Join(err, errors.New("could not get allocatable resources of the node")) @@ -93,7 +94,7 @@ func (k *Kubernetes) getEKSVolumeCount(node corev1.Node) (uint64, error) { return 0, nil } - // Get nodes's type. + // Get node's type. nodeType, ok := node.Labels["beta.kubernetes.io/instance-type"] if !ok { return 0, errors.New("dealing with AWS EKS cluster but the node does not have label 'beta.kubernetes.io/instance-type'") @@ -165,7 +166,7 @@ func (k *Kubernetes) GetConsumedCPUAndMemory(ctx context.Context, namespace stri cpuMillis uint64, memoryBytes uint64, err error, ) { // Get CPU and Memory Requests of Pods' containers. - pods, err := k.GetPods(ctx, namespace, nil) + pods, err := k.ListPods(ctx, ctrlclient.InNamespace(namespace)) if err != nil { return 0, 0, errors.Join(err, errors.New("failed to get consumed resources")) } diff --git a/pkg/kubernetes/role.go b/pkg/kubernetes/role.go deleted file mode 100644 index 99ad6d840..000000000 --- a/pkg/kubernetes/role.go +++ /dev/null @@ -1,105 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 kubernetes ... -package kubernetes - -import ( - rbac "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CreateRole creates a new role. -func (k *Kubernetes) CreateRole(namespace, name string, rules []rbac.PolicyRule) error { - m := &rbac.Role{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "Role", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Rules: rules, - } - - return k.client.ApplyObject(m) -} - -// CreateRoleBinding binds a role to a service account. -func (k *Kubernetes) CreateRoleBinding(namespace, name, roleName, serviceAccountName string) error { - m := &rbac.RoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "RoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - RoleRef: rbac.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "Role", - Name: roleName, - }, - Subjects: []rbac.Subject{{ - Kind: "ServiceAccount", - Name: serviceAccountName, - }}, - } - - return k.client.ApplyObject(m) -} - -// CreateClusterRole creates a new cluster role. -func (k *Kubernetes) CreateClusterRole(name string, rules []rbac.PolicyRule) error { - m := &rbac.ClusterRole{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRole", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Rules: rules, - } - - return k.client.ApplyObject(m) -} - -// CreateClusterRoleBinding binds a cluster role to a service account. -func (k *Kubernetes) CreateClusterRoleBinding(namespace, name, roleName, serviceAccountName string) error { - m := &rbac.ClusterRoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "ClusterRoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - RoleRef: rbac.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: roleName, - }, - Subjects: []rbac.Subject{{ - Kind: "ServiceAccount", - Name: serviceAccountName, - Namespace: namespace, - }}, - } - - return k.client.ApplyObject(m) -} diff --git a/pkg/kubernetes/secret.go b/pkg/kubernetes/secret.go index bba4bdd86..2af86e56f 100644 --- a/pkg/kubernetes/secret.go +++ b/pkg/kubernetes/secret.go @@ -4,34 +4,45 @@ import ( "context" corev1 "k8s.io/api/core/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// ListSecrets returns secret by name. -func (k *Kubernetes) ListSecrets(ctx context.Context, namespace string) (*corev1.SecretList, error) { - return k.client.ListSecrets(ctx, namespace) +// ListSecrets returns list of secrets that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListSecrets(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.SecretList, error) { + result := &corev1.SecretList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetSecret returns a secret by name. -func (k *Kubernetes) GetSecret(ctx context.Context, namespace, name string) (*corev1.Secret, error) { - return k.client.GetSecret(ctx, namespace, name) +// GetSecret returns a secret that matches the criteria. +func (k *Kubernetes) GetSecret(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Secret, error) { + result := &corev1.Secret{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } // CreateSecret creates a secret. func (k *Kubernetes) CreateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) { - return k.client.CreateSecret(ctx, secret) -} - -// SetSecret creates or updates an existing secret. -func (k *Kubernetes) SetSecret(secret *corev1.Secret) error { - return k.client.ApplyObject(secret) + if err := k.k8sClient.Create(ctx, secret); err != nil { + return nil, err + } + return secret, nil } // UpdateSecret updates a secret. func (k *Kubernetes) UpdateSecret(ctx context.Context, secret *corev1.Secret) (*corev1.Secret, error) { - return k.client.UpdateSecret(ctx, secret) + if err := k.k8sClient.Update(ctx, secret); err != nil { + return nil, err + } + return secret, nil } -// DeleteSecret deletes a secret. -func (k *Kubernetes) DeleteSecret(ctx context.Context, namespace, name string) error { - return k.client.DeleteSecret(ctx, namespace, name) +// DeleteSecret deletes a secret that matches the criteria. +func (k *Kubernetes) DeleteSecret(ctx context.Context, obj *corev1.Secret) error { + return k.k8sClient.Delete(ctx, obj) } diff --git a/pkg/kubernetes/monitoring.go b/pkg/kubernetes/service.go similarity index 57% rename from pkg/kubernetes/monitoring.go rename to pkg/kubernetes/service.go index aad5b709c..76f0310e8 100644 --- a/pkg/kubernetes/monitoring.go +++ b/pkg/kubernetes/service.go @@ -1,5 +1,5 @@ // everest -// Copyright (C) 2023 Percona LLC +// Copyright (C) 2025 Percona LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,13 +13,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package kubernetes ... package kubernetes -import "context" +import ( + "context" -// DeleteAllMonitoringResources deletes all resources related to monitoring from k8s cluster. -// If namespace is empty, a default namespace is used. -func (k *Kubernetes) DeleteAllMonitoringResources(ctx context.Context, namespace string) error { - return k.client.DeleteAllMonitoringResources(ctx, namespace) + corev1 "k8s.io/api/core/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// GetService returns service that matches the criteria. +func (k *Kubernetes) GetService(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.Service, error) { + result := &corev1.Service{} + if err := k.k8sClient.Get(ctx, key, result); err != nil { + return nil, err + } + return result, nil } diff --git a/pkg/kubernetes/service_account.go b/pkg/kubernetes/service_account.go deleted file mode 100644 index 1edeebf00..000000000 --- a/pkg/kubernetes/service_account.go +++ /dev/null @@ -1,58 +0,0 @@ -// everest -// Copyright (C) 2023 Percona LLC -// -// 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 kubernetes ... -package kubernetes - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// CreateServiceAccount creates a new service account. -func (k *Kubernetes) CreateServiceAccount(name, namespace string) error { - sa := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - } - - return k.client.ApplyObject(sa) -} - -// CreateServiceAccountToken creates a new secret with service account token. -func (k *Kubernetes) CreateServiceAccountToken(serviceAccountName, secretName, namespace string) error { - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - }, - Name: secretName, - Namespace: namespace, - }, - Type: corev1.SecretTypeServiceAccountToken, - } - - return k.client.ApplyObject(secret) -} diff --git a/pkg/kubernetes/storage.go b/pkg/kubernetes/storage.go index e7dd0a029..1ddd2c468 100644 --- a/pkg/kubernetes/storage.go +++ b/pkg/kubernetes/storage.go @@ -5,14 +5,25 @@ import ( corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" ) -// GetPersistentVolumes returns list of persistent volumes. -func (k *Kubernetes) GetPersistentVolumes(ctx context.Context) (*corev1.PersistentVolumeList, error) { - return k.client.GetPersistentVolumes(ctx) +// ListPersistentVolumes returns list of persistent volumes that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListPersistentVolumes(ctx context.Context, opts ...ctrlclient.ListOption) (*corev1.PersistentVolumeList, error) { + result := &corev1.PersistentVolumeList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } -// GetStorageClasses returns list of storage classes. -func (k *Kubernetes) GetStorageClasses(ctx context.Context) (*storagev1.StorageClassList, error) { - return k.client.GetStorageClasses(ctx) +// ListStorageClasses returns list of storage classes that match the criteria. +// This method returns a list of full objects (meta and spec). +func (k *Kubernetes) ListStorageClasses(ctx context.Context, opts ...ctrlclient.ListOption) (*storagev1.StorageClassList, error) { + result := &storagev1.StorageClassList{} + if err := k.k8sClient.List(ctx, result, opts...); err != nil { + return nil, err + } + return result, nil } diff --git a/pkg/oidc/configure.go b/pkg/oidc/configure.go index 0f1adcc1c..56ab67866 100644 --- a/pkg/oidc/configure.go +++ b/pkg/oidc/configure.go @@ -24,6 +24,7 @@ import ( "slices" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/percona/everest/pkg/cli/steps" "github.com/percona/everest/pkg/cli/tui" @@ -35,7 +36,7 @@ import ( // OIDC describes the command to configure OIDC settings. type OIDC struct { config Config - kubeClient *kubernetes.Kubernetes + kubeClient kubernetes.KubernetesConnector l *zap.SugaredLogger } @@ -102,7 +103,7 @@ func NewOIDC(c Config, l *zap.SugaredLogger) (*OIDC, error) { cli.l = zap.NewNop().Sugar() } - k, err := cliutils.NewKubeclient(cli.l, c.KubeconfigPath) + k, err := cliutils.NewKubeConnector(cli.l, c.KubeconfigPath) if err != nil { return nil, err } @@ -169,7 +170,10 @@ func (u *OIDC) getOIDCProviderConfigureSteps() []steps.Step { stepList = append(stepList, steps.Step{ Desc: "Restarting Everest", F: func(ctx context.Context) error { - return u.kubeClient.RestartDeployment(ctx, common.PerconaEverestDeploymentName, common.SystemNamespace) + return u.kubeClient.RestartDeployment(ctx, types.NamespacedName{ + Namespace: common.SystemNamespace, + Name: common.PerconaEverestDeploymentName, + }) }, }, ) diff --git a/pkg/rbac/configmap-adapter/adapter.go b/pkg/rbac/configmap-adapter/adapter.go index a659d704e..4187d8e69 100644 --- a/pkg/rbac/configmap-adapter/adapter.go +++ b/pkg/rbac/configmap-adapter/adapter.go @@ -25,12 +25,13 @@ import ( "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" rbacutils "github.com/percona/everest/pkg/rbac/utils" ) type k8s interface { - GetConfigMap(ctx context.Context, namespace, name string) (*corev1.ConfigMap, error) + GetConfigMap(ctx context.Context, key ctrlclient.ObjectKey) (*corev1.ConfigMap, error) } // Adapter is the ConfigMap adapter for Casbin. @@ -56,20 +57,12 @@ func New( // ConfigMap returns the configmap used for RBAC. func (a *Adapter) ConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { - return a.kubeClient.GetConfigMap( - ctx, - a.namespacedName.Namespace, - a.namespacedName.Name, - ) + return a.kubeClient.GetConfigMap(ctx, a.namespacedName) } // LoadPolicy loads all policy rules from the storage. func (a *Adapter) LoadPolicy(model model.Model) error { - cm, err := a.kubeClient.GetConfigMap( - context.Background(), - a.namespacedName.Namespace, - a.namespacedName.Name, - ) + cm, err := a.kubeClient.GetConfigMap(context.Background(), a.namespacedName) if err != nil { return err } diff --git a/pkg/rbac/rbac.go b/pkg/rbac/rbac.go index 5fc17c289..16621fd88 100644 --- a/pkg/rbac/rbac.go +++ b/pkg/rbac/rbac.go @@ -80,14 +80,14 @@ type User struct { // This informer reloads the policy whenever the ConfigMap is updated. func refreshEnforcerInBackground( ctx context.Context, - kubeClient kubernetes.KubernetesConnector, + kubeConnector kubernetes.KubernetesConnector, enforcer *casbin.Enforcer, l *zap.SugaredLogger, ) error { inf, err := informer.New( - informer.WithConfig(kubeClient.Config()), + informer.WithConfig(kubeConnector.Config()), informer.WithLogger(l), - informer.Watches(&corev1.ConfigMap{}, kubeClient.Namespace()), + informer.Watches(&corev1.ConfigMap{}, common.SystemNamespace), ) inf.OnUpdate(func(_, newObj interface{}) { cm, ok := newObj.(*corev1.ConfigMap) @@ -158,21 +158,21 @@ func NewIOReaderEnforcer(r io.Reader) (*casbin.Enforcer, error) { } // NewEnforcerWithRefresh creates a new enforcer that refreshes the policy whenever the ConfigMap is updated. -func NewEnforcerWithRefresh(ctx context.Context, kubeClient kubernetes.KubernetesConnector, l *zap.SugaredLogger) (*casbin.Enforcer, error) { - enf, err := NewEnforcer(ctx, kubeClient, l) +func NewEnforcerWithRefresh(ctx context.Context, kubeConnector kubernetes.KubernetesConnector, l *zap.SugaredLogger) (*casbin.Enforcer, error) { + enf, err := NewEnforcer(ctx, kubeConnector, l) if err != nil { return nil, err } - return enf, refreshEnforcerInBackground(ctx, kubeClient, enf, l) + return enf, refreshEnforcerInBackground(ctx, kubeConnector, enf, l) } // NewEnforcer creates a new Casbin enforcer with the RBAC model and ConfigMap adapter. -func NewEnforcer(ctx context.Context, kubeClient kubernetes.KubernetesConnector, l *zap.SugaredLogger) (*casbin.Enforcer, error) { +func NewEnforcer(ctx context.Context, kubeConnector kubernetes.KubernetesConnector, l *zap.SugaredLogger) (*casbin.Enforcer, error) { cmReq := types.NamespacedName{ Namespace: common.SystemNamespace, Name: common.EverestRBACConfigMapName, } - adapter := configmapadapter.New(l, kubeClient, cmReq) + adapter := configmapadapter.New(l, kubeConnector, cmReq) enforcer, err := newEnforcer(adapter, false) if err != nil { return nil, err @@ -308,7 +308,7 @@ func NewSkipper(basePath string) (func(echo.Context) bool, error) { // Can checks if a user is allowed to perform an action on a resource. // Input request should be of the form [user action resource object]. -func Can(ctx context.Context, filePath string, k *kubernetes.Kubernetes, req ...string) (bool, error) { +func Can(ctx context.Context, filePath string, k kubernetes.KubernetesConnector, req ...string) (bool, error) { if len(req) != 4 { //nolint:mnd return false, errors.New("expected input of the form [user action resource object]") } diff --git a/pkg/rbac/validate.go b/pkg/rbac/validate.go index 3c6572659..276e6902f 100644 --- a/pkg/rbac/validate.go +++ b/pkg/rbac/validate.go @@ -49,7 +49,7 @@ func validatePolicy(enforcer *casbin.Enforcer) error { // ValidatePolicy validates a policy from either Kubernetes or local file. func ValidatePolicy( ctx context.Context, - k *kubernetes.Kubernetes, + k kubernetes.KubernetesConnector, filepath string, ) error { enforcer, err := newKubeOrFileEnforcer(ctx, k, filepath) @@ -111,7 +111,7 @@ func validateTerms(terms []string) error { //nolint:nonamedreturns func newKubeOrFileEnforcer( ctx context.Context, - kubeClient *kubernetes.Kubernetes, + kubeClient kubernetes.KubernetesConnector, filePath string, ) (e *casbin.Enforcer, err error) { defer func() { diff --git a/pkg/version/everest.go b/pkg/version/everest.go index bc6c1289c..61dcaaad2 100644 --- a/pkg/version/everest.go +++ b/pkg/version/everest.go @@ -24,26 +24,27 @@ import ( goversion "github.com/hashicorp/go-version" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/apimachinery/pkg/types" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/everest/pkg/common" ) type deploymentGetter interface { - GetDeployment(ctx context.Context, name, namespace string) (*appsv1.Deployment, error) + GetDeployment(ctx context.Context, key ctrlclient.ObjectKey) (*appsv1.Deployment, error) } // EverestVersionFromDeployment returns Everest version from the k8s deployment resource. func EverestVersionFromDeployment(ctx context.Context, dg deploymentGetter) (*goversion.Version, error) { - dep, err := dg.GetDeployment(ctx, common.PerconaEverestDeploymentName, common.SystemNamespace) + dep, err := dg.GetDeployment(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.PerconaEverestDeploymentName}) // Ignore not found error, we will try again with legacy name. - if client.IgnoreNotFound(err) != nil { + if ctrlclient.IgnoreNotFound(err) != nil { return nil, err } // If the deployment is not found, try to get it with the legacy name. if dep == nil || dep.GetCreationTimestamp().Time.IsZero() { - dep, err = dg.GetDeployment(ctx, common.PerconaEverestDeploymentNameLegacy, common.SystemNamespace) + dep, err = dg.GetDeployment(ctx, types.NamespacedName{Namespace: common.SystemNamespace, Name: common.PerconaEverestDeploymentNameLegacy}) if err != nil { return nil, err }