From 5f02817ec27f63edf0d7cc46ea1bd9c734211f99 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 09:05:08 +0000 Subject: [PATCH 01/10] Refactor auth commands into package --- internal/command/auth.go | 212 +------------------------------- internal/command/auth/login.go | 75 +++++++++++ internal/command/auth/logout.go | 31 +++++ internal/command/auth/status.go | 84 +++++++++++++ internal/command/auth/utils.go | 59 +++++++++ 5 files changed, 253 insertions(+), 208 deletions(-) create mode 100644 internal/command/auth/login.go create mode 100644 internal/command/auth/logout.go create mode 100644 internal/command/auth/status.go create mode 100644 internal/command/auth/utils.go diff --git a/internal/command/auth.go b/internal/command/auth.go index b705ceb..5528b15 100644 --- a/internal/command/auth.go +++ b/internal/command/auth.go @@ -1,18 +1,9 @@ package command import ( - "context" - "errors" - "fmt" - "os" - - "github.com/golang-jwt/jwt/v4" "github.com/spf13/cobra" - "github.com/toqueteos/webbrowser" - "golang.org/x/oauth2" - "github.com/jetstack/jsctl/internal/auth" - "github.com/jetstack/jsctl/internal/config" + "github.com/jetstack/jsctl/internal/command/auth" ) // Auth returns a cobra.Command instance that is the root for all "jsctl auth" subcommands. @@ -23,205 +14,10 @@ func Auth() *cobra.Command { } cmd.AddCommand( - authLogin(), - authLogout(), - authStatus(), - ) - - return cmd -} - -func authStatus() *cobra.Command { - var credentials string - - cmd := &cobra.Command{ - Use: "status", - Short: "Print the logged in account and token location", - Args: cobra.ExactArgs(0), - Run: run(func(ctx context.Context, args []string) error { - var token *oauth2.Token - var err error - var tokenPath string - if credentials != "" { - tokenPath = credentials - token, err = loginWithCredentials(ctx, auth.GetOAuthConfig(), credentials) - if err != nil { - return fmt.Errorf("failed to login with credentials file %q: %w", credentials, err) - } - } else { - tokenPath, err = auth.DetermineTokenFilePath(ctx) - if err != nil { - return fmt.Errorf("failed to determine token path: %w", err) - } - if _, err := os.Stat(tokenPath); errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("token missing at %s", tokenPath) - } - - fmt.Println("Token path:", tokenPath) - - token, err = auth.LoadOAuthToken(ctx) - if err != nil { - fmt.Println("Not logged in") - return nil - } - } - - claims := jwt.MapClaims{} - _, err = jwt.ParseWithClaims(token.AccessToken, claims, func(token *jwt.Token) (interface{}, error) { - return []byte(""), nil - }) - - email, ok := claims["https://jetstack.io/claims/name"].(string) - if ok { - fmt.Println("Logged in as:", email) - } - - cnf, ok := config.FromContext(ctx) - if !ok || cnf.Organization == "" { - fmt.Println("You do not have an organization selected, select one using: \n\n\tjsctl config set organization [name]\n\n" + - "To view organizations you have access to, list them using: \n\n\tjsctl organizations list") - return nil - } - fmt.Println("Current Organization:", cnf.Organization) - - return nil - }), - } - - flags := cmd.PersistentFlags() - flags.StringVar( - &credentials, - "credentials", - os.Getenv("JSCTL_CREDENTIALS"), - "The location of a credentials file to use instead of the normal oauth login flow", - ) - - return cmd -} - -func authLogin() *cobra.Command { - var credentials string - var disconnected bool - - cmd := &cobra.Command{ - Use: "login", - Short: "Performs the authentication flow to allow access to other commands", - Args: cobra.ExactArgs(0), - Run: run(func(ctx context.Context, args []string) error { - oAuthConfig := auth.GetOAuthConfig() - - var err error - var token *oauth2.Token - if credentials != "" { - token, err = loginWithCredentials(ctx, oAuthConfig, credentials) - } else { - token, err = loginWithOAuth(ctx, oAuthConfig, disconnected) - } - - if err != nil { - return fmt.Errorf("failed to obtain token: %w", err) - } - - if err = auth.SaveOAuthToken(ctx, token); err != nil { - return fmt.Errorf("failed to save token: %w", err) - } - - fmt.Println("Login succeeded") - - err = config.Save(ctx, &config.Config{}) - if err != nil { - return fmt.Errorf("failed to save configuration: %w", err) - } - - cnf, ok := config.FromContext(ctx) - if !ok || cnf.Organization == "" { - fmt.Println("You do not have an organization selected, select one using: \n\n\tjsctl config set organization [name]\n\n" + - "To view organizations you have access to, list them using: \n\n\tjsctl organizations list") - } - - return nil - }), - } - - flags := cmd.PersistentFlags() - flags.StringVar( - &credentials, - "credentials", - os.Getenv("JSCTL_CREDENTIALS"), - "The location of service account credentials file to use instead of the normal oauth login flow", - ) - flags.BoolVar( - &disconnected, - "disconnected", - false, - "Use a disconnected login flow where browser and terminal are not running on the same machine", + auth.Login(run), + auth.Logout(run), + auth.Status(run), ) return cmd } - -func authLogout() *cobra.Command { - return &cobra.Command{ - Use: "logout", - Args: cobra.ExactArgs(0), - Run: run(func(ctx context.Context, args []string) error { - err := auth.DeleteOAuthToken(ctx) - switch { - case errors.Is(err, auth.ErrNoToken): - return fmt.Errorf("host contains no authentication data") - case err != nil: - return fmt.Errorf("failed to remove authentication data: %w", err) - default: - fmt.Println("You were logged out successfully") - return nil - } - }), - } -} - -func loginWithOAuth(ctx context.Context, oAuthConfig *oauth2.Config, disconnected bool) (*oauth2.Token, error) { - url, state := auth.GetOAuthURLAndState(oAuthConfig) - - // disconnected can be set to true when the browser and terminal are not running - // on the same machine. - if disconnected { - fmt.Printf("Navigate to the URL below to login:\n%s\n", url) - token, err := auth.WaitForOAuthTokenCommandLine(ctx, oAuthConfig, state) - if err != nil { - return nil, fmt.Errorf("failed to obtain token: %w", err) - } - return token, nil - } - - fmt.Println("Opening browser to:", url) - - if err := webbrowser.Open(url); err != nil { - fmt.Printf("Navigate to the URL below to login:\n%s\n", url) - } else { - fmt.Println("You will be taken to your browser for authentication") - } - - token, err := auth.WaitForOAuthTokenCallback(ctx, oAuthConfig, state) - if err != nil { - return nil, err - } - - return token, nil -} - -func loginWithCredentials(ctx context.Context, oAuthConfig *oauth2.Config, location string) (*oauth2.Token, error) { - credentials, err := auth.LoadCredentials(location) - switch { - case errors.Is(err, auth.ErrNoCredentials): - return nil, fmt.Errorf("no service account was found at: %s", location) - case err != nil: - return nil, fmt.Errorf("failed to read service account key: %w", err) - } - - token, err := auth.GetOAuthTokenForCredentials(ctx, oAuthConfig, credentials) - if err != nil { - return nil, err - } - - return token, nil -} diff --git a/internal/command/auth/login.go b/internal/command/auth/login.go new file mode 100644 index 0000000..21b994c --- /dev/null +++ b/internal/command/auth/login.go @@ -0,0 +1,75 @@ +package auth + +import ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + "golang.org/x/oauth2" + + "github.com/jetstack/jsctl/internal/auth" + "github.com/jetstack/jsctl/internal/command/types" + "github.com/jetstack/jsctl/internal/config" +) + +func Login(run types.RunFunc) *cobra.Command { + var credentials string + var disconnected bool + + cmd := &cobra.Command{ + Use: "login", + Short: "Performs the authentication flow to allow access to other commands", + Args: cobra.ExactArgs(0), + Run: run(func(ctx context.Context, args []string) error { + oAuthConfig := auth.GetOAuthConfig() + + var err error + var token *oauth2.Token + if credentials != "" { + token, err = loginWithCredentials(ctx, oAuthConfig, credentials) + } else { + token, err = loginWithOAuth(ctx, oAuthConfig, disconnected) + } + + if err != nil { + return fmt.Errorf("failed to obtain token: %w", err) + } + + if err = auth.SaveOAuthToken(ctx, token); err != nil { + return fmt.Errorf("failed to save token: %w", err) + } + + fmt.Println("Login succeeded") + + err = config.Save(ctx, &config.Config{}) + if err != nil { + return fmt.Errorf("failed to save configuration: %w", err) + } + + cnf, ok := config.FromContext(ctx) + if !ok || cnf.Organization == "" { + fmt.Println("You do not have an organization selected, select one using: \n\n\tjsctl config set organization [name]\n\n" + + "To view organizations you have access to, list them using: \n\n\tjsctl organizations list") + } + + return nil + }), + } + + flags := cmd.PersistentFlags() + flags.StringVar( + &credentials, + "credentials", + os.Getenv("JSCTL_CREDENTIALS"), + "The location of service account credentials file to use instead of the normal oauth login flow", + ) + flags.BoolVar( + &disconnected, + "disconnected", + false, + "Use a disconnected login flow where browser and terminal are not running on the same machine", + ) + + return cmd +} diff --git a/internal/command/auth/logout.go b/internal/command/auth/logout.go new file mode 100644 index 0000000..0966f6b --- /dev/null +++ b/internal/command/auth/logout.go @@ -0,0 +1,31 @@ +package auth + +import ( + "context" + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/jetstack/jsctl/internal/auth" + "github.com/jetstack/jsctl/internal/command/types" +) + +func Logout(run types.RunFunc) *cobra.Command { + return &cobra.Command{ + Use: "logout", + Args: cobra.ExactArgs(0), + Run: run(func(ctx context.Context, args []string) error { + err := auth.DeleteOAuthToken(ctx) + switch { + case errors.Is(err, auth.ErrNoToken): + return fmt.Errorf("host contains no authentication data") + case err != nil: + return fmt.Errorf("failed to remove authentication data: %w", err) + default: + fmt.Println("You were logged out successfully") + return nil + } + }), + } +} diff --git a/internal/command/auth/status.go b/internal/command/auth/status.go new file mode 100644 index 0000000..1a21f43 --- /dev/null +++ b/internal/command/auth/status.go @@ -0,0 +1,84 @@ +package auth + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/golang-jwt/jwt/v4" + "github.com/spf13/cobra" + "golang.org/x/oauth2" + + "github.com/jetstack/jsctl/internal/auth" + "github.com/jetstack/jsctl/internal/command/types" + "github.com/jetstack/jsctl/internal/config" +) + +func Status(run types.RunFunc) *cobra.Command { + var credentials string + + cmd := &cobra.Command{ + Use: "status", + Short: "Print the logged in account and token location", + Args: cobra.ExactArgs(0), + Run: run(func(ctx context.Context, args []string) error { + var token *oauth2.Token + var err error + var tokenPath string + if credentials != "" { + tokenPath = credentials + token, err = loginWithCredentials(ctx, auth.GetOAuthConfig(), credentials) + if err != nil { + return fmt.Errorf("failed to login with credentials file %q: %w", credentials, err) + } + } else { + tokenPath, err = auth.DetermineTokenFilePath(ctx) + if err != nil { + return fmt.Errorf("failed to determine token path: %w", err) + } + if _, err := os.Stat(tokenPath); errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("token missing at %s", tokenPath) + } + + fmt.Println("Token path:", tokenPath) + + token, err = auth.LoadOAuthToken(ctx) + if err != nil { + fmt.Println("Not logged in") + return nil + } + } + + claims := jwt.MapClaims{} + _, err = jwt.ParseWithClaims(token.AccessToken, claims, func(token *jwt.Token) (interface{}, error) { + return []byte(""), nil + }) + + email, ok := claims["https://jetstack.io/claims/name"].(string) + if ok { + fmt.Println("Logged in as:", email) + } + + cnf, ok := config.FromContext(ctx) + if !ok || cnf.Organization == "" { + fmt.Println("You do not have an organization selected, select one using: \n\n\tjsctl config set organization [name]\n\n" + + "To view organizations you have access to, list them using: \n\n\tjsctl organizations list") + return nil + } + fmt.Println("Current Organization:", cnf.Organization) + + return nil + }), + } + + flags := cmd.PersistentFlags() + flags.StringVar( + &credentials, + "credentials", + os.Getenv("JSCTL_CREDENTIALS"), + "The location of a credentials file to use instead of the normal oauth login flow", + ) + + return cmd +} diff --git a/internal/command/auth/utils.go b/internal/command/auth/utils.go new file mode 100644 index 0000000..3773043 --- /dev/null +++ b/internal/command/auth/utils.go @@ -0,0 +1,59 @@ +package auth + +import ( + "context" + "errors" + "fmt" + + "github.com/toqueteos/webbrowser" + "golang.org/x/oauth2" + + "github.com/jetstack/jsctl/internal/auth" +) + +func loginWithOAuth(ctx context.Context, oAuthConfig *oauth2.Config, disconnected bool) (*oauth2.Token, error) { + url, state := auth.GetOAuthURLAndState(oAuthConfig) + + // disconnected can be set to true when the browser and terminal are not running + // on the same machine. + if disconnected { + fmt.Printf("Navigate to the URL below to login:\n%s\n", url) + token, err := auth.WaitForOAuthTokenCommandLine(ctx, oAuthConfig, state) + if err != nil { + return nil, fmt.Errorf("failed to obtain token: %w", err) + } + return token, nil + } + + fmt.Println("Opening browser to:", url) + + if err := webbrowser.Open(url); err != nil { + fmt.Printf("Navigate to the URL below to login:\n%s\n", url) + } else { + fmt.Println("You will be taken to your browser for authentication") + } + + token, err := auth.WaitForOAuthTokenCallback(ctx, oAuthConfig, state) + if err != nil { + return nil, err + } + + return token, nil +} + +func loginWithCredentials(ctx context.Context, oAuthConfig *oauth2.Config, location string) (*oauth2.Token, error) { + credentials, err := auth.LoadCredentials(location) + switch { + case errors.Is(err, auth.ErrNoCredentials): + return nil, fmt.Errorf("no service account was found at: %s", location) + case err != nil: + return nil, fmt.Errorf("failed to read service account key: %w", err) + } + + token, err := auth.GetOAuthTokenForCredentials(ctx, oAuthConfig, credentials) + if err != nil { + return nil, err + } + + return token, nil +} From 5dd25e99d8ae98468b2a3dedbedf8df3259ddabd Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 09:24:31 +0000 Subject: [PATCH 02/10] Implement command to output new cluster SA --- internal/cluster/cluster.go | 24 +++++++++ internal/command/auth.go | 1 + internal/command/auth/clusters.go | 89 +++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 internal/command/auth/clusters.go diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index 71f66f9..3a57ef0 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -15,6 +15,9 @@ import ( "text/template" "time" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/jetstack/jsctl/internal/client" ) @@ -158,3 +161,24 @@ func marshalBase64(in interface{}) ([]byte, error) { return buffer.Bytes(), nil } + +// AgentServiceAccount secret takes a service account json and formats it as a +// k8s secret. +func AgentServiceAccountSecret(keyData []byte) *corev1.Secret { + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "agent-credentials", + Namespace: "jetstack-secure", + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "credentials.json": keyData, + }, + } + + return secret +} diff --git a/internal/command/auth.go b/internal/command/auth.go index 5528b15..9f23a01 100644 --- a/internal/command/auth.go +++ b/internal/command/auth.go @@ -17,6 +17,7 @@ func Auth() *cobra.Command { auth.Login(run), auth.Logout(run), auth.Status(run), + auth.Clusters(run, apiURL), ) return cmd diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go new file mode 100644 index 0000000..85fa1f4 --- /dev/null +++ b/internal/command/auth/clusters.go @@ -0,0 +1,89 @@ +package auth + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" + "sigs.k8s.io/yaml" + + "github.com/jetstack/jsctl/internal/client" + "github.com/jetstack/jsctl/internal/cluster" + "github.com/jetstack/jsctl/internal/command/types" + "github.com/jetstack/jsctl/internal/config" +) + +func Clusters(run types.RunFunc, apiURL string) *cobra.Command { + cmd := &cobra.Command{ + Use: "clusters", + Args: cobra.ExactArgs(0), + } + + cmd.AddCommand(createServiceAccount(run, apiURL)) + + return cmd +} + +func createServiceAccount(run types.RunFunc, apiURL string) *cobra.Command { + var serviceAccountFormat string + + cmd := &cobra.Command{ + Use: "create-service-account [name]", + Short: "Create a new service account identity for a cluster", + Args: cobra.MatchAll(cobra.ExactArgs(1)), + Long: `jsctl can do this automatically for you, in jsctl clusters connect. However, +sometimes it's helpful to get a new standalone service account JSON. +`, + Run: run(func(ctx context.Context, args []string) error { + name := args[0] + if name == "" { + return errors.New("you must specify a cluster name") + } + + cnf, ok := config.FromContext(ctx) + if !ok || cnf.Organization == "" { + return fmt.Errorf("organization must be set using jsctl config set organization [org]") + } + + http := client.New(ctx, apiURL) + serviceAccount, err := cluster.CreateServiceAccount(ctx, http, cnf.Organization, name) + if err != nil { + return fmt.Errorf("failed to create service account: %w", err) + } + + serviceAccountBytes, err := json.MarshalIndent(serviceAccount, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal service account for output: %w", err) + } + + switch serviceAccountFormat { + case "json": + fmt.Println(strings.TrimSpace(string(serviceAccountBytes))) + case "secret": + secret := cluster.AgentServiceAccountSecret(serviceAccountBytes) + secretYAMLBytes, err := yaml.Marshal(secret) + if err != nil { + return fmt.Errorf("failed to marshal image pull secret: %s", err) + } + + fmt.Println(strings.TrimSpace(string(secretYAMLBytes))) + default: + return fmt.Errorf("unknown service account format: %s", serviceAccountFormat) + } + return nil + }), + } + + flags := cmd.PersistentFlags() + flags.StringVar( + &serviceAccountFormat, + "format", + "json", + "The desired output format, valid options: [json, secret]", + ) + + return cmd +} From c713a628ab3b5ecacecc2f9416fa8691674e8c75 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 15:16:28 +0000 Subject: [PATCH 03/10] Add name & namespace flags --- internal/cluster/cluster.go | 6 +++--- internal/command/auth/clusters.go | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index 3a57ef0..2d4fc53 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -164,15 +164,15 @@ func marshalBase64(in interface{}) ([]byte, error) { // AgentServiceAccount secret takes a service account json and formats it as a // k8s secret. -func AgentServiceAccountSecret(keyData []byte) *corev1.Secret { +func AgentServiceAccountSecret(keyData []byte, name, namespace string) *corev1.Secret { secret := &corev1.Secret{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), Kind: "Secret", }, ObjectMeta: metav1.ObjectMeta{ - Name: "agent-credentials", - Namespace: "jetstack-secure", + Name: name, + Namespace: namespace, }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 85fa1f4..0bb4cb0 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -29,6 +29,7 @@ func Clusters(run types.RunFunc, apiURL string) *cobra.Command { func createServiceAccount(run types.RunFunc, apiURL string) *cobra.Command { var serviceAccountFormat string + var secretName, secretNamespace string cmd := &cobra.Command{ Use: "create-service-account [name]", @@ -63,7 +64,7 @@ sometimes it's helpful to get a new standalone service account JSON. case "json": fmt.Println(strings.TrimSpace(string(serviceAccountBytes))) case "secret": - secret := cluster.AgentServiceAccountSecret(serviceAccountBytes) + secret := cluster.AgentServiceAccountSecret(serviceAccountBytes, secretName, secretNamespace) secretYAMLBytes, err := yaml.Marshal(secret) if err != nil { return fmt.Errorf("failed to marshal image pull secret: %s", err) @@ -84,6 +85,18 @@ sometimes it's helpful to get a new standalone service account JSON. "json", "The desired output format, valid options: [json, secret]", ) + flags.StringVar( + &secretName, + "secret-name", + "agent-credentials", + "If using the 'secret' format, the name of the secret to create", + ) + flags.StringVar( + &secretNamespace, + "secret-namespace", + "jetstack-secure", + "If using the 'secret' format, the namespace of the secret to create", + ) return cmd } From 7e89834d91a599942a126e9cac17d58b24d152a4 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 15:18:57 +0000 Subject: [PATCH 04/10] Update command description --- internal/command/auth/clusters.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 0bb4cb0..2ff778a 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -35,8 +35,9 @@ func createServiceAccount(run types.RunFunc, apiURL string) *cobra.Command { Use: "create-service-account [name]", Short: "Create a new service account identity for a cluster", Args: cobra.MatchAll(cobra.ExactArgs(1)), - Long: `jsctl can do this automatically for you, in jsctl clusters connect. However, -sometimes it's helpful to get a new standalone service account JSON. + Long: `Generate a new service account for a Jetstack Secure cluster agent +This is only needed if you are not deploying the agent with jsctl. +Output can be json formatted or as Kubernetes Secret. `, Run: run(func(ctx context.Context, args []string) error { name := args[0] From 87fbc369379cf0576e1d8fe7ae7c64f70c867f31 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 15:20:18 +0000 Subject: [PATCH 05/10] Update failed marshal error message --- internal/command/auth/clusters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 2ff778a..9ecc981 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -68,7 +68,7 @@ Output can be json formatted or as Kubernetes Secret. secret := cluster.AgentServiceAccountSecret(serviceAccountBytes, secretName, secretNamespace) secretYAMLBytes, err := yaml.Marshal(secret) if err != nil { - return fmt.Errorf("failed to marshal image pull secret: %s", err) + return fmt.Errorf("failed to marshal agent service account secret: %s", err) } fmt.Println(strings.TrimSpace(string(secretYAMLBytes))) From 23547603d560e548812b463d91075ed48a3ca0d2 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 15:21:12 +0000 Subject: [PATCH 06/10] Update command short text --- internal/command/auth/clusters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 9ecc981..436c80d 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -33,7 +33,7 @@ func createServiceAccount(run types.RunFunc, apiURL string) *cobra.Command { cmd := &cobra.Command{ Use: "create-service-account [name]", - Short: "Create a new service account identity for a cluster", + Short: "Create a new Jetstack Secure service account for a cluster agent", Args: cobra.MatchAll(cobra.ExactArgs(1)), Long: `Generate a new service account for a Jetstack Secure cluster agent This is only needed if you are not deploying the agent with jsctl. From 13111fc96e174bb4b38919ffd045278226d32873 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 15:22:32 +0000 Subject: [PATCH 07/10] Update json format flag name --- internal/command/auth/clusters.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 436c80d..5d2e211 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -62,7 +62,7 @@ Output can be json formatted or as Kubernetes Secret. } switch serviceAccountFormat { - case "json": + case "jsonKeyData": fmt.Println(strings.TrimSpace(string(serviceAccountBytes))) case "secret": secret := cluster.AgentServiceAccountSecret(serviceAccountBytes, secretName, secretNamespace) @@ -83,8 +83,8 @@ Output can be json formatted or as Kubernetes Secret. flags.StringVar( &serviceAccountFormat, "format", - "json", - "The desired output format, valid options: [json, secret]", + "jsonKeyData", + "The desired output format, valid options: [jsonKeyData, secret]", ) flags.StringVar( &secretName, From b68e162c9c84b2a8da750e95caf83ac82e5491d1 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 16:31:47 +0000 Subject: [PATCH 08/10] Make name required arg --- internal/command/auth/clusters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/auth/clusters.go b/internal/command/auth/clusters.go index 5d2e211..89fb117 100644 --- a/internal/command/auth/clusters.go +++ b/internal/command/auth/clusters.go @@ -32,7 +32,7 @@ func createServiceAccount(run types.RunFunc, apiURL string) *cobra.Command { var secretName, secretNamespace string cmd := &cobra.Command{ - Use: "create-service-account [name]", + Use: "create-service-account name", Short: "Create a new Jetstack Secure service account for a cluster agent", Args: cobra.MatchAll(cobra.ExactArgs(1)), Long: `Generate a new service account for a Jetstack Secure cluster agent From 5c51f18e908f6ad12c4b86f9b73623a7828f6b7d Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 16:36:20 +0000 Subject: [PATCH 09/10] Reformat other arg command usages --- internal/command/clusters/connect.go | 4 ++-- internal/command/clusters/delete.go | 4 ++-- internal/command/clusters/list.go | 2 +- internal/command/clusters/status.go | 2 +- internal/command/clusters/view.go | 4 ++-- internal/command/config.go | 6 +++--- internal/command/users.go | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/command/clusters/connect.go b/internal/command/clusters/connect.go index 1aef1c6..1af0938 100644 --- a/internal/command/clusters/connect.go +++ b/internal/command/clusters/connect.go @@ -22,9 +22,9 @@ func Connect(run types.RunFunc, kubeConfigPath, apiURL *string, useStdout *bool) var registry string cmd := &cobra.Command{ - Use: "connect [name]", + Use: "connect name", Short: "Creates a new cluster in the control plane and deploys the agent in your current kubenetes context", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { name := args[0] if name == "" { diff --git a/internal/command/clusters/delete.go b/internal/command/clusters/delete.go index 8e98155..80e298f 100644 --- a/internal/command/clusters/delete.go +++ b/internal/command/clusters/delete.go @@ -21,9 +21,9 @@ func Delete(run types.RunFunc, apiURL *string) *cobra.Command { var force bool cmd := &cobra.Command{ - Use: "delete [name]", + Use: "delete name", Short: "Deletes a cluster from the organization", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { cnf, ok := config.FromContext(ctx) if !ok || cnf.Organization == "" { diff --git a/internal/command/clusters/list.go b/internal/command/clusters/list.go index 2b00f73..fec7b4b 100644 --- a/internal/command/clusters/list.go +++ b/internal/command/clusters/list.go @@ -23,7 +23,7 @@ func List(run types.RunFunc, apiURL *string) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "Lists all clusters connected to the control plane for the organization", - Args: cobra.ExactValidArgs(0), + Args: cobra.MatchAll(cobra.ExactArgs(0)), Run: run(func(ctx context.Context, args []string) error { cnf, ok := config.FromContext(ctx) if !ok || cnf.Organization == "" { diff --git a/internal/command/clusters/status.go b/internal/command/clusters/status.go index 842d037..5cf3729 100644 --- a/internal/command/clusters/status.go +++ b/internal/command/clusters/status.go @@ -19,7 +19,7 @@ func Status(run types.RunFunc, kubeConfigPath *string) *cobra.Command { Use: "status", Short: "Prints information about the state in the currently configured cluster in kubeconfig", Long: "The information printed by this command can be used to determine the state of a cluster prior to installing Jetstack Secure.", - Args: cobra.ExactValidArgs(0), + Args: cobra.MatchAll(cobra.ExactArgs(0)), Run: run(func(ctx context.Context, args []string) error { kubeCfg, err := kubernetes.NewConfig(*kubeConfigPath) if err != nil { diff --git a/internal/command/clusters/view.go b/internal/command/clusters/view.go index f061bb7..da90282 100644 --- a/internal/command/clusters/view.go +++ b/internal/command/clusters/view.go @@ -18,9 +18,9 @@ import ( // View returns a new cobra.Command for viewing a cluster in the JSCP api func View(run types.RunFunc, apiURL *string) *cobra.Command { return &cobra.Command{ - Use: "view [name]", + Use: "view name", Short: "Opens a browser window to the cluster's dashboard", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { cnf, ok := config.FromContext(ctx) if !ok || cnf.Organization == "" { diff --git a/internal/command/config.go b/internal/command/config.go index 08fd99f..e772e8b 100644 --- a/internal/command/config.go +++ b/internal/command/config.go @@ -48,7 +48,7 @@ func configShow() *cobra.Command { return &cobra.Command{ Use: "show", Short: "View your current configuration values", - Args: cobra.ExactValidArgs(0), + Args: cobra.MatchAll(cobra.ExactArgs(0)), Run: run(func(ctx context.Context, args []string) error { cnf, ok := config.FromContext(ctx) if !ok { @@ -76,9 +76,9 @@ func configShow() *cobra.Command { func configSetOrganization() *cobra.Command { return &cobra.Command{ - Use: "organization [value]", + Use: "organization name", Short: "Set your current organization", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { name := args[0] if name == "" { diff --git a/internal/command/users.go b/internal/command/users.go index 9437201..b615e9b 100644 --- a/internal/command/users.go +++ b/internal/command/users.go @@ -83,9 +83,9 @@ func usersAdd() *cobra.Command { var admin bool cmd := &cobra.Command{ - Use: "add [email]", + Use: "add email", Short: "Add a user to the current organization", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { http := client.New(ctx, apiURL) cnf, ok := config.FromContext(ctx) @@ -117,9 +117,9 @@ func usersRemove() *cobra.Command { var force bool cmd := &cobra.Command{ - Use: "remove [email]", + Use: "remove email", Short: "Remove a user from the current organization", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1)), Run: run(func(ctx context.Context, args []string) error { http := client.New(ctx, apiURL) cnf, ok := config.FromContext(ctx) From 94cab1e85693d2082341b95dbdfbb48dbe6dc5b3 Mon Sep 17 00:00:00 2001 From: Charlie Egan Date: Fri, 18 Nov 2022 16:39:22 +0000 Subject: [PATCH 10/10] Update docs --- docs/reference/jsctl_auth.md | 1 + docs/reference/jsctl_auth_clusters.md | 24 ++++++++++++ ...tl_auth_clusters_create-service-account.md | 37 +++++++++++++++++++ docs/reference/jsctl_clusters_connect.md | 2 +- docs/reference/jsctl_clusters_delete.md | 2 +- docs/reference/jsctl_clusters_view.md | 2 +- .../jsctl_configuration_set_organization.md | 2 +- docs/reference/jsctl_users_add.md | 2 +- docs/reference/jsctl_users_remove.md | 2 +- 9 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 docs/reference/jsctl_auth_clusters.md create mode 100644 docs/reference/jsctl_auth_clusters_create-service-account.md diff --git a/docs/reference/jsctl_auth.md b/docs/reference/jsctl_auth.md index 7db175c..9b7efc7 100644 --- a/docs/reference/jsctl_auth.md +++ b/docs/reference/jsctl_auth.md @@ -20,6 +20,7 @@ Subcommands for authentication ### SEE ALSO * [jsctl](jsctl.md) - Command-line tool for the Jetstack Secure Control Plane +* [jsctl auth clusters](jsctl_auth_clusters.md) - * [jsctl auth login](jsctl_auth_login.md) - Performs the authentication flow to allow access to other commands * [jsctl auth logout](jsctl_auth_logout.md) - * [jsctl auth status](jsctl_auth_status.md) - Print the logged in account and token location diff --git a/docs/reference/jsctl_auth_clusters.md b/docs/reference/jsctl_auth_clusters.md new file mode 100644 index 0000000..6559d3b --- /dev/null +++ b/docs/reference/jsctl_auth_clusters.md @@ -0,0 +1,24 @@ +## jsctl auth clusters + + + +### Options + +``` + -h, --help help for clusters +``` + +### Options inherited from parent commands + +``` + --api-url string Base URL of the control-plane API (default "https://platform.jetstack.io") + --config string Location of the user's jsctl config directory (default "HOME or USERPROFILE/.jsctl") + --kubeconfig string Location of the user's kubeconfig file for applying directly to the cluster (default "~/.kube/config") + --stdout If provided, manifests are written to stdout rather than applied to the current cluster +``` + +### SEE ALSO + +* [jsctl auth](jsctl_auth.md) - Subcommands for authentication +* [jsctl auth clusters create-service-account](jsctl_auth_clusters_create-service-account.md) - Create a new Jetstack Secure service account for a cluster agent + diff --git a/docs/reference/jsctl_auth_clusters_create-service-account.md b/docs/reference/jsctl_auth_clusters_create-service-account.md new file mode 100644 index 0000000..de950ce --- /dev/null +++ b/docs/reference/jsctl_auth_clusters_create-service-account.md @@ -0,0 +1,37 @@ +## jsctl auth clusters create-service-account + +Create a new Jetstack Secure service account for a cluster agent + +### Synopsis + +Generate a new service account for a Jetstack Secure cluster agent +This is only needed if you are not deploying the agent with jsctl. +Output can be json formatted or as Kubernetes Secret. + + +``` +jsctl auth clusters create-service-account name [flags] +``` + +### Options + +``` + --format string The desired output format, valid options: [jsonKeyData, secret] (default "jsonKeyData") + -h, --help help for create-service-account + --secret-name string If using the 'secret' format, the name of the secret to create (default "agent-credentials") + --secret-namespace string If using the 'secret' format, the namespace of the secret to create (default "jetstack-secure") +``` + +### Options inherited from parent commands + +``` + --api-url string Base URL of the control-plane API (default "https://platform.jetstack.io") + --config string Location of the user's jsctl config directory (default "HOME or USERPROFILE/.jsctl") + --kubeconfig string Location of the user's kubeconfig file for applying directly to the cluster (default "~/.kube/config") + --stdout If provided, manifests are written to stdout rather than applied to the current cluster +``` + +### SEE ALSO + +* [jsctl auth clusters](jsctl_auth_clusters.md) - + diff --git a/docs/reference/jsctl_clusters_connect.md b/docs/reference/jsctl_clusters_connect.md index 518ada6..bc7d453 100644 --- a/docs/reference/jsctl_clusters_connect.md +++ b/docs/reference/jsctl_clusters_connect.md @@ -3,7 +3,7 @@ Creates a new cluster in the control plane and deploys the agent in your current kubenetes context ``` -jsctl clusters connect [name] [flags] +jsctl clusters connect name [flags] ``` ### Options diff --git a/docs/reference/jsctl_clusters_delete.md b/docs/reference/jsctl_clusters_delete.md index badd829..cdc12c8 100644 --- a/docs/reference/jsctl_clusters_delete.md +++ b/docs/reference/jsctl_clusters_delete.md @@ -3,7 +3,7 @@ Deletes a cluster from the organization ``` -jsctl clusters delete [name] [flags] +jsctl clusters delete name [flags] ``` ### Options diff --git a/docs/reference/jsctl_clusters_view.md b/docs/reference/jsctl_clusters_view.md index 52b80a9..48e471c 100644 --- a/docs/reference/jsctl_clusters_view.md +++ b/docs/reference/jsctl_clusters_view.md @@ -3,7 +3,7 @@ Opens a browser window to the cluster's dashboard ``` -jsctl clusters view [name] [flags] +jsctl clusters view name [flags] ``` ### Options diff --git a/docs/reference/jsctl_configuration_set_organization.md b/docs/reference/jsctl_configuration_set_organization.md index b5f2cbc..32c155d 100644 --- a/docs/reference/jsctl_configuration_set_organization.md +++ b/docs/reference/jsctl_configuration_set_organization.md @@ -3,7 +3,7 @@ Set your current organization ``` -jsctl configuration set organization [value] [flags] +jsctl configuration set organization name [flags] ``` ### Options diff --git a/docs/reference/jsctl_users_add.md b/docs/reference/jsctl_users_add.md index f2e81ca..bfbc6d1 100644 --- a/docs/reference/jsctl_users_add.md +++ b/docs/reference/jsctl_users_add.md @@ -3,7 +3,7 @@ Add a user to the current organization ``` -jsctl users add [email] [flags] +jsctl users add email [flags] ``` ### Options diff --git a/docs/reference/jsctl_users_remove.md b/docs/reference/jsctl_users_remove.md index 3a0d5ee..882c19e 100644 --- a/docs/reference/jsctl_users_remove.md +++ b/docs/reference/jsctl_users_remove.md @@ -3,7 +3,7 @@ Remove a user from the current organization ``` -jsctl users remove [email] [flags] +jsctl users remove email [flags] ``` ### Options