From ede245b8de57877a159cca19361a74cd9f2012c5 Mon Sep 17 00:00:00 2001 From: Jeff Roche <82384784+jeff-roche@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:31:50 -0400 Subject: [PATCH] feat: switched to cobra for the cli parsing, added saving to a dotenv (#11) --- README.md | 23 ++++++++-- go.mod | 3 ++ go.sum | 9 ++++ main.go | 23 ++-------- src/cmd/root.go | 46 +++++++++++++++++++ src/cmd/run.go | 39 ++++++++++++++++ src/cmd/save.go | 43 +++++++++++++++++ src/lib/setters/basic_setter.go | 4 +- src/lib/setters/basic_setter_test.go | 3 +- src/lib/setters/cli_setter.go | 4 +- src/lib/setters/dragoman_setter.go | 8 ++-- src/lib/setters/secrets_manager_setter.go | 22 ++++----- .../setters/secrets_manager_setter_test.go | 21 ++++++--- src/lib/setters/setter_ifc.go | 3 +- src/services/biome_configuration_service.go | 12 ++++- .../biome_configuration_service_test.go | 26 ++++++++++- 16 files changed, 235 insertions(+), 54 deletions(-) create mode 100644 src/cmd/root.go create mode 100644 src/cmd/run.go create mode 100644 src/cmd/save.go diff --git a/README.md b/README.md index 3cba4bd..b482af1 100644 --- a/README.md +++ b/README.md @@ -63,21 +63,24 @@ The most common use case is for use with scripts that need context via environme ### Via direct command ```bash -$ biome -b my-biome ${COMMAND} +$ biome run -b my-biome ${COMMAND} ``` - `-b` is a required parameter that specifies the name of the biome you want to use - In this case, the name of the biome is `my-biome` - `${COMMAND}` specifies the command you want to run (ex: `env`, `ls -al`, ``, etc.) +> **NOTE** if you want to add command line flags to the command being run, you need to preface it with `--` + *Example*: `biome run -b my-biome -- ls -al` + ### Via bash alias A way that makes Biome a little more convenient is to alias your profiles via bash aliases and use them that way. This configuration: ```bash # ~/.bashrc -alias onstaging='biome -b staging-biome' -alias onprod='biome -b production-biome' +alias onstaging='biome run -b staging-biome' +alias onprod='biome run -b production-biome' ``` Allows the following command to be run on the command line: @@ -86,10 +89,20 @@ Allows the following command to be run on the command line: $ onstaging ./bin/ci/deploy-service.sh ``` +### Exporting to a dotenv file +You can export the loaded environment variables to a dotenv file with the following command: +```bash +$ biome save -b my-biome -f my.env +``` + +> **NOTE**: If no input file is specified with `-f`, `./.env` will be used as the file path + +> **NOTE**: AWS environment variables are not currently exported + ## Future Plans -- Export loaded variables to a dotenv file - Have goreleaser create a docker image and publish to ghcr -- Potentially switching to [cobra](https://github.com/spf13/cobra) for the cli +- :white_check_mark: Export loaded variables to a dotenv file +- :white_check_mark: Switch to [cobra](https://github.com/spf13/cobra) for the cli - :white_check_mark: Allow CLI input to be a secret (for passwords) - :white_check_mark: Allow inhereting from other biomes in the same file - :white_check_mark: Allow setting an environment variable from stdin diff --git a/go.mod b/go.mod index 83046d7..2c7ad36 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 github.com/joho/godotenv v1.4.0 github.com/meltwater/dragoman v1.2.2 + github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.7.1 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d gopkg.in/yaml.v3 v3.0.1 @@ -25,8 +26,10 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.11.7 // indirect github.com/aws/smithy-go v1.11.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kr/pretty v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.2.0 // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect diff --git a/go.sum b/go.sum index 3b4992f..5655315 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,7 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.16.6/go.mod h1:rP1rEOKAGZoXp4iGDxSXF github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -35,6 +36,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= @@ -48,6 +51,11 @@ github.com/meltwater/dragoman v1.2.2 h1:ZdpdB2vkm9grPjqvp9/6eZEmGubCQtVPuMUi4NDg github.com/meltwater/dragoman v1.2.2/go.mod h1:waYPsylnXTj4F7xblDEeDEKqxNPZjkqcLYd/6DaUoOM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -68,6 +76,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 04bfa27..d6e2eec 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,14 @@ package main import ( - "flag" - "fmt" - "log" - - "github.com/jeff-roche/biome/src/lib/cmdr" + "github.com/jeff-roche/biome/src/cmd" "github.com/jeff-roche/biome/src/services" ) var Version string func main() { - var versionFlag bool + /*var versionFlag bool flag.BoolVar(&versionFlag, "version", false, "Display the current version of this utility") flag.BoolVar(&versionFlag, "v", false, "Display the current version of this utility") @@ -36,21 +32,10 @@ func main() { if len(cmds) < 1 { log.Fatalln("No command provided") - } + }*/ // Setup the biome biomeSvc := services.NewBiomeConfigurationService() - if err := biomeSvc.LoadBiomeFromDefaults(*biomeName); err != nil { - log.Fatalln(err) - } - - if err := biomeSvc.ActivateBiome(); err != nil { - log.Fatalln(err) - } - - // Execute order 66 - if err := cmdr.Run(cmds[0], cmds[1:]...); err != nil { - log.Fatal(err) - } + cmd.Execute(biomeSvc) } diff --git a/src/cmd/root.go b/src/cmd/root.go new file mode 100644 index 0000000..da5e15b --- /dev/null +++ b/src/cmd/root.go @@ -0,0 +1,46 @@ +package cmd + +import ( + "os" + + "github.com/jeff-roche/biome/src/services" + "github.com/spf13/cobra" +) + +var biomeService *services.BiomeConfigurationService + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "biome", + Short: "An environment configuration tool", + Long: `An environment variable configuration tool with capabilities such as: + - CLI input + - Configuration commands + - AWS environment configuration + - AWS Secrets Manager support + - Environment variable decryption`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute(biomeSvc *services.BiomeConfigurationService) { + biomeService = biomeSvc + + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + // Cobra also supports local flags, which will only run + // when this action is called directly. + //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/src/cmd/run.go b/src/cmd/run.go new file mode 100644 index 0000000..c7ecbf1 --- /dev/null +++ b/src/cmd/run.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "log" + + "github.com/jeff-roche/biome/src/lib/cmdr" + "github.com/spf13/cobra" +) + +// runCmd represents the run command +var runCmd = &cobra.Command{ + Use: "run -b [flags] [...cmd]", + Short: "Run any cli command in the provided biome", + Long: "Run any cli command in the provided biome", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + biomeName, _ := cmd.Flags().GetString("biome") + + if err := biomeService.LoadBiomeFromDefaults(biomeName); err != nil { + log.Fatalln(err) + } + + if err := biomeService.ActivateBiome(); err != nil { + log.Fatalln(err) + } + + // Execute order 66 + if err := cmdr.Run(args[0], args[1:]...); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + rootCmd.AddCommand(runCmd) + + runCmd.Flags().StringP("biome", "b", "", "the name of the biome to configure") + runCmd.MarkFlagRequired("biome") +} diff --git a/src/cmd/save.go b/src/cmd/save.go new file mode 100644 index 0000000..55bfa39 --- /dev/null +++ b/src/cmd/save.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "fmt" + "log" + + "github.com/spf13/cobra" +) + +// saveCmd represents the save command +var saveCmd = &cobra.Command{ + Use: "save", + Short: "Save the loaded environment variables to a dotenv (.env) file", + Long: `Save the loaded environment variables to a dotenv (.env) file + The default file is '.env' in the current directory`, + Run: func(cmd *cobra.Command, args []string) { + biomeName, _ := cmd.Flags().GetString("biome") + fileName, _ := cmd.Flags().GetString("file") + fmt.Println("LLAMA") + fmt.Println(biomeName) + + if err := biomeService.LoadBiomeFromDefaults(biomeName); err != nil { + log.Fatalln(err) + } + + if err := biomeService.ActivateBiome(); err != nil { + log.Fatalln(err) + } + + // Execute order 66 + if err := biomeService.SaveBiomeToFile(fileName); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + rootCmd.AddCommand(saveCmd) + + saveCmd.Flags().StringP("file", "f", ".env", "set the output file name") + saveCmd.Flags().StringP("biome", "b", "", "the name of the biome to configure") + saveCmd.MarkFlagRequired("biome") +} diff --git a/src/lib/setters/basic_setter.go b/src/lib/setters/basic_setter.go index 2bb1b01..fd79a4f 100644 --- a/src/lib/setters/basic_setter.go +++ b/src/lib/setters/basic_setter.go @@ -17,6 +17,6 @@ func NewBasicEnvironmentSetter(key string, value interface{}) *BasicEnvironmentS } } -func (s BasicEnvironmentSetter) SetEnv() error { - return os.Setenv(s.Key, s.Value) +func (s BasicEnvironmentSetter) SetEnv() (string, error) { + return s.Value, os.Setenv(s.Key, s.Value) } diff --git a/src/lib/setters/basic_setter_test.go b/src/lib/setters/basic_setter_test.go index 86f6874..bbc9f16 100644 --- a/src/lib/setters/basic_setter_test.go +++ b/src/lib/setters/basic_setter_test.go @@ -31,9 +31,10 @@ func TestBasicSetter(t *testing.T) { t.Run("should set the env", func(t *testing.T) { testVal := "BAZ" s := NewBasicEnvironmentSetter(testEnvKey, testVal) - err := s.SetEnv() + val, err := s.SetEnv() assert.Nil(t, err) + assert.Equal(t, val, testVal) assert.Equal(t, os.Getenv(testEnvKey), testVal) }) diff --git a/src/lib/setters/cli_setter.go b/src/lib/setters/cli_setter.go index 8cc2976..84a1171 100644 --- a/src/lib/setters/cli_setter.go +++ b/src/lib/setters/cli_setter.go @@ -42,8 +42,8 @@ func NewCLIEnvironmentSetter(key string, rd io.Reader, isSecret bool) (*CLIEnvir }, nil } -func (s CLIEnvironmentSetter) SetEnv() error { - return os.Setenv(s.Key, s.Value) +func (s CLIEnvironmentSetter) SetEnv() (string, error) { + return s.Value, os.Setenv(s.Key, s.Value) } func getCliInput(rd io.Reader) (string, error) { diff --git a/src/lib/setters/dragoman_setter.go b/src/lib/setters/dragoman_setter.go index 9b1a770..aecb4d4 100644 --- a/src/lib/setters/dragoman_setter.go +++ b/src/lib/setters/dragoman_setter.go @@ -30,15 +30,15 @@ func NewDragomanEnvironmentSetter(key string, val string) (*DragomanEnvironmentS }, nil } -func (s DragomanEnvironmentSetter) SetEnv() error { +func (s DragomanEnvironmentSetter) SetEnv() (string, error) { dec, err := s.repo.Decrypt(s.Encrypted) if err != nil { - return err + return "", err } if s.EnvKey == "" { - return fmt.Errorf("no environment key specified") + return "", fmt.Errorf("no environment key specified") } - return os.Setenv(s.EnvKey, dec) + return dec, os.Setenv(s.EnvKey, dec) } diff --git a/src/lib/setters/secrets_manager_setter.go b/src/lib/setters/secrets_manager_setter.go index e421e6a..dad8e73 100644 --- a/src/lib/setters/secrets_manager_setter.go +++ b/src/lib/setters/secrets_manager_setter.go @@ -47,10 +47,10 @@ func NewSecretsManagerEnvironmentSetter(key string, subkeys map[string]interface return setter, nil } -func (s SecretsManagerEnvironmentSetter) SetEnv() error { +func (s SecretsManagerEnvironmentSetter) SetEnv() (string, error) { // Get the value from Secrets Manager if s.ARN == "" { - return NewSecretsManagerEnvironmentSetterError( + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("no Secrets Manager ARN specified. Please use '%s' to specify one", SECRETS_MANAGER_ENV_ARN_KEY), ) @@ -58,7 +58,7 @@ func (s SecretsManagerEnvironmentSetter) SetEnv() error { secret, err := s.repo.GetSecretString(s.ARN) if err != nil { - return NewSecretsManagerEnvironmentSetterError( + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("unable to load secret '%s': %v", s.ARN, err), ) @@ -66,7 +66,7 @@ func (s SecretsManagerEnvironmentSetter) SetEnv() error { // Validate the output if secret == "" { - return NewSecretsManagerEnvironmentSetterError( + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("only string based secrets are currently supported"), ) @@ -74,7 +74,7 @@ func (s SecretsManagerEnvironmentSetter) SetEnv() error { // Get value from the JSON key if s.SecretKey == "" { - return NewSecretsManagerEnvironmentSetterError( + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("no JSON key for the secret was specified. Please use '%s' to specify one", SECRETS_MANAGER_ENV_JSON_KEY), ) @@ -82,23 +82,23 @@ func (s SecretsManagerEnvironmentSetter) SetEnv() error { var jsonData map[string]string if err := json.Unmarshal([]byte(secret), &jsonData); err != nil { - return NewSecretsManagerEnvironmentSetterError( + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("unable to parse secret '%s' JSON: %v", s.ARN, err), ) } // Check and set the key - if val, exists := jsonData[s.SecretKey]; exists { - os.Setenv(s.EnvKey, val) - } else { - return NewSecretsManagerEnvironmentSetterError( + val, exists := jsonData[s.SecretKey] + + if !exists { + return "", NewSecretsManagerEnvironmentSetterError( s.EnvKey, fmt.Sprintf("secret '%s' does not contain JSON key '%s'", s.ARN, s.SecretKey), ) } - return nil + return val, os.Setenv(s.EnvKey, val) } type SecretsManagerEnvironmentSetterError struct { diff --git a/src/lib/setters/secrets_manager_setter_test.go b/src/lib/setters/secrets_manager_setter_test.go index 5edb146..2bf6ed6 100644 --- a/src/lib/setters/secrets_manager_setter_test.go +++ b/src/lib/setters/secrets_manager_setter_test.go @@ -87,10 +87,11 @@ func TestSecretsManagerSetter(t *testing.T) { }) // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.Nil(t, err) + assert.Equal(t, testJSONValue, val) assert.Equal(t, testJSONValue, os.Getenv(testEnv)) }) @@ -100,10 +101,11 @@ func TestSecretsManagerSetter(t *testing.T) { setter.ARN = "" // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.NotNil(t, err) + assert.Equal(t, val, "") assert.ErrorContains(t, err, "ARN") }) @@ -115,9 +117,10 @@ func TestSecretsManagerSetter(t *testing.T) { setter := getTestSetter(mockRepo) // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert + assert.Equal(t, val, "") assert.ErrorContains(t, err, customErr) }) @@ -128,10 +131,11 @@ func TestSecretsManagerSetter(t *testing.T) { setter := getTestSetter(mockRepo) // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.NotNil(t, err) + assert.Equal(t, val, "") }) t.Run("should report an error if no JSON key is provided", func(t *testing.T) { @@ -142,10 +146,11 @@ func TestSecretsManagerSetter(t *testing.T) { setter.SecretKey = "" // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.NotNil(t, err) + assert.Equal(t, val, "") assert.ErrorContains(t, err, "JSON") }) @@ -156,10 +161,11 @@ func TestSecretsManagerSetter(t *testing.T) { setter := getTestSetter(mockRepo) // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.NotNil(t, err) + assert.Equal(t, val, "") assert.ErrorContains(t, err, "unable to parse secret") }) @@ -171,10 +177,11 @@ func TestSecretsManagerSetter(t *testing.T) { setter.SecretKey = "invalidKey" // Act - err := setter.SetEnv() + val, err := setter.SetEnv() // Assert assert.NotNil(t, err) + assert.Equal(t, val, "") assert.ErrorContains(t, err, "does not contain JSON key") }) } diff --git a/src/lib/setters/setter_ifc.go b/src/lib/setters/setter_ifc.go index f9cb0e5..2d95832 100644 --- a/src/lib/setters/setter_ifc.go +++ b/src/lib/setters/setter_ifc.go @@ -2,5 +2,6 @@ package setters // EnvironmentSetter is the minimum contract needed to set an environment variable of any kind type EnvironmentSetter interface { - SetEnv() error + // Returns the value being set and an error if one was encountered + SetEnv() (string, error) } diff --git a/src/services/biome_configuration_service.go b/src/services/biome_configuration_service.go index 1bdf350..def3d45 100644 --- a/src/services/biome_configuration_service.go +++ b/src/services/biome_configuration_service.go @@ -20,6 +20,7 @@ type BiomeConfigurationService struct { ActiveBiome *types.BiomeConfig configFileRepo repos.BiomeFileParserIfc awsStsRepo repos.AwsStsRepositoryIfc + configuredEnvs map[string]string } // NewBiomeConfigurationService is a builder function to generate the service @@ -27,6 +28,7 @@ func NewBiomeConfigurationService() *BiomeConfigurationService { return &BiomeConfigurationService{ configFileRepo: repos.NewBiomeFileParser(), awsStsRepo: repos.NewAwsStsRepository(), + configuredEnvs: make(map[string]string), } } @@ -73,6 +75,11 @@ func (svc *BiomeConfigurationService) LoadBiomeFromFile(biomeName string, fpath return nil } +// SaveBiomeToFile will export the loaded environment variables to the file specified +func (svc BiomeConfigurationService) SaveBiomeToFile(fpath string) error { + return godotenv.Write(svc.configuredEnvs, fpath) +} + // Activate biome will load up the configuration and run any setup commands before running the specified program func (svc *BiomeConfigurationService) ActivateBiome() error { if svc.ActiveBiome == nil { @@ -146,10 +153,13 @@ func (svc *BiomeConfigurationService) loadEnvs() error { return fmt.Errorf("error setting '%s': %v", env, err) } - err = setter.SetEnv() + raw_val, err := setter.SetEnv() if err != nil { return fmt.Errorf("error setting '%s': %v", env, err) } + + // Save off the envs we configured + svc.configuredEnvs[env] = raw_val } return nil diff --git a/src/services/biome_configuration_service_test.go b/src/services/biome_configuration_service_test.go index 3ea9215..7fc88ee 100644 --- a/src/services/biome_configuration_service_test.go +++ b/src/services/biome_configuration_service_test.go @@ -114,7 +114,8 @@ func TestBiomeConfigurationService(t *testing.T) { b := getTestBiome() testSvc := &BiomeConfigurationService{ - ActiveBiome: &b, + ActiveBiome: &b, + configuredEnvs: map[string]string{}, } b.AwsProfile = "" @@ -131,6 +132,29 @@ func TestBiomeConfigurationService(t *testing.T) { assert.Equal(t, b.Environment[testEnv], os.Getenv(testEnv)) }) + t.Run("should save the environment variables to the configuredEnvs map", func(t *testing.T) { + // Assemble + b := getTestBiome() + + testSvc := &BiomeConfigurationService{ + ActiveBiome: &b, + configuredEnvs: map[string]string{}, + } + + b.AwsProfile = "" + + t.Cleanup(func() { + os.Unsetenv(testEnv) + }) + + // Act + err := testSvc.ActivateBiome() + + // Assert + assert.Nil(t, err) + assert.Equal(t, b.Environment[testEnv], testSvc.configuredEnvs[testEnv]) + }) + t.Run("should load the AWS environment", func(t *testing.T) { // Assemble b := getTestBiome()