diff --git a/cmd/root.go b/cmd/root.go index dafcfd4..ff1eb97 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,17 +4,15 @@ package cmd import ( - "fmt" - "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "cloudctl", - Short: "A CLI tool to manage clusters, including OIDC login and kubeconfig sync.", + Short: "A CLI tool to access Greenhouse clusters", Long: `cloudctl is a command line interface that helps: - 1) Fetch and merge Kubeconfigs from a special cluster CRD`, + 1) Fetch and merge kubeconfigs from central Greenhouse cluster`, } func Execute() error { @@ -25,8 +23,3 @@ func init() { // Add subcommands here rootCmd.AddCommand(syncCmd) } - -// A utility function that might be used across multiple commands -func printDebugMessage(msg string) { - fmt.Println("DEBUG:", msg) -} diff --git a/cmd/sync.go b/cmd/sync.go index bb1ed14..5e5ef61 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -12,69 +12,94 @@ import ( "maps" "strings" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "sigs.k8s.io/controller-runtime/pkg/client" ) var ( - greenhouseCentralClusterKubeconfig string - greenhouseRemoteClusterKubeconfig string - prefix string - mergeIdenticalUsers bool + greenhouseClusterKubeconfig string + greenhouseClusterContext string + greenhouseClusterNamespace string + remoteClusterKubeconfig string + remoteClusterName string + prefix string + mergeIdenticalUsers bool ) func init() { - syncCmd.Flags().StringVar(&greenhouseCentralClusterKubeconfig, "central-cluster-kubeconfig", clientcmd.RecommendedHomeFile, "kubeconfig for central Greenhouse cluster") - syncCmd.Flags().StringVar(&greenhouseRemoteClusterKubeconfig, "remote-cluster-kubeconfig", clientcmd.RecommendedHomeFile, "kubeconfig for remote Greenhouse clusters") - syncCmd.Flags().StringVar(&prefix, "prefix", "cloudctl", "prefix for kubeconfig entries. It is used to separate and manage the entries of this tool only") + syncCmd.Flags().StringVarP(&greenhouseClusterKubeconfig, "greenhouse-cluster-kubeconfig", "k", clientcmd.RecommendedHomeFile, "kubeconfig file path for Greenhouse cluster") + syncCmd.Flags().StringVarP(&greenhouseClusterContext, "greenhouse-cluster-context", "c", "", "context in greenhouse-cluster-kubeconfig, the context in the file is used if this flag is not set") + syncCmd.Flags().StringVarP(&greenhouseClusterNamespace, "greenhouse-cluster-namespace", "n", "", "namespace for greenhouse-cluster-kubeconfig, it is the same value as Greenhouse organization") + syncCmd.MarkFlagRequired("greenhouse-cluster-namespace") + syncCmd.Flags().StringVarP(&remoteClusterKubeconfig, "remote-cluster-kubeconfig", "r", clientcmd.RecommendedHomeFile, "kubeconfig file path for remote clusters") + syncCmd.Flags().StringVar(&remoteClusterName, "remote-cluster-name", "", "name of the remote cluster, if not set all clusters are retrieved") + syncCmd.Flags().StringVar(&prefix, "prefix", "cloudctl", "prefix for kubeconfig entries. it is used to separate and manage the entries of this tool only") syncCmd.Flags().BoolVar(&mergeIdenticalUsers, "merge-identical-users", true, "merge identical user information in kubeconfig file so that you only login once for the clusters that share the same auth info") } var ( syncCmd = &cobra.Command{ Use: "sync", - Short: "Fetches remote kubeconfigs from Greenhouse cluster and merges them into your local config", + Short: "Fetches kubeconfigs of remote clusters from Greenhouse cluster and merges them into your local config", RunE: runSync, } ) func runSync(cmd *cobra.Command, args []string) error { - centralConfig, err := clientcmd.BuildConfigFromFlags("", greenhouseCentralClusterKubeconfig) + centralConfig, err := clientcmd.BuildConfigFromFlags("", greenhouseClusterKubeconfig) if err != nil { - return fmt.Errorf("failed to build central kubeconfig: %w", err) + return fmt.Errorf("failed to build greenhouse kubeconfig: %w", err) } - dynamicClient, err := dynamic.NewForConfig(centralConfig) - if err != nil { - return fmt.Errorf("failed to create dynamic client: %w", err) + if greenhouseClusterContext != "" { + centralConfig, err = configWithContext(greenhouseClusterContext, greenhouseClusterKubeconfig) + if err != nil { + return fmt.Errorf("failed to build greenhouse kubeconfig with context %s: %w", greenhouseClusterContext, err) + } } - gvr := schema.GroupVersionResource{ - Group: "greenhouse.sap", - Version: "v1alpha1", - Resource: "clusterkubeconfigs", + // Create a scheme and register Greenhouse types. + scheme := runtime.NewScheme() + if err := v1alpha1.AddToScheme(scheme); err != nil { + return fmt.Errorf("failed to add greenhouse scheme: %w", err) } - unstructuredList, err := dynamicClient.Resource(gvr).Namespace("ccloud").List(cmd.Context(), metav1.ListOptions{}) + // Create a typed client. + c, err := client.New(centralConfig, client.Options{Scheme: scheme}) if err != nil { - return fmt.Errorf("failed to list ClusterKubeconfigs: %w", err) + return fmt.Errorf("failed to create client: %w", err) } - if len(unstructuredList.Items) == 0 { + ctx := cmd.Context() + var clusterKubeconfigs []v1alpha1.ClusterKubeconfig + + // If a specific remote cluster name is provided, fetch that single resource; + // otherwise, list all ClusterKubeconfigs in the given namespace. + if remoteClusterName != "" { + var ckc v1alpha1.ClusterKubeconfig + if err := c.Get(ctx, client.ObjectKey{Namespace: greenhouseClusterNamespace, Name: remoteClusterName}, &ckc); err != nil { + return fmt.Errorf("failed to get ClusterKubeconfig %q: %w", remoteClusterName, err) + } + clusterKubeconfigs = append(clusterKubeconfigs, ckc) + } else { + var list v1alpha1.ClusterKubeconfigList + if err := c.List(ctx, &list, client.InNamespace(greenhouseClusterNamespace)); err != nil { + return fmt.Errorf("failed to list ClusterKubeconfigs: %w", err) + } + clusterKubeconfigs = list.Items + } + if len(clusterKubeconfigs) == 0 { log.Println("No ClusterKubeconfigs found to sync.") return nil } - localConfig, err := clientcmd.LoadFromFile(greenhouseRemoteClusterKubeconfig) + localConfig, err := clientcmd.LoadFromFile(remoteClusterKubeconfig) if err != nil { return fmt.Errorf("failed to load local kubeconfig: %w", err) } @@ -83,7 +108,7 @@ func runSync(cmd *cobra.Command, args []string) error { localConfig = clientcmdapi.NewConfig() } - serverConfig, err := buildIncomingKubeconfig(unstructuredList.Items) + serverConfig, err := buildIncomingKubeconfig(clusterKubeconfigs) if err != nil { return fmt.Errorf("failed to create server config: %w", err) } @@ -93,52 +118,47 @@ func runSync(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to merge ClusterKubeconfig: %w", err) } - err = writeConfig(localConfig, greenhouseRemoteClusterKubeconfig) + err = writeConfig(localConfig, remoteClusterKubeconfig) if err != nil { return fmt.Errorf("failed to write merged kubeconfig: %w", err) } - log.Println("Successfully synced and merged the new cluster kubeconfig with your local config.") + log.Println("Successfully synced and merged into your local config.") return nil } -func buildIncomingKubeconfig(items []unstructured.Unstructured) (*clientcmdapi.Config, error) { +// buildIncomingKubeconfig converts the list of typed ClusterKubeconfig objects +// into a clientcmdapi.Config. +func buildIncomingKubeconfig(items []v1alpha1.ClusterKubeconfig) (*clientcmdapi.Config, error) { kubeconfig := clientcmdapi.NewConfig() - for _, unstructuredItem := range items { - var ckc v1alpha1.ClusterKubeconfig - err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredItem.Object, &ckc) - if err != nil { - return nil, fmt.Errorf("failed to convert unstructured to ClusterKubeconfig: %w", err) - } - - // Assuming each ClusterKubeconfig has exactly one context, authInfo, and cluster + for _, ckc := range items { + // Assuming each ClusterKubeconfig has exactly one context, authInfo, and cluster. if len(ckc.Spec.Kubeconfig.Contexts) > 0 { - ctx := ckc.Spec.Kubeconfig.Contexts[0] - kubeconfig.Contexts[ctx.Name] = &clientcmdapi.Context{ - Cluster: ctx.Context.Cluster, - AuthInfo: ctx.Context.AuthInfo, - Namespace: ctx.Context.Namespace, + ctxItem := ckc.Spec.Kubeconfig.Contexts[0] + kubeconfig.Contexts[ctxItem.Name] = &clientcmdapi.Context{ + Cluster: ctxItem.Context.Cluster, + AuthInfo: ctxItem.Context.AuthInfo, + Namespace: ctxItem.Context.Namespace, } } if len(ckc.Spec.Kubeconfig.AuthInfo) > 0 { - auth := ckc.Spec.Kubeconfig.AuthInfo[0].AuthInfo - kubeconfig.AuthInfos[ckc.Spec.Kubeconfig.AuthInfo[0].Name] = &clientcmdapi.AuthInfo{ - ClientCertificateData: auth.ClientCertificateData, - ClientKeyData: auth.ClientKeyData, - AuthProvider: &auth.AuthProvider, + authItem := ckc.Spec.Kubeconfig.AuthInfo[0] + kubeconfig.AuthInfos[authItem.Name] = &clientcmdapi.AuthInfo{ + ClientCertificateData: authItem.AuthInfo.ClientCertificateData, + ClientKeyData: authItem.AuthInfo.ClientKeyData, + AuthProvider: &authItem.AuthInfo.AuthProvider, } } if len(ckc.Spec.Kubeconfig.Clusters) > 0 { - cluster := ckc.Spec.Kubeconfig.Clusters[0].Cluster - kubeconfig.Clusters[ckc.Spec.Kubeconfig.Clusters[0].Name] = &clientcmdapi.Cluster{ - Server: cluster.Server, - CertificateAuthorityData: cluster.CertificateAuthorityData, + clusterItem := ckc.Spec.Kubeconfig.Clusters[0] + kubeconfig.Clusters[clusterItem.Name] = &clientcmdapi.Cluster{ + Server: clusterItem.Cluster.Server, + CertificateAuthorityData: clusterItem.Cluster.CertificateAuthorityData, } } - } return kubeconfig, nil @@ -447,3 +467,11 @@ func mergeAuthInfo(serverAuth, localAuth *clientcmdapi.AuthInfo) *clientcmdapi.A return mergedAuth } + +func configWithContext(context, kubeconfigPath string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, + &clientcmd.ConfigOverrides{ + CurrentContext: context, + }).ClientConfig() +} diff --git a/go.mod b/go.mod index 1b9b939..50e745b 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,35 @@ go 1.23.4 require ( github.com/cloudoperators/greenhouse v0.0.1-alpha.1 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.19.0 - k8s.io/apimachinery v0.32.1 - k8s.io/client-go v0.32.1 + k8s.io/apimachinery v0.32.2 + k8s.io/client-go v0.32.2 + sigs.k8s.io/controller-runtime v0.20.2 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -32,7 +43,7 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -43,12 +54,14 @@ require ( golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.1 // indirect - k8s.io/apiextensions-apiserver v0.30.4 // indirect + k8s.io/api v0.32.2 // indirect + k8s.io/apiextensions-apiserver v0.32.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect diff --git a/go.sum b/go.sum index d8da326..cf918af 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,14 @@ github.com/cloudoperators/greenhouse v0.0.1-alpha.1 h1:gU18Xi4r1mFz0xCsDQxbiFb7cFgGPNdAr7HX1+pG4YI= github.com/cloudoperators/greenhouse v0.0.1-alpha.1/go.mod h1:K2T5MEUVrHJRBLke2Ri13b65LKjgqUGWZMUr68hKetY= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -15,6 +17,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= @@ -66,10 +70,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -90,10 +94,10 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -114,6 +118,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -169,20 +175,22 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= -k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= -k8s.io/apiextensions-apiserver v0.30.4 h1:FwOMIk/rzZvM/Gx0IOz0+biZ+dlnlCeyfXW17uzV1qE= -k8s.io/apiextensions-apiserver v0.30.4/go.mod h1:m8cAkJ9PVU8Olb4cPW4hrUDBZGvoSJ0kY0G0CfdGQac= -k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= -k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= +k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.20.2 h1:/439OZVxoEc02psi1h4QO3bHzTgu49bb347Xp4gW1pc= +sigs.k8s.io/controller-runtime v0.20.2/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=