Skip to content

Commit

Permalink
zero init features: dedupe/new prompts/config file
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcheung committed Jun 2, 2020
1 parent 532413e commit 2211d53
Show file tree
Hide file tree
Showing 9 changed files with 336 additions and 27 deletions.
10 changes: 6 additions & 4 deletions configs/configs.go
@@ -1,8 +1,10 @@
package configs

const (
TemplatesDir = "tmp/templates"
ZeroProjectYml = "zero-project.yml"
IgnoredPaths = "(?i)zero.module.yml|.git/"
TemplateExtn = ".tmpl"
TemplatesDir = "tmp/templates"
ZeroProjectYml = "zero-project.yml"
ZeroHomeDirectory = ".zero"
UserCredentials = "credentials.yml"
IgnoredPaths = "(?i)zero.module.yml|.git/"
TemplateExtn = ".tmpl"
)
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -24,7 +24,7 @@ require (
github.com/matryer/is v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/spf13/cobra v0.0.6
github.com/stretchr/testify v1.5.1 // indirect
github.com/stretchr/testify v1.4.0
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -181,6 +181,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
Expand Down
123 changes: 123 additions & 0 deletions internal/config/global_config.go
@@ -0,0 +1,123 @@
package config

import (
"bytes"
"io/ioutil"
"log"
"os"
"os/user"
"path"

"github.com/commitdev/zero/configs"
"github.com/commitdev/zero/pkg/util/exit"
yaml "gopkg.in/yaml.v2"
)

var GetCredentialsPath = getCredentialsPath

type ProjectCredentials map[string]ProjectCredential

type ProjectCredential struct {
ProjectName string `yaml:"-"`
AWSResourceConfig `yaml:"aws,omitempty"`
GithubResourceConfig `yaml:"github,omitempty"`
CircleCiResourceConfig `yaml:"circleci,omitempty"`
}

type AWSResourceConfig struct {
AccessKeyId string `yaml:"accessKeyId,omitempty"`
SecretAccessKey string `yaml:"secretAccessKey,omitempty"`
}
type GithubResourceConfig struct {
AccessToken string `yaml:"accessToken,omitempty"`
}
type CircleCiResourceConfig struct {
ApiKey string `yaml:"apiKey,omitempty"`
}

func (p ProjectCredentials) Unmarshal(data []byte) error {
if len(data) == 0 {
return nil
}
err := yaml.NewDecoder(bytes.NewReader(data)).Decode(p)
if err != nil {
return err
}
for k, v := range p {
v.ProjectName = k
p[k] = v
}
return nil
}

func LoadUserCredentials() ProjectCredentials {
data := readOrCreateUserCredentialsFile()

projects := ProjectCredentials{}

err := projects.Unmarshal(data)

if err != nil {
exit.Fatal("Failed to parse configuration: %v", err)
}
return projects
}

func getCredentialsPath() string {
usr, err := user.Current()
if err != nil {
exit.Fatal("Failed to get user directory path: %v", err)
}

rootDir := path.Join(usr.HomeDir, configs.ZeroHomeDirectory)
os.MkdirAll(rootDir, os.ModePerm)
filePath := path.Join(rootDir, configs.UserCredentials)
return filePath
}

func readOrCreateUserCredentialsFile() []byte {
credPath := GetCredentialsPath()

_, fileStateErr := os.Stat(credPath)
if os.IsNotExist(fileStateErr) {
var file, fileStateErr = os.Create(credPath)
if fileStateErr != nil {
exit.Fatal("Failed to create config file: %v", fileStateErr)
}
defer file.Close()
}
data, err := ioutil.ReadFile(credPath)
if err != nil {
exit.Fatal("Failed to read credentials file: %v", err)
}
return data
}

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

if val, ok := projects[targetProjectName]; ok {
return val
} else {
p := ProjectCredential{
ProjectName: targetProjectName,
}
projects[targetProjectName] = p
return p
}
}

func Save(project ProjectCredential) {
projects := LoadUserCredentials()
projects[project.ProjectName] = project
writeCredentialsFile(projects)
}

func writeCredentialsFile(projects ProjectCredentials) {
credsPath := GetCredentialsPath()
content, _ := yaml.Marshal(projects)
err := ioutil.WriteFile(credsPath, content, 0644)
if err != nil {
log.Panicf("failed to write config: %v", err)
}
}
103 changes: 103 additions & 0 deletions internal/config/global_config_test.go
@@ -0,0 +1,103 @@
package config_test

import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"testing"

"github.com/commitdev/zero/internal/config"
"github.com/stretchr/testify/assert"
)

const baseTestFixturesDir = "../../tests/test_data/configs/"

var testCredentialFile = func() (func() string, func()) {
tmpConfigPath := getTmpConfig()
mockFunc := func() string { return tmpConfigPath }
teardownFunc := func() { os.RemoveAll(tmpConfigPath) }
return mockFunc, teardownFunc
}

func getTmpConfig() string {
pathFrom := path.Join(baseTestFixturesDir, fmt.Sprintf("credentials%s.yml", ""))
pathTo := path.Join(baseTestFixturesDir, fmt.Sprintf("credentials%s.yml", "-tmp"))
copyFile(pathFrom, pathTo)
return pathTo
}

func copyFile(from string, to string) {
bytesRead, err := ioutil.ReadFile(from)
if err != nil {
log.Fatal(err)
}

err = ioutil.WriteFile(to, bytesRead, 0644)
if err != nil {
log.Fatal(err)
}
}
func TestReadOrCreateUserCredentialsFile(t *testing.T) {
config.GetCredentialsPath = func() string {
return path.Join(baseTestFixturesDir, "does-not-exist.yml")
}
credPath := config.GetCredentialsPath()

defer os.RemoveAll(credPath)
_, fileStateErr := os.Stat(credPath)
assert.True(t, os.IsNotExist(fileStateErr), "File should not exist")
// attempting to read the file should create the file
config.GetUserCredentials("any-project")

stats, err := os.Stat(credPath)
assert.False(t, os.IsNotExist(err), "File should be created")
assert.Equal(t, "does-not-exist.yml", stats.Name(), "Should create yml automatically")
}

func TestGetUserCredentials(t *testing.T) {
var teardownFn func()
config.GetCredentialsPath, teardownFn = testCredentialFile()
defer teardownFn()

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

// Reading from fixtures: tests/test_data/configs/credentials.yml
assert.Equal(t, "AKIAABCD", project.AWSResourceConfig.AccessKeyId)
assert.Equal(t, "ZXCV", project.AWSResourceConfig.SecretAccessKey)
assert.Equal(t, "0987", project.GithubResourceConfig.AccessToken)
assert.Equal(t, "SOME_API_KEY", project.CircleCiResourceConfig.ApiKey)
})

t.Run("Fixture file should support multiple projects", func(t *testing.T) {
projectName := "another-project"
project := config.GetUserCredentials(projectName)
assert.Equal(t, "654", project.GithubResourceConfig.AccessToken)
})
}

func TestEditUserCredentials(t *testing.T) {
var teardownFn func()
config.GetCredentialsPath, teardownFn = testCredentialFile()
defer teardownFn()

t.Run("Should create new project if not exist", func(t *testing.T) {
projectName := "test-project3"
project := config.GetUserCredentials(projectName)
project.AWSResourceConfig.AccessKeyId = "TEST_KEY_ID_1"
config.Save(project)
newKeyID := config.GetUserCredentials(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 := config.GetUserCredentials(projectName)
project.AWSResourceConfig.AccessKeyId = "EDITED_ACCESS_KEY_ID"
config.Save(project)
newKeyID := config.GetUserCredentials(projectName).AWSResourceConfig.AccessKeyId
assert.Equal(t, "EDITED_ACCESS_KEY_ID", newKeyID)
})
}
3 changes: 2 additions & 1 deletion internal/config/init_test.go
Expand Up @@ -23,7 +23,8 @@ func TestInit(t *testing.T) {
t.Fatal(err)
}

config.Init(config.RootDir, projectName, nil)
projectConfig := config.ZeroProjectConfig{}
config.Init(config.RootDir, projectName, &projectConfig)

if _, err := os.Stat(path.Join(testDirPath, configs.ZeroProjectYml)); err != nil {
t.Fatal(err)
Expand Down

0 comments on commit 2211d53

Please sign in to comment.