Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: the db creds injected through secret manager issue in the load env #173

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions internal/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ func FileName() string {
}

func LoadEnv() error {
const (
localEnvFile = "local"
)
Comment on lines +61 to +63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor: Use a global constant for localEnvFile.

The constant localEnvFile is declared within the LoadEnv function, which limits its scope unnecessarily. Consider declaring it at the package level to enhance its reusability and maintainability.

- const (
-   localEnvFile = "local"
- )
+ const localEnvFile = "local"
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const (
localEnvFile = "local"
)
const localEnvFile = "local"

_, filename, _, ok := runtime.Caller(0)
if !ok {
return fmt.Errorf("Error getting current file path")
Expand All @@ -71,45 +74,60 @@ func LoadEnv() error {

envName := os.Getenv("ENVIRONMENT_NAME")
if envName == "" {
envName = "local"
envName = localEnvFile
}
log.Println("envName: " + envName)

envVarInjection := GetBool("ENV_INJECTION")
if !envVarInjection || envName == "local" {
if !envVarInjection || envName == localEnvFile {
err = godotenv.Load(fmt.Sprintf("%s.env.%s", prefix, envName))

if err != nil {
fmt.Printf(".env.%s\n", envName)
log.Println(err)
return err
return fmt.Errorf("couldn't load .env.%s file: %w", envName, err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error Handling: Improve error message clarity.

The error message when failing to load the environment file could be more descriptive by including which environment was attempted to be loaded.

- return fmt.Errorf("couldn't load .env.%s file: %w", envName, err)
+ return fmt.Errorf("Failed to load environment file for '%s': %w", envName, err)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !envVarInjection || envName == localEnvFile {
err = godotenv.Load(fmt.Sprintf("%s.env.%s", prefix, envName))
if err != nil {
fmt.Printf(".env.%s\n", envName)
log.Println(err)
return err
return fmt.Errorf("couldn't load .env.%s file: %w", envName, err)
if !envVarInjection || envName == localEnvFile {
err = godotenv.Load(fmt.Sprintf("%s.env.%s", prefix, envName))
if err != nil {
return fmt.Errorf("Failed to load environment file for '%s': %w", envName, err)

}
fmt.Println("loaded", fmt.Sprintf("%s.env.%s", prefix, envName))
return nil
}

dbCredsInjected := GetBool("COPILOT_DB_CREDS_VIA_SECRETS_MANAGER")

if dbCredsInjected {
type copilotSecrets struct {
Username string `json:"username"`
Host string `json:"host"`
DBName string `json:"dbname"`
Password string `json:"password"`
Port int `json:"port"`
}
secrets := &copilotSecrets{}
// except for local environment the db creds should be
// injected through the secret manager
if envName != localEnvFile && !dbCredsInjected {
return fmt.Errorf("db creds should be injected through secret manager")
}

err := json.Unmarshal([]byte(os.Getenv("DB_SECRET")), secrets)
if err != nil {
return err
}
// for local environment the db configs will be loaded through
// env itself by `godotenv`, even if db creds are not injected
if !dbCredsInjected {
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant Code: Simplify conditional branches.

The condition and return statement appear redundant as they are effectively a no-operation if the condition is met. Consider removing this block if it does not serve any additional purpose.

- if !dbCredsInjected {
-   return nil
- }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// for local environment the db configs will be loaded through
// env itself by `godotenv`, even if db creds are not injected
if !dbCredsInjected {
return nil
}
// for local environment the db configs will be loaded through
// env itself by `godotenv`, even if db creds are not injected


os.Setenv("PSQL_DBNAME", secrets.DBName)
os.Setenv("PSQL_HOST", secrets.Host)
os.Setenv("PSQL_PASS", secrets.Password)
os.Setenv("PSQL_PORT", strconv.Itoa(secrets.Port))
os.Setenv("PSQL_USER", secrets.Username)
// if dbCreds is injected then extract the db creds
return extractDBCredsFromSecret()
}

// extractDBCredsFromSecret helper function to extract db secret
func extractDBCredsFromSecret() error {
type copilotSecrets struct {
Username string `json:"username"`
Host string `json:"host"`
DBName string `json:"dbname"`
Password string `json:"password"`
Port int `json:"port"`
}
secrets := &copilotSecrets{}

err := json.Unmarshal([]byte(os.Getenv("DB_SECRET")), secrets)
if err != nil {
return fmt.Errorf("couldn't unmarshal db secret: %w", err)
}
return fmt.Errorf("COPILOT_DB_CREDS_VIA_SECRETS_MANAGER should have had a value")

os.Setenv("PSQL_DBNAME", secrets.DBName)
os.Setenv("PSQL_HOST", secrets.Host)
os.Setenv("PSQL_PASS", secrets.Password)
os.Setenv("PSQL_PORT", strconv.Itoa(secrets.Port))
os.Setenv("PSQL_USER", secrets.Username)

return nil
}
175 changes: 166 additions & 9 deletions internal/config/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
. "go-template/internal/config"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -41,6 +42,7 @@ func TestGetString(t *testing.T) {
if tt.success {
os.Setenv(tt.args.key, tt.want)
}
time.Sleep(time.Microsecond)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance Consideration: Question the necessity of time.Sleep.

The use of time.Sleep in a test function is generally a sign of an anti-pattern. Unless there's a compelling reason to simulate timing issues, consider removing it or using more deterministic approaches.

- time.Sleep(time.Microsecond)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
time.Sleep(time.Microsecond)


if got := GetString(tt.args.key); got != tt.want {
t.Errorf("GetString() = %v, want %v", got, tt.want)
Expand Down Expand Up @@ -231,6 +233,7 @@ func loadLocalEnv() envTestCaseArgs {
},
}
}

func errorOnEnvInjectionAndCopilotFalse() envTestCaseArgs {
return envTestCaseArgs{
name: "Error when ENV_INJECTION and COPILOT_DB_CREDS_VIA_SECRETS_MANAGER false",
Expand All @@ -254,10 +257,67 @@ func errorOnEnvInjectionAndCopilotFalse() envTestCaseArgs {
}
}

func loadOnDbCredsInjected(username string, host string, dbname string, password string, port string) envTestCaseArgs {
func loadOnCopilotTrueAndLocalEnv() envTestCaseArgs {
return envTestCaseArgs{
name: "Load local without copilot",
wantErr: false,
args: args{
setEnv: []keyValueArgs{
{
key: "ENV_INJECTION",
value: "true",
},
{
key: "ENVIRONMENT_NAME",
value: "local",
},
{
key: "COPILOT_DB_CREDS_VIA_SECRETS_MANAGER",
value: "false",
},
},
},
}
}

func errorOnDbCredsInjectedInDevEnv() envTestCaseArgs {
return envTestCaseArgs{
name: "dbCredsInjected True",
name: "dbCredsInjected True for `develop` environment,with invalid json in db secret",
wantErr: true,
args: args{
setEnv: []keyValueArgs{
{
key: "ENV_INJECTION",
value: "true",
},
{
key: "ENVIRONMENT_NAME",
value: "develop",
},
{
key: "COPILOT_DB_CREDS_VIA_SECRETS_MANAGER",
value: "true",
},
{
key: "DB_SECRET",
value: "invalid json",
},
},
expectedKeyValues: []keyValueArgs{},
},
}
}

func loadOnDbCredsInjectedInDevEnv(
username string,
host string,
dbname string,
password string,
port string,
) envTestCaseArgs {
return envTestCaseArgs{
name: "dbCredsInjected True for `develop` environment,and should parse the db secret",
wantErr: false,
args: args{
setEnv: []keyValueArgs{
{
Expand All @@ -274,27 +334,103 @@ func loadOnDbCredsInjected(username string, host string, dbname string, password
},
{
key: "DB_SECRET",
value: fmt.Sprintf(`{"username": "%s", "password": "%s", "port": "%s", "dbname": "%s", "host": "%s"}`,
value: fmt.Sprintf(`{"username": "%s", "password": "%s", "port": %s, "dbname": "%s", "host": "%s"}`,
username,
password,
port,
host,
dbname),
dbname,
host),
},
},
expectedKeyValues: []keyValueArgs{
{
key: "PSQL_USER",
value: username,
},
{
key: "PSQL_PORT",
value: port,
},
{
key: "PSQL_PASS",
value: password,
},
{
key: "PSQL_HOST",
value: host,
},
{
key: "PSQL_DBNAME",
value: dbname,
},
},
},
}
}

func errorOnDbCredsInjectedInLocalEnv() envTestCaseArgs {
return envTestCaseArgs{
name: "dbCredsInjected True for `local` environment, with invalid json in db secret",
wantErr: true,
args: args{
setEnv: []keyValueArgs{
{
key: "ENV_INJECTION",
value: "true",
},
{
key: "PSQL_HOST",
value: host,
key: "ENVIRONMENT_NAME",
value: "develop",
},
{
key: "COPILOT_DB_CREDS_VIA_SECRETS_MANAGER",
value: "true",
},
{
key: "DB_SECRET",
value: `invalid json`,
},
},
expectedKeyValues: []keyValueArgs{},
},
}
}

func loadDbCredsInjectedInLocalEnv(
username string,
host string,
dbname string,
password string,
port string,
) envTestCaseArgs {
return envTestCaseArgs{
name: "dbCredsInjected True for local environment,and should parse the db secret",
wantErr: false,
args: args{
setEnv: []keyValueArgs{
{
key: "ENV_INJECTION",
value: "true",
},
{
key: "ENVIRONMENT_NAME",
value: "local",
},
{
key: "COPILOT_DB_CREDS_VIA_SECRETS_MANAGER",
value: "true",
},
{
key: "DB_SECRET",
value: fmt.Sprintf(`{"username": "%s", "password": "%s", "port": %s, "dbname": "%s", "host": "%s"}`,
username,
password,
port,
dbname,
host),
},
},
expectedKeyValues: []keyValueArgs{
{
key: "PSQL_USER",
value: username,
Expand All @@ -303,31 +439,52 @@ func loadOnDbCredsInjected(username string, host string, dbname string, password
key: "PSQL_PORT",
value: port,
},
{
key: "PSQL_PASS",
value: password,
},
{
key: "PSQL_HOST",
value: host,
},
{
key: "PSQL_DBNAME",
value: dbname,
},
},
},
}
}

func errorOnWrongEnvName() envTestCaseArgs {
return envTestCaseArgs{
name: "Failed to load env",
name: "Failed to load env for local1",
wantErr: true,
args: args{
setEnv: []keyValueArgs{
{
key: "ENVIRONMENT_NAME",
value: "local1",
},
{
key: "ENV_INJECTION",
value: "false",
},
},
},
}
}

func getTestCases(username string, host string, dbname string, password string, port string) []envTestCaseArgs {
return []envTestCaseArgs{
loadLocalEnvIfNoEnvName(),
loadLocalEnv(),
errorOnEnvInjectionAndCopilotFalse(),
loadOnDbCredsInjected(username, host, dbname, password, port),
loadOnCopilotTrueAndLocalEnv(),
errorOnDbCredsInjectedInDevEnv(),
loadOnDbCredsInjectedInDevEnv(username, host, dbname, password, port),
errorOnDbCredsInjectedInLocalEnv(),
loadDbCredsInjectedInLocalEnv(username, host, dbname, password, port),
errorOnWrongEnvName(),
}
}
Expand Down