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/pkg/admin_load_credentials.go b/pkg/admin_load_credentials.go index d4e52b6e..458571ac 100644 --- a/pkg/admin_load_credentials.go +++ b/pkg/admin_load_credentials.go @@ -52,7 +52,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..3d539ff4 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,9 +55,12 @@ func UpdateClusterKubeconfig(organizationId string, clusterId string, kubeconfig return nil } -func GetTokenByClusterId(clusterId string) string { +func GetTokenByClusterId(clusterId string, readOnly bool) string { qoveryClient := GetQoveryClientInstance() + // readOnly is accepted by the CLI flag but not yet passed to the server — + // token generation for the qovery-readonly SA requires cluster-agent gRPC (future work). + _ = readOnly request := qoveryClient.DefaultAPI.GetClusterTokenByClusterId(context.Background(), clusterId) _, response, err := qoveryClient.DefaultAPI.GetClusterTokenByClusterIdExecute(request) if err != nil {