From 91e5c3d90c34779f94c1877931aa5cdd004d5222 Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 18 Aug 2022 13:09:25 -0600 Subject: [PATCH] add the `corso env` command for env var guidance (#591) --- src/cli/cli.go | 2 + src/cli/help/env.go | 100 +++++++++++++++++++++++++++++++++++++++ src/cli/help/env_test.go | 36 ++++++++++++++ src/cli/print/print.go | 6 +++ 4 files changed, 144 insertions(+) create mode 100644 src/cli/help/env.go create mode 100644 src/cli/help/env_test.go diff --git a/src/cli/cli.go b/src/cli/cli.go index 1e8c5ff9c..0d55c7738 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -8,6 +8,7 @@ import ( "github.com/alcionai/corso/cli/backup" "github.com/alcionai/corso/cli/config" + "github.com/alcionai/corso/cli/help" "github.com/alcionai/corso/cli/print" "github.com/alcionai/corso/cli/repo" "github.com/alcionai/corso/cli/restore" @@ -65,6 +66,7 @@ func BuildCommandTree(cmd *cobra.Command) { repo.AddCommands(cmd) backup.AddCommands(cmd) restore.AddCommands(cmd) + help.AddCommands(cmd) } // ------------------------------------------------------------------------------------------ diff --git a/src/cli/help/env.go b/src/cli/help/env.go new file mode 100644 index 000000000..fe23305ca --- /dev/null +++ b/src/cli/help/env.go @@ -0,0 +1,100 @@ +package help + +import ( + "github.com/spf13/cobra" + + . "github.com/alcionai/corso/cli/print" +) + +// AddCommands attaches all `corso env * *` commands to the parent. +func AddCommands(parent *cobra.Command) { + parent.AddCommand(envCmd()) +} + +// The env command: purely a help display. +// `corso env [--help]` +func envCmd() *cobra.Command { + envCmd := &cobra.Command{ + Use: "env", + Short: "env var guide", + Long: `A guide to using environment variables in Corso.`, + RunE: handleEnvCmd, + Args: cobra.NoArgs, + } + envCmd.SetHelpFunc(envGuide) + return envCmd +} + +// Handler for flat calls to `corso env`. +// Produces the same output as `corso env --help`. +func handleEnvCmd(cmd *cobra.Command, args []string) error { + return cmd.Help() +} + +type envVar struct { + category string + name string + description string +} + +// interface compliance check +var _ Printable = &envVar{} + +// no modifications needed, just passthrough. +func (ev envVar) MinimumPrintable() any { + return ev +} + +func (ev envVar) Headers() []string { + return []string{ev.category, " "} +} + +func (ev envVar) Values() []string { + return []string{ev.name, ev.description} +} + +// headers +const ( + corso = "Corso" + azure = "Azure AD App Credentials" + aws = "AWS Credentials" +) + +var ( + corsoEVs = []envVar{ + {corso, "CORSO_PASSWORD", "Passphrase to protect repository encryption material." + + "It is impossible to use the repository or recover any backups without this key."}, + } + azureEVs = []envVar{ + {azure, "CLIENT_ID", "Client ID for your Azure AD application used to access your M365 tenant."}, + {azure, "CLIENT_SECRET", "Azure secret for your Azure AD application used to access your M365 tenant."}, + } + awsEVs = []envVar{ + {aws, "AWS_ACCESS_KEY_ID", "AWS access key for an IAM user or role for accessing S3 bucket repository."}, + {aws, "AWS_SECRET_ACCESS_KEY", "Secret key associated with the access key."}, + {aws, "AWS_SESSION_TOKEN", "Session token required when using temporary credentials."}, + } +) + +func toPrintable(evs []envVar) []Printable { + ps := []Printable{} + for _, ev := range evs { + ps = append(ps, ev) + } + return ps +} + +// envGuide outputs a help menu for setting env vars in Corso. +func envGuide(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + Info(ctx, + "\n--- Environment Variable Guide ---\n", + "As a best practice, Corso retrieves credentials and sensitive information from environment variables.\n ", + "\n", + ) + Table(ctx, toPrintable(corsoEVs)) + Info(ctx, "\n") + Table(ctx, toPrintable(azureEVs)) + Info(ctx, "\n") + Table(ctx, toPrintable(awsEVs)) +} diff --git a/src/cli/help/env_test.go b/src/cli/help/env_test.go new file mode 100644 index 000000000..8b54c58fe --- /dev/null +++ b/src/cli/help/env_test.go @@ -0,0 +1,36 @@ +package help + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/internal/tester" +) + +type EnvSuite struct { + suite.Suite +} + +func TestEnvSuite(t *testing.T) { + suite.Run(t, new(EnvSuite)) +} + +func (suite *EnvSuite) TestAddEnvCommands() { + t := suite.T() + cmd := &cobra.Command{Use: "root"} + + AddCommands(cmd) + c := envCmd() + require.NotNil(t, c) + + cmds := cmd.Commands() + require.Len(t, cmds, 1) + + assert.Equal(t, "env", c.Use) + assert.Equal(t, envCmd().Short, c.Short) + tester.AreSameFunc(t, handleEnvCmd, c.RunE) +} diff --git a/src/cli/print/print.go b/src/cli/print/print.go index a6a64a9d1..a23fb8d35 100644 --- a/src/cli/print/print.go +++ b/src/cli/print/print.go @@ -155,6 +155,12 @@ func printAll(w io.Writer, ps []Printable) { // Tabular // ------------------------------------------------------------------------------------------ +// Table writes the printables in a tabular format. Takes headers from +// the 0th printable only. +func Table(ctx context.Context, ps []Printable) { + outputTable(getRootCmd(ctx).OutOrStdout(), ps) +} + // output to stdout the list of printable structs in a table func outputTable(w io.Writer, ps []Printable) { t := table.Table{