Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions cmd/init.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

Mind renaming context while you're in there? I'd like to get rid of that. I think this should probably be renamed to internal/init to match what we've been going for with other commands where the package is named after the command in cmd.

}

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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading