diff --git a/cmd/admin_k9s.go b/cmd/admin_k9s.go index a4e3f9d1..afa4438b 100644 --- a/cmd/admin_k9s.go +++ b/cmd/admin_k9s.go @@ -46,7 +46,7 @@ func launchK9s(args []string) { } clusterId := args[0] - kubeconfig := pkg.GetKubeconfigByClusterId(clusterId) + kubeconfig := pkg.GetKubeconfigByClusterId(clusterId, false) filePath := utils.WriteInFile(clusterId, "kubeconfig", []byte(kubeconfig)) if err := os.Setenv("KUBECONFIG", filePath); err != nil { log.Fatal(err) diff --git a/cmd/cluster_get_token.go b/cmd/cluster_get_token.go index 711b0048..4d427a09 100644 --- a/cmd/cluster_get_token.go +++ b/cmd/cluster_get_token.go @@ -7,17 +7,20 @@ import ( "github.com/spf13/cobra" ) +var getTokenReadOnly bool + var getTokenCommand = &cobra.Command{ Use: "get-token", Short: "Get token for a cluster ID", Run: func(cmd *cobra.Command, args []string) { validateGetTokenFlags() - getToken() + getToken(getTokenReadOnly) }, } func init() { getTokenCommand.Flags().StringVarP(&clusterId, "cluster-id", "c", "", "Cluster ID") + getTokenCommand.Flags().BoolVarP(&getTokenReadOnly, "read-only", "r", false, "Get a read-only service account token instead of an admin token") clusterCmd.AddCommand(getTokenCommand) } @@ -27,7 +30,7 @@ func validateGetTokenFlags() { } } -func getToken() { - response := pkg.GetTokenByClusterId(clusterId) +func getToken(readOnly bool) { + response := pkg.GetTokenByClusterId(clusterId, readOnly) utils.Println(response) } diff --git a/cmd/cluster_kubeconfig.go b/cmd/cluster_kubeconfig.go index 38e5e77f..f1b5f2f4 100644 --- a/cmd/cluster_kubeconfig.go +++ b/cmd/cluster_kubeconfig.go @@ -12,19 +12,25 @@ import ( "github.com/spf13/cobra" ) +var readOnlyKubeconfig bool + var downloadKubeconfigCmd = &cobra.Command{ Use: "kubeconfig", Short: "Retrieve kubeconfig with a cluster ID", Run: func(cmd *cobra.Command, args []string) { validateKubeconfigFlags() - kubeconfigFilename := downloadKubeconfig(clusterId) + kubeconfigFilename := downloadKubeconfig(clusterId, readOnlyKubeconfig) log.Info("Kubeconfig file created in the current directory.") log.Info("Execute `export KUBECONFIG=" + kubeconfigFilename + "` to use it.") + if readOnlyKubeconfig { + log.Info("This kubeconfig uses read-only access (ServiceAccount with view ClusterRole).") + } }, } func init() { downloadKubeconfigCmd.Flags().StringVarP(&clusterId, "cluster-id", "c", "", "Cluster ID") + downloadKubeconfigCmd.Flags().BoolVarP(&readOnlyKubeconfig, "read-only", "r", false, "Download a read-only kubeconfig backed by a Kubernetes service account with the view ClusterRole") clusterCmd.AddCommand(downloadKubeconfigCmd) } @@ -35,11 +41,9 @@ func validateKubeconfigFlags() { } } -func downloadKubeconfig(clusterId string) string { - // download kubeconfig - kubeconfig := pkg.GetKubeconfigByClusterId(clusterId) +func downloadKubeconfig(clusterId string, readOnly bool) string { + kubeconfig := pkg.GetKubeconfigByClusterId(clusterId, readOnly) - // get current working directory dir, err := os.Getwd() if err != nil { @@ -47,8 +51,11 @@ func downloadKubeconfig(clusterId string) string { os.Exit(1) } - kubeconfigFilename := filepath.Join(dir, "kubeconfig-"+clusterId+".yaml") - // create a file in the current folder + suffix := "" + if readOnly { + suffix = "-readonly" + } + kubeconfigFilename := filepath.Join(dir, "kubeconfig"+suffix+"-"+clusterId+".yaml") writeError := os.WriteFile(kubeconfigFilename, []byte(kubeconfig), 0600) if writeError != nil { utils.PrintlnError(writeError) diff --git a/cmd/terraform_setup_backend.go b/cmd/terraform_setup_backend.go index 68d96999..4e91ddf7 100644 --- a/cmd/terraform_setup_backend.go +++ b/cmd/terraform_setup_backend.go @@ -31,7 +31,7 @@ var terraformSetupBackendCmd = &cobra.Command{ utils.Println(fmt.Sprintf("Preparing backend.tf file for terraform `%s` of environment `%s`", terraform.Name, env.Name)) // Download kubeconfig to connect to the cluster - kubeconfigPath := downloadKubeconfig(env.ClusterId) + kubeconfigPath := downloadKubeconfig(env.ClusterId, false) // Create kubeclient to retrieve the namespace of the tfstate secret kubeconfig, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) diff --git a/go.mod b/go.mod index 44fef392..c512badb 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/posthog/posthog-go v1.12.5 github.com/pterm/pterm v0.12.83 - github.com/qovery/qovery-client-go v0.0.0-20260609072636-f548ebe903f2 + github.com/qovery/qovery-client-go v0.0.0-20260610095547-986d768ca7f9 github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index e17d13f8..0702a9f1 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,8 @@ github.com/posthog/posthog-go v1.12.5 h1:l/x3mpqisXJ0sTOyyRutsTQAgiWYuJT1uhN4cQr github.com/posthog/posthog-go v1.12.5/go.mod h1:xsVOW9YImilUcazwPNEq4PJDqEZf2KeCS758zXjwkPg= github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= -github.com/qovery/qovery-client-go v0.0.0-20260609072636-f548ebe903f2 h1:R6lG3dFH9/N7k7Hz+MMMCY1y3c0N9rmY6NAAjy1dkos= -github.com/qovery/qovery-client-go v0.0.0-20260609072636-f548ebe903f2/go.mod h1:mcXeQtxR4AIGIBaWLhy52S16UwL8/1fcDywDuSK1BZ4= +github.com/qovery/qovery-client-go v0.0.0-20260610095547-986d768ca7f9 h1:vYYPlj1RNfR/8RXTE8ATWsguR25mVxktq3HNIUqPhyA= +github.com/qovery/qovery-client-go v0.0.0-20260610095547-986d768ca7f9/go.mod h1:mcXeQtxR4AIGIBaWLhy52S16UwL8/1fcDywDuSK1BZ4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/pkg/admin_load_credentials.go b/pkg/admin_load_credentials.go index 8756eb25..b88477d9 100644 --- a/pkg/admin_load_credentials.go +++ b/pkg/admin_load_credentials.go @@ -53,7 +53,7 @@ func LoadCredentials(clusterId string, doNotConnectToBastion bool) error { } utils.PrintlnInfo(fmt.Sprintf("Set environment variable %s for child process", cred.Key)) } - kubeconfig := GetKubeconfigByClusterId(clusterId) + kubeconfig := GetKubeconfigByClusterId(clusterId, false) filePath := utils.WriteInFile(clusterId, "kubeconfig", []byte(kubeconfig)) if err := os.Setenv("KUBECONFIG", filePath); err != nil { return fmt.Errorf("failed to set KUBECONFIG: %w", err) diff --git a/pkg/cluster.go b/pkg/cluster.go index e4140e29..28321ff8 100644 --- a/pkg/cluster.go +++ b/pkg/cluster.go @@ -10,7 +10,7 @@ import ( "github.com/qovery/qovery-client-go" ) -func GetKubeconfigByClusterId(clusterId string) string { +func GetKubeconfigByClusterId(clusterId string, readOnly bool) string { qoveryClient := GetQoveryClientInstance() request := qoveryClient.ClustersAPI.GetClusterKubeconfig( @@ -18,6 +18,11 @@ func GetKubeconfigByClusterId(clusterId string) string { "00000000-0000-0000-000000000000", clusterId, ).WithTokenFromCli(true) + + if readOnly { + request = request.ReadOnly(true) + } + response, httpResponse, err := qoveryClient.ClustersAPI.GetClusterKubeconfigExecute(request) if err != nil { utils.PrintlnError(err) @@ -50,10 +55,13 @@ func UpdateClusterKubeconfig(organizationId string, clusterId string, kubeconfig return nil } -func GetTokenByClusterId(clusterId string) string { +func GetTokenByClusterId(clusterId string, readOnly bool) string { qoveryClient := GetQoveryClientInstance() request := qoveryClient.DefaultAPI.GetClusterTokenByClusterId(context.Background(), clusterId) + if readOnly { + request = request.ReadOnly(true) + } _, response, err := qoveryClient.DefaultAPI.GetClusterTokenByClusterIdExecute(request) if err != nil { utils.PrintlnError(err)