Skip to content

Commit

Permalink
make envprep context aware
Browse files Browse the repository at this point in the history
  • Loading branch information
adityathebe committed Oct 18, 2023
1 parent 4acd325 commit 0a6acab
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 61 deletions.
95 changes: 44 additions & 51 deletions models/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"bytes"
"context"
"fmt"
"math/rand"
"net/url"
"os"
"os/exec"
"path/filepath"
"regexp"
"time"

Expand Down Expand Up @@ -156,91 +158,82 @@ func (c Connection) AsGoGetterURL() (string, error) {
}

// AsEnv generates environment variables and a configuration file content based on the connection type.
func (c Connection) AsEnv() EnvPrep {
envPrep := EnvPrep{
Conn: c,
func (c Connection) AsEnv(ctx context.Context) EnvPrep {
var envPrep = EnvPrep{
Files: make(map[string]bytes.Buffer),
}

switch c.Type {
case ConnectionTypeAWS:
envPrep.Env = append(envPrep.Env, fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", c.Username))
envPrep.Env = append(envPrep.Env, fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", c.Password))

envPrep.File.WriteString("[default]\n")
envPrep.File.WriteString(fmt.Sprintf("aws_access_key_id = %s\n", c.Username))
envPrep.File.WriteString(fmt.Sprintf("aws_secret_access_key = %s\n", c.Password))
// credentialFilePath :="$HOME/.aws/credentials"
credentialFilePath := filepath.Join(".creds", "aws", fmt.Sprintf("cred-%d", rand.Intn(100000000)))

var credentialFile bytes.Buffer
credentialFile.WriteString("[default]\n")
credentialFile.WriteString(fmt.Sprintf("aws_access_key_id = %s\n", c.Username))
credentialFile.WriteString(fmt.Sprintf("aws_secret_access_key = %s\n", c.Password))

if v, ok := c.Properties["profile"]; ok {
envPrep.Env = append(envPrep.Env, fmt.Sprintf("AWS_DEFAULT_PROFILE=%s", v))
}

if v, ok := c.Properties["region"]; ok {
envPrep.Env = append(envPrep.Env, fmt.Sprintf("AWS_DEFAULT_REGION=%s", v))
envPrep.File.WriteString(fmt.Sprintf("region = %s\n", v))
credentialFile.WriteString(fmt.Sprintf("region = %s\n", v))
}

envPrep.Files[credentialFilePath] = credentialFile

case ConnectionTypeAzure:
// Do nothing
args := []string{"login", "--service-principal", "--username", c.Username, "--password", c.Password}
if v, ok := c.Properties["tenant"]; ok {
args = append(args, "--tenant")
args = append(args, v)
}

// login with service principal
envPrep.PreRuns = append(envPrep.PreRuns, exec.CommandContext(ctx, "az", args...))

case ConnectionTypeGCP:
envPrep.File.WriteString(c.Certificate)
var credentialFile bytes.Buffer
credentialFile.WriteString(c.Certificate)

// credentialFilePath := "$HOME/.config/gcloud/credentials"
credentialFilePath := filepath.Join(".creds", "gcp", fmt.Sprintf("cred-%d", rand.Intn(100000000)))

// to configure gcloud CLI to use the service account specified in GOOGLE_APPLICATION_CREDENTIALS,
// we need to explicitly activate it
envPrep.PreRuns = append(envPrep.PreRuns, exec.CommandContext(ctx, "gcloud", "auth", "activate-service-account", "--key-file", credentialFilePath))
envPrep.Files[credentialFilePath] = credentialFile
}

return envPrep
}

type EnvPrep struct {
Conn Connection

// Env is the connection credentials in environment variables
Env []string

PreRuns []*exec.Cmd

// File contains the content of the configuration file based on the connection
File bytes.Buffer
Files map[string]bytes.Buffer
}

func (c *EnvPrep) Apply(ctx context.Context, cmd *exec.Cmd, configAbsPath string) error {
switch c.Conn.Type {
case ConnectionTypeAWS:
if err := saveConfig(c.File.Bytes(), configAbsPath); err != nil {
return err
}

cmd.Env = append(cmd.Env, "AWS_EC2_METADATA_DISABLED=true") // https://github.com/aws/aws-cli/issues/5262#issuecomment-705832151
cmd.Env = append(cmd.Env, fmt.Sprintf("AWS_SHARED_CREDENTIALS_FILE=%s", configAbsPath))
if v, ok := c.Conn.Properties["region"]; ok {
cmd.Env = append(cmd.Env, fmt.Sprintf("AWS_DEFAULT_REGION=%s", v))
}

case ConnectionTypeGCP:
if err := saveConfig(c.File.Bytes(), configAbsPath); err != nil {
return err
}

// to configure gcloud CLI to use the service account specified in GOOGLE_APPLICATION_CREDENTIALS,
// we need to explicitly activate it
runCmd := exec.Command("gcloud", "auth", "activate-service-account", "--key-file", configAbsPath)
if err := runCmd.Run(); err != nil {
return fmt.Errorf("failed to activate GCP service account: %w", err)
}

cmd.Env = append(cmd.Env, fmt.Sprintf("GOOGLE_APPLICATION_CREDENTIALS=%s", configAbsPath))

case ConnectionTypeAzure:
args := []string{"login", "--service-principal", "--username", c.Conn.Username, "--password", c.Conn.Password}
if v, ok := c.Conn.Properties["tenant"]; ok {
args = append(args, "--tenant")
args = append(args, v)
}

// login with service principal
runCmd := exec.CommandContext(ctx, "az", args...)
if err := runCmd.Run(); err != nil {
return err
// Inject creates the config file & injects the necessary environment variable into the command
func (c *EnvPrep) Inject(ctx context.Context, cmd *exec.Cmd) ([]*exec.Cmd, error) {
for path, file := range c.Files {
if err := saveConfig(file.Bytes(), path); err != nil {
return nil, fmt.Errorf("error saving config to %s: %w", path, err)
}
}

return nil
cmd.Env = append(cmd.Env, c.Env...)

return c.PreRuns, nil
}

func saveConfig(content []byte, absPath string) error {
Expand Down
28 changes: 18 additions & 10 deletions models/connections_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package models

import (
"context"
"testing"
)

Expand Down Expand Up @@ -52,10 +53,10 @@ func Test_Connection_AsGoGetterURL(t *testing.T) {

func Test_Connection_AsEnv(t *testing.T) {
testCases := []struct {
name string
connection Connection
expectedEnv []string
expectedFile string
name string
connection Connection
expectedEnv []string
expectedFiles map[string]string
}{
{
name: "AWS Connection",
Expand All @@ -71,7 +72,9 @@ func Test_Connection_AsEnv(t *testing.T) {
"AWS_DEFAULT_PROFILE=awsprofile",
"AWS_DEFAULT_REGION=us-east-1",
},
expectedFile: "[default]\naws_access_key_id = awsuser\naws_secret_access_key = awssecret\nregion = us-east-1\n",
expectedFiles: map[string]string{
"$HOME/.aws/credentials": "[default]\naws_access_key_id = awsuser\naws_secret_access_key = awssecret\nregion = us-east-1\n",
},
},
{
name: "GCP Connection",
Expand All @@ -80,23 +83,28 @@ func Test_Connection_AsEnv(t *testing.T) {
Username: "gcpuser",
Certificate: `{"account": "gcpuser"}`,
},
expectedEnv: []string{},
expectedFile: `{"account": "gcpuser"}`,
expectedEnv: []string{},
expectedFiles: map[string]string{
"$HOME/.config/gcloud/credentials": `{"account": "gcpuser"}`,
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
envPrep := tc.connection.AsEnv()
envPrep := tc.connection.AsEnv(context.Background())

for i, expected := range tc.expectedEnv {
if envPrep.Env[i] != expected {
t.Errorf("Expected environment variable: %s, but got: %s", expected, envPrep.Env[i])
}
}

if envPrep.File.String() != tc.expectedFile {
t.Errorf("Expected file content:\n%s\nBut got:\n%s", tc.expectedFile, envPrep.File.String())
for path, expected := range tc.expectedFiles {
got := envPrep.Files[path]
if got.String() != expected {
t.Errorf("Expected file content: %s, but got: %s", expected, got.String())
}
}
})
}
Expand Down

0 comments on commit 0a6acab

Please sign in to comment.