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

dedupe #143

Merged
merged 1 commit into from
Jun 2, 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
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
Copy link
Member

Choose a reason for hiding this comment

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

Nice tests!


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) {
Copy link
Member

Choose a reason for hiding this comment

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

These should really be testing the API of the package rather than the internals. By calling a function like ReadOrCreateUserCredentialsFile it means that function now has to be exported, and I would think the only exported functions would be LoadUserCredentials and Save

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Make sense, should I create another file for tests like this under the config package? so I can keep them private and still test them?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe, though the general practice is try to make your tests in the _test package, then test the exported stuff and not the internals so that your test is coupled to the "API" which should be stable and not the internals which could change. And if you get to the point where the "API" is masking a massive amount of code, try to split it up into smaller packages which can be easily tested in isolation.

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