Skip to content

Commit

Permalink
Merge pull request #174 from commitdev/aws-profile-prompt
Browse files Browse the repository at this point in the history
Aws profile prompt
  • Loading branch information
davidcheung committed Jun 23, 2020
2 parents 74528df + 6a06dfd commit 7e55bdd
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 135 deletions.
4 changes: 2 additions & 2 deletions cmd/init.go
Expand Up @@ -2,7 +2,7 @@ package cmd

import (
"github.com/commitdev/zero/internal/config/projectconfig"
"github.com/commitdev/zero/internal/context"
initPrompts "github.com/commitdev/zero/internal/init"
"github.com/spf13/cobra"
)

Expand All @@ -14,7 +14,7 @@ var initCmd = &cobra.Command{
Use: "init",
Short: "Create new project with provided name and initialize configuration based on user input.",
Run: func(cmd *cobra.Command, args []string) {
projectContext := context.Init(projectconfig.RootDir)
projectContext := initPrompts.Init(projectconfig.RootDir)
projectconfig.Init(projectconfig.RootDir, projectContext.Name, projectContext)
},
}
Binary file added internal/init/debug.test
Binary file not shown.
63 changes: 54 additions & 9 deletions internal/context/init.go → internal/init/init.go
@@ -1,4 +1,4 @@
package context
package init

import (
"fmt"
Expand Down Expand Up @@ -171,42 +171,87 @@ func getProjectPrompts(projectName string, modules map[string]moduleconfig.Modul
return handlers
}

func getCredentialPrompts(projectCredentials globalconfig.ProjectCredential, moduleConfigs map[string]moduleconfig.ModuleConfig) map[string][]PromptHandler {
func getCredentialPrompts(projectCredentials globalconfig.ProjectCredential, moduleConfigs map[string]moduleconfig.ModuleConfig) []CredentialPrompts {
var uniqueVendors []string
for _, module := range moduleConfigs {
uniqueVendors = appendToSet(uniqueVendors, module.RequiredCredentials)
}

// map is to keep track of which vendor they belong to, to fill them back into the projectConfig
prompts := map[string][]PromptHandler{}
for _, vendor := range uniqueVendors {
prompts[vendor] = mapVendorToPrompts(projectCredentials, vendor)
prompts := []CredentialPrompts{}
for _, vendor := range AvailableVendorOrders {
if itemInSlice(uniqueVendors, vendor) {
vendorPrompts := CredentialPrompts{vendor, mapVendorToPrompts(projectCredentials, vendor)}
prompts = append(prompts, vendorPrompts)
}
}
return prompts
}

func mapVendorToPrompts(projectCred globalconfig.ProjectCredential, vendor string) []PromptHandler {
var prompts []PromptHandler
profiles, err := project.GetAWSProfiles()
if err != nil {
profiles = []string{}
}

// if no profiles available, dont prompt use to pick profile
customAwsPickProfileCondition := func(param map[string]string) bool {
if len(profiles) == 0 {
flog.Infof(":warning: No AWS profiles found, please manually input AWS credentials")
return false
} else {
return true
}
}

// condition for prompting manual AWS credentials input
customAwsMustInputCondition := func(param map[string]string) bool {
toPickProfile := awsPickProfile
if val, ok := param["use_aws_profile"]; ok && val != toPickProfile {
return true
}
return false
}

switch vendor {
case "aws":
awsPrompts := []PromptHandler{
{
moduleconfig.Parameter{
Field: "use_aws_profile",
Label: "Use credentials from existing AWS profiles?",
Options: []string{awsPickProfile, awsManualInputCredentials},
},
customAwsPickProfileCondition,
NoValidation,
},
{
moduleconfig.Parameter{
Field: "aws_profile",
Label: "Select AWS Profile",
Options: profiles,
},
KeyMatchCondition("use_aws_profile", awsPickProfile),
NoValidation,
},
{
moduleconfig.Parameter{
Field: "accessKeyId",
Label: "AWS Access Key ID",
Default: projectCred.AWSResourceConfig.AccessKeyId,
},
NoCondition,
NoValidation,
CustomCondition(customAwsMustInputCondition),
project.ValidateAKID,
},
{
moduleconfig.Parameter{
Field: "secretAccessKey",
Label: "AWS Secret access key",
Default: projectCred.AWSResourceConfig.SecretAccessKey,
},
NoCondition,
NoValidation,
CustomCondition(customAwsMustInputCondition),
project.ValidateSAK,
},
}
prompts = append(prompts, awsPrompts...)
Expand Down
47 changes: 38 additions & 9 deletions internal/context/prompts.go → internal/init/prompts.go
@@ -1,4 +1,4 @@
package context
package init

import (
"fmt"
Expand All @@ -10,27 +10,48 @@ import (

"github.com/commitdev/zero/internal/config/globalconfig"
"github.com/commitdev/zero/internal/config/moduleconfig"
"github.com/commitdev/zero/pkg/credentials"
"github.com/commitdev/zero/pkg/util/exit"
"github.com/manifoldco/promptui"
"gopkg.in/yaml.v2"
)

// Constant to maintain prompt orders so users can have the same flow,
// modules get downloaded asynchronously therefore its easier to just hardcode an order
var AvailableVendorOrders = []string{"aws", "github", "circleci"}

const awsPickProfile = "Existing AWS Profiles"
const awsManualInputCredentials = "Enter my own AWS credentials"

type PromptHandler struct {
moduleconfig.Parameter
Condition func(map[string]string) bool
Condition CustomConditionSignature
Validate func(string) error
}

type CredentialPrompts struct {
Vendor string
Prompts []PromptHandler
}

type CustomConditionSignature func(map[string]string) bool

func NoCondition(map[string]string) bool {
return true
}

func KeyMatchCondition(key string, value string) func(map[string]string) bool {
func KeyMatchCondition(key string, value string) CustomConditionSignature {
return func(param map[string]string) bool {
return param[key] == value
}
}

func CustomCondition(fn CustomConditionSignature) CustomConditionSignature {
return func(param map[string]string) bool {
return fn(param)
}
}

func NoValidation(string) error {
return nil
}
Expand Down Expand Up @@ -150,24 +171,32 @@ func PromptModuleParams(moduleConfig moduleconfig.ModuleConfig, parameters map[s
return parameters, nil
}

func promptCredentialsAndFillProjectCreds(credentialPrompts map[string][]PromptHandler, credentials globalconfig.ProjectCredential) globalconfig.ProjectCredential {
func promptCredentialsAndFillProjectCreds(credentialPrompts []CredentialPrompts, creds globalconfig.ProjectCredential) globalconfig.ProjectCredential {
promptsValues := map[string]map[string]string{}

for vendor, prompts := range credentialPrompts {
for _, prompts := range credentialPrompts {
vendor := prompts.Vendor
vendorPromptValues := map[string]string{}

// vendors like AWS have multiple prompts (accessKeyId and secretAccessKey)
for _, prompt := range prompts {
vendorPromptValues[prompt.Field] = prompt.GetParam(map[string]string{})
for _, prompt := range prompts.Prompts {
vendorPromptValues[prompt.Field] = prompt.GetParam(vendorPromptValues)
}
promptsValues[vendor] = vendorPromptValues
}

// FIXME: what is a good way to dynamically modify partial data of a struct
// current just marashing to yaml, then unmarshaling into the base struct
yamlContent, _ := yaml.Marshal(promptsValues)
yaml.Unmarshal(yamlContent, &credentials)
return credentials
yaml.Unmarshal(yamlContent, &creds)

// Fill AWS credentials based on profile from ~/.aws/credentials
if val, ok := promptsValues["aws"]; ok {
if val["use_aws_profile"] == awsPickProfile {
creds = credentials.GetAWSProfileProjectCredentials(val["aws_profile"], creds)
}
}
return creds
}

func appendToSet(set []string, toAppend []string) []string {
Expand Down
23 changes: 12 additions & 11 deletions internal/context/prompts_test.go → internal/init/prompts_test.go
@@ -1,10 +1,11 @@
package context_test
package init_test

import (
"testing"

"github.com/commitdev/zero/internal/config/moduleconfig"
"github.com/commitdev/zero/internal/context"
// init is a reserved word
initPrompts "github.com/commitdev/zero/internal/init"

"github.com/stretchr/testify/assert"
)
Expand All @@ -17,10 +18,10 @@ func TestGetParam(t *testing.T) {
Execute: "echo \"my-acconut-id\"",
}

prompt := context.PromptHandler{
prompt := initPrompts.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
initPrompts.NoCondition,
initPrompts.NoValidation,
}

result := prompt.GetParam(projectParams)
Expand All @@ -33,10 +34,10 @@ func TestGetParam(t *testing.T) {
Execute: "echo $INJECTEDENV",
}

prompt := context.PromptHandler{
prompt := initPrompts.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
initPrompts.NoCondition,
initPrompts.NoValidation,
}

result := prompt.GetParam(map[string]string{
Expand All @@ -51,10 +52,10 @@ func TestGetParam(t *testing.T) {
Value: "lorem-ipsum",
}

prompt := context.PromptHandler{
prompt := initPrompts.PromptHandler{
param,
context.NoCondition,
context.NoValidation,
initPrompts.NoCondition,
initPrompts.NoValidation,
}

result := prompt.GetParam(projectParams)
Expand Down

0 comments on commit 7e55bdd

Please sign in to comment.