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

Global cred prompt #164

Merged
merged 1 commit into from
Jun 17, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/config/globalconfig/global_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func readOrCreateUserCredentialsFile() []byte {
return data
}

func GetUserCredentials(targetProjectName string) ProjectCredential {
func GetProjectCredentials(targetProjectName string) ProjectCredential {
projects := LoadUserCredentials()

if val, ok := projects[targetProjectName]; ok {
Expand Down
14 changes: 7 additions & 7 deletions internal/config/globalconfig/global_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestReadOrCreateUserCredentialsFile(t *testing.T) {
_, fileStateErr := os.Stat(credPath)
assert.True(t, os.IsNotExist(fileStateErr), "File should not exist")
// attempting to read the file should create the file
globalconfig.GetUserCredentials("any-project")
globalconfig.GetProjectCredentials("any-project")

stats, err := os.Stat(credPath)
assert.False(t, os.IsNotExist(err), "File should be created")
Expand All @@ -63,7 +63,7 @@ func TestGetUserCredentials(t *testing.T) {

t.Run("Fixture file should have existing project with creds", func(t *testing.T) {
projectName := "my-project"
project := globalconfig.GetUserCredentials(projectName)
project := globalconfig.GetProjectCredentials(projectName)

// Reading from fixtures: tests/test_data/configs/credentials.yml
assert.Equal(t, "AKIAABCD", project.AWSResourceConfig.AccessKeyId)
Expand All @@ -74,7 +74,7 @@ func TestGetUserCredentials(t *testing.T) {

t.Run("Fixture file should support multiple projects", func(t *testing.T) {
projectName := "another-project"
project := globalconfig.GetUserCredentials(projectName)
project := globalconfig.GetProjectCredentials(projectName)
assert.Equal(t, "654", project.GithubResourceConfig.AccessToken)
})
}
Expand All @@ -86,18 +86,18 @@ func TestEditUserCredentials(t *testing.T) {

t.Run("Should create new project if not exist", func(t *testing.T) {
projectName := "test-project3"
project := globalconfig.GetUserCredentials(projectName)
project := globalconfig.GetProjectCredentials(projectName)
project.AWSResourceConfig.AccessKeyId = "TEST_KEY_ID_1"
globalconfig.Save(project)
newKeyID := globalconfig.GetUserCredentials(projectName).AWSResourceConfig.AccessKeyId
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyId
assert.Equal(t, "TEST_KEY_ID_1", newKeyID)
})
t.Run("Should edit old project if already exist", func(t *testing.T) {
projectName := "my-project"
project := globalconfig.GetUserCredentials(projectName)
project := globalconfig.GetProjectCredentials(projectName)
project.AWSResourceConfig.AccessKeyId = "EDITED_ACCESS_KEY_ID"
globalconfig.Save(project)
newKeyID := globalconfig.GetUserCredentials(projectName).AWSResourceConfig.AccessKeyId
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyId
assert.Equal(t, "EDITED_ACCESS_KEY_ID", newKeyID)
})
}
2 changes: 1 addition & 1 deletion internal/config/moduleconfig/module_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type ModuleConfig struct {
Description string
Author string
TemplateConfig `yaml:"template"`
RequiredCredentials []string
RequiredCredentials []string `yaml:"requiredCredentials"`
Parameters []Parameter
}

Expand Down
86 changes: 70 additions & 16 deletions internal/context/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,10 @@ func Init(outDir string) *projectconfig.ZeroProjectConfig {

// Prompting for push-up stream, then conditionally prompting for github
initParams["GithubRootOrg"] = prompts["GithubRootOrg"].GetParam(initParams)
initParams["GithubPersonalToken"] = prompts["GithubPersonalToken"].GetParam(initParams)
if initParams["GithubRootOrg"] != "" && initParams["GithubPersonalToken"] != globalconfig.GetUserCredentials(projectConfig.Name).AccessToken {
projectCredential := globalconfig.GetUserCredentials(projectConfig.Name)
projectCredential.GithubResourceConfig.AccessToken = initParams["GithubPersonalToken"]
globalconfig.Save(projectCredential)
}

projectCredentials := globalconfig.GetProjectCredentials(projectConfig.Name)
credentialPrompts := getCredentialPrompts(projectCredentials, moduleConfigs)
projectCredentials = promptCredentialsAndFillProjectCreds(credentialPrompts, projectCredentials)
globalconfig.Save(projectCredentials)
projectParameters := promptAllModules(moduleConfigs)

// Map parameter values back to specific modules
Expand All @@ -84,6 +81,7 @@ func Init(outDir string) *projectconfig.ZeroProjectConfig {

// TODO : Write the project config file. For now, print.
pp.Println(projectConfig)
pp.Print(projectCredentials)

// TODO: load ~/.zero/config.yml (or credentials)
// TODO: prompt global credentials
Expand Down Expand Up @@ -159,15 +157,6 @@ func getProjectPrompts(projectName string, modules map[string]moduleconfig.Modul
KeyMatchCondition("ShouldPushRepositories", "y"),
NoValidation,
},
"GithubPersonalToken": {
moduleconfig.Parameter{
Field: "GithubPersonalToken",
Label: "Github Personal Access Token with access to the above organization",
Default: globalconfig.GetUserCredentials(projectName).AccessToken,
},
KeyMatchCondition("ShouldPushRepositories", "y"),
NoValidation,
},
}

for moduleName, module := range modules {
Expand All @@ -187,6 +176,71 @@ func getProjectPrompts(projectName string, modules map[string]moduleconfig.Modul
return handlers
}

func getCredentialPrompts(projectCredentials globalconfig.ProjectCredential, moduleConfigs map[string]moduleconfig.ModuleConfig) map[string][]PromptHandler {
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)
}
return prompts
}

func mapVendorToPrompts(projectCred globalconfig.ProjectCredential, vendor string) []PromptHandler {
var prompts []PromptHandler

switch vendor {
case "aws":
awsPrompts := []PromptHandler{
{
moduleconfig.Parameter{
Field: "accessKeyId",
Label: "AWS Access Key ID",
Default: projectCred.AWSResourceConfig.AccessKeyId,
},
NoCondition,
NoValidation,
},
{
moduleconfig.Parameter{
Field: "secretAccessKey",
Label: "AWS Secret access key",
Default: projectCred.AWSResourceConfig.SecretAccessKey,
},
NoCondition,
NoValidation,
},
}
prompts = append(prompts, awsPrompts...)
case "github":
githubPrompt := PromptHandler{
moduleconfig.Parameter{
Field: "accessToken",
Label: "Github Personal Access Token with access to the above organization",
Default: projectCred.GithubResourceConfig.AccessToken,
},
NoCondition,
NoValidation,
}
prompts = append(prompts, githubPrompt)
case "circleci":
circleCiPrompt := PromptHandler{
moduleconfig.Parameter{
Field: "apiKey",
Label: "Circleci api key for CI/CD",
Default: projectCred.CircleCiResourceConfig.ApiKey,
},
NoCondition,
NoValidation,
}
prompts = append(prompts, circleCiPrompt)
}
return prompts
}

func chooseCloudProvider(projectConfig *projectconfig.ZeroProjectConfig) {
// @TODO move options into configs
providerPrompt := promptui.Select{
Expand Down
40 changes: 40 additions & 0 deletions internal/context/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"regexp"
"strings"

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

type PromptHandler struct {
Expand Down Expand Up @@ -147,3 +149,41 @@ func PromptModuleParams(moduleConfig moduleconfig.ModuleConfig, parameters map[s
}
return parameters, nil
}

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

for vendor, prompts := range credentialPrompts {
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{})
}
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)
Copy link
Member

Choose a reason for hiding this comment

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

Hm yeah that's not ideal..

Copy link
Member

Choose a reason for hiding this comment

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

Probably okay for now though..

return credentials
}

func appendToSet(set []string, toAppend []string) []string {
for _, appendee := range toAppend {
if !itemInSlice(set, appendee) {
set = append(set, appendee)
}
}
return set
}

func itemInSlice(slice []string, target string) bool {
for _, item := range slice {
if item == target {
return true
}
}
return false
}
4 changes: 4 additions & 0 deletions internal/module/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func TestParseModuleConfig(t *testing.T) {
assert.Equal(t, "CI Platform", param.Label)
})

t.Run("requiredCredentials are loaded", func(t *testing.T) {
assert.Equal(t, []string{"aws", "circleci", "github"}, mod.RequiredCredentials)
})

t.Run("TemplateConfig is unmarshaled", func(t *testing.T) {
mod, _ = module.ParseModuleConfig(testModuleSource)
assert.Equal(t, ".circleci", mod.TemplateConfig.OutputDir)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_data/modules/ci/zero-module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ author: ""
icon: ""
thumbnail: ""

requiredCredentials:
- aws
- circleci
- github

# Template variables to populate, these could be overwritten by the file spefic frontmatter variables
template:
# strictMode: true # will only parse files that includes the .tmpl.* extension, otherwise it will copy file
Expand Down