Add help/usage units for CLI exchange-credential
subcommand
This commit is contained in:
parent
831df90c93
commit
4ced58b5b7
@ -23,19 +23,39 @@ import (
|
|||||||
|
|
||||||
//nolint: gochecknoinits
|
//nolint: gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
exchangeCredentialCmd := &cobra.Command{
|
rootCmd.AddCommand(newExchangeCredentialCmd(os.Args, os.Stdout, os.Stderr).cmd)
|
||||||
Run: runExchangeCredential,
|
}
|
||||||
|
|
||||||
|
type exchangeCredentialCommand struct {
|
||||||
|
// runFunc is called by the cobra.Command.Run hook. It is included here for
|
||||||
|
// testability.
|
||||||
|
runFunc func(stdout, stderr io.Writer)
|
||||||
|
|
||||||
|
// cmd is the cobra.Command for this CLI command. It is included here for
|
||||||
|
// testability.
|
||||||
|
cmd *cobra.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExchangeCredentialCmd(args []string, stdout, stderr io.Writer) *exchangeCredentialCommand {
|
||||||
|
c := &exchangeCredentialCommand{
|
||||||
|
runFunc: runExchangeCredential,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cmd = &cobra.Command{
|
||||||
|
Run: func(cmd *cobra.Command, _ []string) {
|
||||||
|
c.runFunc(stdout, stderr)
|
||||||
|
},
|
||||||
Args: cobra.NoArgs, // do not accept positional arguments for this command
|
Args: cobra.NoArgs, // do not accept positional arguments for this command
|
||||||
Use: "exchange-credential",
|
Use: "exchange-credential",
|
||||||
Short: "Exchange a credential for a cluster-specific access credential",
|
Short: "Exchange a credential for a cluster-specific access credential",
|
||||||
Long: here.Doc(`
|
Long: here.Doc(`
|
||||||
Exchange a credential which proves your identity for a time-limited,
|
Exchange a credential which proves your identity for a time-limited,
|
||||||
cluster-specific access credential.
|
cluster-specific access credential.
|
||||||
|
|
||||||
Designed to be conveniently used as an credential plugin for kubectl.
|
Designed to be conveniently used as an credential plugin for kubectl.
|
||||||
See the help message for 'pinniped get-kubeconfig' for more
|
See the help message for 'pinniped get-kubeconfig' for more
|
||||||
information about setting up a kubeconfig file using Pinniped.
|
information about setting up a kubeconfig file using Pinniped.
|
||||||
|
|
||||||
Requires all of the following environment variables, which are
|
Requires all of the following environment variables, which are
|
||||||
typically set in the kubeconfig:
|
typically set in the kubeconfig:
|
||||||
- PINNIPED_TOKEN: the token to send to Pinniped for exchange
|
- PINNIPED_TOKEN: the token to send to Pinniped for exchange
|
||||||
@ -43,13 +63,17 @@ func init() {
|
|||||||
Pinniped's HTTPS endpoint
|
Pinniped's HTTPS endpoint
|
||||||
- PINNIPED_K8S_API_ENDPOINT: the URL for the Pinniped credential
|
- PINNIPED_K8S_API_ENDPOINT: the URL for the Pinniped credential
|
||||||
exchange API
|
exchange API
|
||||||
|
|
||||||
For more information about credential plugins in general, see
|
For more information about credential plugins in general, see
|
||||||
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
|
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
|
||||||
`),
|
`),
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.AddCommand(exchangeCredentialCmd)
|
c.cmd.SetArgs(args)
|
||||||
|
c.cmd.SetOut(stdout)
|
||||||
|
c.cmd.SetErr(stderr)
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
type envGetter func(string) (string, bool)
|
type envGetter func(string) (string, bool)
|
||||||
@ -57,8 +81,8 @@ type tokenExchanger func(ctx context.Context, token, caBundle, apiEndpoint strin
|
|||||||
|
|
||||||
const ErrMissingEnvVar = constable.Error("failed to get credential: environment variable not set")
|
const ErrMissingEnvVar = constable.Error("failed to get credential: environment variable not set")
|
||||||
|
|
||||||
func runExchangeCredential(_ *cobra.Command, _ []string) {
|
func runExchangeCredential(stdout, _ io.Writer) {
|
||||||
err := exchangeCredential(os.LookupEnv, client.ExchangeToken, os.Stdout, 30*time.Second)
|
err := exchangeCredential(os.LookupEnv, client.ExchangeToken, stdout, 30*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -18,9 +19,105 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
|
||||||
|
"github.com/suzerain-io/pinniped/internal/here"
|
||||||
"github.com/suzerain-io/pinniped/internal/testutil"
|
"github.com/suzerain-io/pinniped/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
knownGoodUsageForExchangeCredential = here.Doc(`
|
||||||
|
Usage:
|
||||||
|
exchange-credential [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for exchange-credential
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
knownGoodHelpForExchangeCredential = here.Doc(`
|
||||||
|
Exchange a credential which proves your identity for a time-limited,
|
||||||
|
cluster-specific access credential.
|
||||||
|
|
||||||
|
Designed to be conveniently used as an credential plugin for kubectl.
|
||||||
|
See the help message for 'pinniped get-kubeconfig' for more
|
||||||
|
information about setting up a kubeconfig file using Pinniped.
|
||||||
|
|
||||||
|
Requires all of the following environment variables, which are
|
||||||
|
typically set in the kubeconfig:
|
||||||
|
- PINNIPED_TOKEN: the token to send to Pinniped for exchange
|
||||||
|
- PINNIPED_CA_BUNDLE: the CA bundle to trust when calling
|
||||||
|
Pinniped's HTTPS endpoint
|
||||||
|
- PINNIPED_K8S_API_ENDPOINT: the URL for the Pinniped credential
|
||||||
|
exchange API
|
||||||
|
|
||||||
|
For more information about credential plugins in general, see
|
||||||
|
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
exchange-credential [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for exchange-credential
|
||||||
|
`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewCredentialExchangeCmd(t *testing.T) {
|
||||||
|
spec.Run(t, "newCredentialExchangeCmd", func(t *testing.T, when spec.G, it spec.S) {
|
||||||
|
var r *require.Assertions
|
||||||
|
var stdout, stderr *bytes.Buffer
|
||||||
|
|
||||||
|
it.Before(func() {
|
||||||
|
r = require.New(t)
|
||||||
|
|
||||||
|
stdout, stderr = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls runFunc and does not print usage or help when correct arguments and flags are used", func() {
|
||||||
|
c := newExchangeCredentialCmd([]string{}, stdout, stderr)
|
||||||
|
|
||||||
|
runFuncCalled := false
|
||||||
|
c.runFunc = func(out, err io.Writer) {
|
||||||
|
runFuncCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
r.NoError(c.cmd.Execute())
|
||||||
|
r.True(runFuncCalled)
|
||||||
|
r.Empty(stdout.String())
|
||||||
|
r.Empty(stderr.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails when args are passed", func() {
|
||||||
|
c := newExchangeCredentialCmd([]string{"some-arg"}, stdout, stderr)
|
||||||
|
|
||||||
|
runFuncCalled := false
|
||||||
|
c.runFunc = func(out, err io.Writer) {
|
||||||
|
runFuncCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage := `unknown command "some-arg" for "exchange-credential"`
|
||||||
|
r.EqualError(c.cmd.Execute(), errorMessage)
|
||||||
|
r.False(runFuncCalled)
|
||||||
|
|
||||||
|
output := "Error: " + errorMessage + "\n" + knownGoodUsageForExchangeCredential
|
||||||
|
r.Equal(output, stdout.String())
|
||||||
|
r.Empty(stderr.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
it("prints a nice help message", func() {
|
||||||
|
c := newExchangeCredentialCmd([]string{"--help"}, stdout, stderr)
|
||||||
|
|
||||||
|
runFuncCalled := false
|
||||||
|
c.runFunc = func(out, err io.Writer) {
|
||||||
|
runFuncCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
r.NoError(c.cmd.Execute())
|
||||||
|
r.False(runFuncCalled)
|
||||||
|
r.Equal(knownGoodHelpForExchangeCredential, stdout.String())
|
||||||
|
r.Empty(stderr.String())
|
||||||
|
})
|
||||||
|
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
||||||
|
}
|
||||||
|
|
||||||
func TestExchangeCredential(t *testing.T) {
|
func TestExchangeCredential(t *testing.T) {
|
||||||
spec.Run(t, "cmd.exchangeCredential", func(t *testing.T, when spec.G, it spec.S) {
|
spec.Run(t, "cmd.exchangeCredential", func(t *testing.T, when spec.G, it spec.S) {
|
||||||
var r *require.Assertions
|
var r *require.Assertions
|
||||||
|
@ -26,46 +26,47 @@ import (
|
|||||||
"github.com/suzerain-io/pinniped/internal/here"
|
"github.com/suzerain-io/pinniped/internal/here"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
knownGoodUsage = `
|
knownGoodUsageForGetKubeConfig = here.Doc(`
|
||||||
Usage:
|
Usage:
|
||||||
get-kubeconfig [flags]
|
get-kubeconfig [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for get-kubeconfig
|
-h, --help help for get-kubeconfig
|
||||||
--kubeconfig string Path to the kubeconfig file
|
--kubeconfig string Path to the kubeconfig file
|
||||||
--kubeconfig-context string Kubeconfig context override
|
--kubeconfig-context string Kubeconfig context override
|
||||||
--pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped")
|
--pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped")
|
||||||
--token string Credential to include in the resulting kubeconfig output (Required)
|
--token string Credential to include in the resulting kubeconfig output (Required)
|
||||||
|
|
||||||
`
|
`)
|
||||||
|
|
||||||
knownGoodHelp = `Print a kubeconfig for authenticating into a cluster via Pinniped.
|
knownGoodHelpForGetKubeConfig = here.Doc(`
|
||||||
|
Print a kubeconfig for authenticating into a cluster via Pinniped.
|
||||||
|
|
||||||
Requires admin-like access to the cluster using the current
|
Requires admin-like access to the cluster using the current
|
||||||
kubeconfig context in order to access Pinniped's metadata.
|
kubeconfig context in order to access Pinniped's metadata.
|
||||||
The current kubeconfig is found similar to how kubectl finds it:
|
The current kubeconfig is found similar to how kubectl finds it:
|
||||||
using the value of the --kubeconfig option, or if that is not
|
using the value of the --kubeconfig option, or if that is not
|
||||||
specified then from the value of the KUBECONFIG environment
|
specified then from the value of the KUBECONFIG environment
|
||||||
variable, or if that is not specified then it defaults to
|
variable, or if that is not specified then it defaults to
|
||||||
.kube/config in your home directory.
|
.kube/config in your home directory.
|
||||||
|
|
||||||
Prints a kubeconfig which is suitable to access the cluster using
|
Prints a kubeconfig which is suitable to access the cluster using
|
||||||
Pinniped as the authentication mechanism. This kubeconfig output
|
Pinniped as the authentication mechanism. This kubeconfig output
|
||||||
can be saved to a file and used with future kubectl commands, e.g.:
|
can be saved to a file and used with future kubectl commands, e.g.:
|
||||||
pinniped get-kubeconfig --token $MY_TOKEN > $HOME/mycluster-kubeconfig
|
pinniped get-kubeconfig --token $MY_TOKEN > $HOME/mycluster-kubeconfig
|
||||||
kubectl --kubeconfig $HOME/mycluster-kubeconfig get pods
|
kubectl --kubeconfig $HOME/mycluster-kubeconfig get pods
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
get-kubeconfig [flags]
|
get-kubeconfig [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for get-kubeconfig
|
-h, --help help for get-kubeconfig
|
||||||
--kubeconfig string Path to the kubeconfig file
|
--kubeconfig string Path to the kubeconfig file
|
||||||
--kubeconfig-context string Kubeconfig context override
|
--kubeconfig-context string Kubeconfig context override
|
||||||
--pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped")
|
--pinniped-namespace string Namespace in which Pinniped was installed (default "pinniped")
|
||||||
--token string Credential to include in the resulting kubeconfig output (Required)
|
--token string Credential to include in the resulting kubeconfig output (Required)
|
||||||
`
|
`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewGetKubeConfigCmd(t *testing.T) {
|
func TestNewGetKubeConfigCmd(t *testing.T) {
|
||||||
@ -126,7 +127,7 @@ func TestNewGetKubeConfigCmd(t *testing.T) {
|
|||||||
r.EqualError(c.cmd.Execute(), errorMessage)
|
r.EqualError(c.cmd.Execute(), errorMessage)
|
||||||
r.False(runFuncCalled)
|
r.False(runFuncCalled)
|
||||||
|
|
||||||
output := "Error: " + errorMessage + knownGoodUsage
|
output := "Error: " + errorMessage + "\n" + knownGoodUsageForGetKubeConfig
|
||||||
r.Equal(output, stdout.String())
|
r.Equal(output, stdout.String())
|
||||||
r.Empty(stderr.String())
|
r.Empty(stderr.String())
|
||||||
})
|
})
|
||||||
@ -174,7 +175,7 @@ func TestNewGetKubeConfigCmd(t *testing.T) {
|
|||||||
r.EqualError(c.cmd.Execute(), errorMessage)
|
r.EqualError(c.cmd.Execute(), errorMessage)
|
||||||
r.False(runFuncCalled)
|
r.False(runFuncCalled)
|
||||||
|
|
||||||
output := "Error: " + errorMessage + knownGoodUsage
|
output := "Error: " + errorMessage + "\n" + knownGoodUsageForGetKubeConfig
|
||||||
r.Equal(output, stdout.String())
|
r.Equal(output, stdout.String())
|
||||||
r.Empty(stderr.String())
|
r.Empty(stderr.String())
|
||||||
})
|
})
|
||||||
@ -195,7 +196,7 @@ func TestNewGetKubeConfigCmd(t *testing.T) {
|
|||||||
|
|
||||||
r.NoError(c.cmd.Execute())
|
r.NoError(c.cmd.Execute())
|
||||||
r.False(runFuncCalled)
|
r.False(runFuncCalled)
|
||||||
r.Equal(knownGoodHelp, stdout.String())
|
r.Equal(knownGoodHelpForGetKubeConfig, stdout.String())
|
||||||
r.Empty(stderr.String())
|
r.Empty(stderr.String())
|
||||||
})
|
})
|
||||||
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
||||||
|
Loading…
Reference in New Issue
Block a user