Skip to content

Commit 7e55bdd

Browse files
authored
Merge pull request #174 from commitdev/aws-profile-prompt
Aws profile prompt
2 parents 74528df + 6a06dfd commit 7e55bdd

File tree

8 files changed

+172
-135
lines changed

8 files changed

+172
-135
lines changed

cmd/init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package cmd
22

33
import (
44
"github.com/commitdev/zero/internal/config/projectconfig"
5-
"github.com/commitdev/zero/internal/context"
5+
initPrompts "github.com/commitdev/zero/internal/init"
66
"github.com/spf13/cobra"
77
)
88

@@ -14,7 +14,7 @@ var initCmd = &cobra.Command{
1414
Use: "init",
1515
Short: "Create new project with provided name and initialize configuration based on user input.",
1616
Run: func(cmd *cobra.Command, args []string) {
17-
projectContext := context.Init(projectconfig.RootDir)
17+
projectContext := initPrompts.Init(projectconfig.RootDir)
1818
projectconfig.Init(projectconfig.RootDir, projectContext.Name, projectContext)
1919
},
2020
}

internal/init/debug.test

24.3 MB
Binary file not shown.
Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package context
1+
package init
22

33
import (
44
"fmt"
@@ -171,42 +171,87 @@ func getProjectPrompts(projectName string, modules map[string]moduleconfig.Modul
171171
return handlers
172172
}
173173

174-
func getCredentialPrompts(projectCredentials globalconfig.ProjectCredential, moduleConfigs map[string]moduleconfig.ModuleConfig) map[string][]PromptHandler {
174+
func getCredentialPrompts(projectCredentials globalconfig.ProjectCredential, moduleConfigs map[string]moduleconfig.ModuleConfig) []CredentialPrompts {
175175
var uniqueVendors []string
176176
for _, module := range moduleConfigs {
177177
uniqueVendors = appendToSet(uniqueVendors, module.RequiredCredentials)
178178
}
179+
179180
// map is to keep track of which vendor they belong to, to fill them back into the projectConfig
180-
prompts := map[string][]PromptHandler{}
181-
for _, vendor := range uniqueVendors {
182-
prompts[vendor] = mapVendorToPrompts(projectCredentials, vendor)
181+
prompts := []CredentialPrompts{}
182+
for _, vendor := range AvailableVendorOrders {
183+
if itemInSlice(uniqueVendors, vendor) {
184+
vendorPrompts := CredentialPrompts{vendor, mapVendorToPrompts(projectCredentials, vendor)}
185+
prompts = append(prompts, vendorPrompts)
186+
}
183187
}
184188
return prompts
185189
}
186190

187191
func mapVendorToPrompts(projectCred globalconfig.ProjectCredential, vendor string) []PromptHandler {
188192
var prompts []PromptHandler
193+
profiles, err := project.GetAWSProfiles()
194+
if err != nil {
195+
profiles = []string{}
196+
}
197+
198+
// if no profiles available, dont prompt use to pick profile
199+
customAwsPickProfileCondition := func(param map[string]string) bool {
200+
if len(profiles) == 0 {
201+
flog.Infof(":warning: No AWS profiles found, please manually input AWS credentials")
202+
return false
203+
} else {
204+
return true
205+
}
206+
}
207+
208+
// condition for prompting manual AWS credentials input
209+
customAwsMustInputCondition := func(param map[string]string) bool {
210+
toPickProfile := awsPickProfile
211+
if val, ok := param["use_aws_profile"]; ok && val != toPickProfile {
212+
return true
213+
}
214+
return false
215+
}
189216

190217
switch vendor {
191218
case "aws":
192219
awsPrompts := []PromptHandler{
220+
{
221+
moduleconfig.Parameter{
222+
Field: "use_aws_profile",
223+
Label: "Use credentials from existing AWS profiles?",
224+
Options: []string{awsPickProfile, awsManualInputCredentials},
225+
},
226+
customAwsPickProfileCondition,
227+
NoValidation,
228+
},
229+
{
230+
moduleconfig.Parameter{
231+
Field: "aws_profile",
232+
Label: "Select AWS Profile",
233+
Options: profiles,
234+
},
235+
KeyMatchCondition("use_aws_profile", awsPickProfile),
236+
NoValidation,
237+
},
193238
{
194239
moduleconfig.Parameter{
195240
Field: "accessKeyId",
196241
Label: "AWS Access Key ID",
197242
Default: projectCred.AWSResourceConfig.AccessKeyId,
198243
},
199-
NoCondition,
200-
NoValidation,
244+
CustomCondition(customAwsMustInputCondition),
245+
project.ValidateAKID,
201246
},
202247
{
203248
moduleconfig.Parameter{
204249
Field: "secretAccessKey",
205250
Label: "AWS Secret access key",
206251
Default: projectCred.AWSResourceConfig.SecretAccessKey,
207252
},
208-
NoCondition,
209-
NoValidation,
253+
CustomCondition(customAwsMustInputCondition),
254+
project.ValidateSAK,
210255
},
211256
}
212257
prompts = append(prompts, awsPrompts...)
Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package context
1+
package init
22

33
import (
44
"fmt"
@@ -10,27 +10,48 @@ import (
1010

1111
"github.com/commitdev/zero/internal/config/globalconfig"
1212
"github.com/commitdev/zero/internal/config/moduleconfig"
13+
"github.com/commitdev/zero/pkg/credentials"
1314
"github.com/commitdev/zero/pkg/util/exit"
1415
"github.com/manifoldco/promptui"
1516
"gopkg.in/yaml.v2"
1617
)
1718

19+
// Constant to maintain prompt orders so users can have the same flow,
20+
// modules get downloaded asynchronously therefore its easier to just hardcode an order
21+
var AvailableVendorOrders = []string{"aws", "github", "circleci"}
22+
23+
const awsPickProfile = "Existing AWS Profiles"
24+
const awsManualInputCredentials = "Enter my own AWS credentials"
25+
1826
type PromptHandler struct {
1927
moduleconfig.Parameter
20-
Condition func(map[string]string) bool
28+
Condition CustomConditionSignature
2129
Validate func(string) error
2230
}
2331

32+
type CredentialPrompts struct {
33+
Vendor string
34+
Prompts []PromptHandler
35+
}
36+
37+
type CustomConditionSignature func(map[string]string) bool
38+
2439
func NoCondition(map[string]string) bool {
2540
return true
2641
}
2742

28-
func KeyMatchCondition(key string, value string) func(map[string]string) bool {
43+
func KeyMatchCondition(key string, value string) CustomConditionSignature {
2944
return func(param map[string]string) bool {
3045
return param[key] == value
3146
}
3247
}
3348

49+
func CustomCondition(fn CustomConditionSignature) CustomConditionSignature {
50+
return func(param map[string]string) bool {
51+
return fn(param)
52+
}
53+
}
54+
3455
func NoValidation(string) error {
3556
return nil
3657
}
@@ -150,24 +171,32 @@ func PromptModuleParams(moduleConfig moduleconfig.ModuleConfig, parameters map[s
150171
return parameters, nil
151172
}
152173

153-
func promptCredentialsAndFillProjectCreds(credentialPrompts map[string][]PromptHandler, credentials globalconfig.ProjectCredential) globalconfig.ProjectCredential {
174+
func promptCredentialsAndFillProjectCreds(credentialPrompts []CredentialPrompts, creds globalconfig.ProjectCredential) globalconfig.ProjectCredential {
154175
promptsValues := map[string]map[string]string{}
155176

156-
for vendor, prompts := range credentialPrompts {
177+
for _, prompts := range credentialPrompts {
178+
vendor := prompts.Vendor
157179
vendorPromptValues := map[string]string{}
158180

159181
// vendors like AWS have multiple prompts (accessKeyId and secretAccessKey)
160-
for _, prompt := range prompts {
161-
vendorPromptValues[prompt.Field] = prompt.GetParam(map[string]string{})
182+
for _, prompt := range prompts.Prompts {
183+
vendorPromptValues[prompt.Field] = prompt.GetParam(vendorPromptValues)
162184
}
163185
promptsValues[vendor] = vendorPromptValues
164186
}
165187

166188
// FIXME: what is a good way to dynamically modify partial data of a struct
167189
// current just marashing to yaml, then unmarshaling into the base struct
168190
yamlContent, _ := yaml.Marshal(promptsValues)
169-
yaml.Unmarshal(yamlContent, &credentials)
170-
return credentials
191+
yaml.Unmarshal(yamlContent, &creds)
192+
193+
// Fill AWS credentials based on profile from ~/.aws/credentials
194+
if val, ok := promptsValues["aws"]; ok {
195+
if val["use_aws_profile"] == awsPickProfile {
196+
creds = credentials.GetAWSProfileProjectCredentials(val["aws_profile"], creds)
197+
}
198+
}
199+
return creds
171200
}
172201

173202
func appendToSet(set []string, toAppend []string) []string {
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
package context_test
1+
package init_test
22

33
import (
44
"testing"
55

66
"github.com/commitdev/zero/internal/config/moduleconfig"
7-
"github.com/commitdev/zero/internal/context"
7+
// init is a reserved word
8+
initPrompts "github.com/commitdev/zero/internal/init"
89

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

20-
prompt := context.PromptHandler{
21+
prompt := initPrompts.PromptHandler{
2122
param,
22-
context.NoCondition,
23-
context.NoValidation,
23+
initPrompts.NoCondition,
24+
initPrompts.NoValidation,
2425
}
2526

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

36-
prompt := context.PromptHandler{
37+
prompt := initPrompts.PromptHandler{
3738
param,
38-
context.NoCondition,
39-
context.NoValidation,
39+
initPrompts.NoCondition,
40+
initPrompts.NoValidation,
4041
}
4142

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

54-
prompt := context.PromptHandler{
55+
prompt := initPrompts.PromptHandler{
5556
param,
56-
context.NoCondition,
57-
context.NoValidation,
57+
initPrompts.NoCondition,
58+
initPrompts.NoValidation,
5859
}
5960

6061
result := prompt.GetParam(projectParams)

0 commit comments

Comments
 (0)