Skip to content

Commit 29d07b7

Browse files
authored
Merge pull request #175 from commitdev/integrate-apply-command2
Integrate apply command
2 parents 325a6c4 + 682d8d0 commit 29d07b7

File tree

23 files changed

+210
-291
lines changed

23 files changed

+210
-291
lines changed

cmd/apply.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package cmd
22

33
import (
4+
"log"
5+
"os"
6+
47
"github.com/commitdev/zero/internal/apply"
58
"github.com/commitdev/zero/internal/config/projectconfig"
69
"github.com/commitdev/zero/internal/constants"
@@ -21,7 +24,11 @@ var applyCmd = &cobra.Command{
2124
Use: "apply",
2225
Short: "Execute modules to create projects, infrastructure, etc.",
2326
Run: func(cmd *cobra.Command, args []string) {
24-
// @TODO rootdir?
25-
apply.Apply(projectconfig.RootDir, applyConfigPath, applyEnvironments)
27+
rootDir, err := os.Getwd()
28+
if err != nil {
29+
log.Println(err)
30+
rootDir = projectconfig.RootDir
31+
}
32+
apply.Apply(rootDir, applyConfigPath, applyEnvironments)
2633
},
2734
}

cmd/apply_test.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

cmd/init_test.go

Lines changed: 0 additions & 30 deletions
This file was deleted.

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/commitdev/zero
22

3-
go 1.12
3+
go 1.13
44

55
require (
66
github.com/aws/aws-sdk-go v1.25.33
@@ -24,6 +24,7 @@ require (
2424
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
2525
github.com/spf13/cobra v0.0.6
2626
github.com/stretchr/testify v1.4.0
27+
github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae
2728
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
2829
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
2930
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
4242
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4343
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
4444
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
45+
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
4546
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
4647
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
4748
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -142,6 +143,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
142143
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
143144
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
144145
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
146+
github.com/moby/moby v1.13.1 h1:mC5WwQwCXt/dYxZ1cIrRsnJAWw7VdtcTZUIGr4tXzOM=
145147
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
146148
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
147149
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -179,6 +181,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
179181
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
180182
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
181183
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
184+
github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae h1:vgGSvdW5Lqg+I1aZOlG32uyE6xHpLdKhZzcTEktz5wM=
185+
github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae/go.mod h1:quDq6Se6jlGwiIKia/itDZxqC5rj6/8OdFyMMAwTxCs=
182186
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
183187
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
184188
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=

internal/apply/apply.go

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,77 @@ package apply
22

33
import (
44
"fmt"
5+
"path/filepath"
56

67
"log"
78
"os/exec"
89
"path"
910
"strings"
1011

12+
"github.com/commitdev/zero/internal/module"
1113
"github.com/commitdev/zero/internal/util"
12-
"github.com/commitdev/zero/pkg/util/flog"
1314

15+
"github.com/commitdev/zero/internal/config/globalconfig"
1416
"github.com/commitdev/zero/internal/config/projectconfig"
1517
"github.com/commitdev/zero/pkg/util/exit"
18+
"github.com/commitdev/zero/pkg/util/flog"
1619
"github.com/manifoldco/promptui"
1720
)
1821

19-
// Apply will bootstrap the runtime environment for the project
20-
func Apply(dir string, applyConfigPath string, applyEnvironments []string) []string {
21-
context := loadContext(dir, applyConfigPath, applyEnvironments)
22+
func Apply(rootDir string, configPath string, environments []string) {
23+
if strings.Trim(configPath, " ") == "" {
24+
exit.Fatal("config path cannot be empty!")
25+
}
26+
configFilePath := path.Join(rootDir, configPath)
27+
projectConfig := projectconfig.LoadConfig(configFilePath)
28+
29+
if len(environments) == 0 {
30+
fmt.Println(`Choose the environments to apply. This will create infrastructure, CI pipelines, etc.
31+
At this point, real things will be generated that may cost money!
32+
Only a single environment may be suitable for an initial test, but for a real system we suggest setting up both staging and production environments.`)
33+
environments = promptEnvironments()
34+
}
2235

23-
flog.Infof(":tada: Bootstrapping project %s. Please use the zero.[hcl, yaml] file to modify the project as needed. %s.", context.Name)
36+
flog.Infof(":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name)
2437

2538
flog.Infof("Cloud provider: %s", "AWS") // will this come from the config?
2639

2740
flog.Infof("Runtime platform: %s", "Kubernetes")
2841

2942
flog.Infof("Infrastructure executor: %s", "Terraform")
3043

31-
// other details...
44+
applyAll(rootDir, *projectConfig, environments)
3245

33-
return makeAll(dir, context, applyEnvironments)
46+
// TODO Summary
47+
flog.Infof(":check_mark_button: Done - Summary goes here.")
3448
}
3549

36-
// loadContext will load the context/configuration to be used by the apply command
37-
func loadContext(dir string, applyConfigPath string, applyEnvironments []string) *projectconfig.ZeroProjectConfig {
38-
if len(applyEnvironments) == 0 {
39-
fmt.Println(`Choose the environments to apply. This will create infrastructure, CI pipelines, etc.
40-
At this point, real things will be generated that may cost money!
41-
Only a single environment may be suitable for an initial test, but for a real system we suggest setting up both staging and production environments.`)
42-
applyEnvironments = promptEnvironments()
43-
}
50+
func applyAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEnvironments []string) {
51+
environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ","))
4452

45-
validateEnvironments(applyEnvironments)
53+
// Go through each of the modules and run `make`
54+
for _, mod := range projectConfig.Modules {
55+
// Add env vars for the makefile
56+
envList := []string{
57+
environmentArg,
58+
fmt.Sprintf("PROJECT_DIR=%s", path.Join(dir, mod.Files.Directory)),
59+
fmt.Sprintf("REPOSITORY=%s", mod.Files.Repository),
60+
}
4661

47-
if applyConfigPath == "" {
48-
exit.Fatal("config path cannot be empty!")
62+
modulePath := module.GetSourceDir(mod.Files.Source)
63+
// Passed in `dir` will only be used to find the project path, not the module path,
64+
// unless the module path is relative
65+
if module.IsLocal(mod.Files.Source) && !filepath.IsAbs(modulePath) {
66+
modulePath = filepath.Join(dir, modulePath)
67+
}
68+
69+
// Get project credentials for the makefile
70+
credentials := globalconfig.GetProjectCredentials(projectConfig.Name)
71+
72+
envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList)
73+
envList = util.AppendProjectEnvToCmdEnv(credentials.AsEnvVars(), envList)
74+
util.ExecuteCommand(exec.Command("make"), modulePath, envList)
4975
}
50-
configPath := path.Join(dir, applyConfigPath)
51-
projectConfig := projectconfig.LoadConfig(configPath)
52-
return projectConfig
5376
}
5477

5578
// promptEnvironments Prompts the user for the environments to apply against and returns a slice of strings representing the environments
@@ -85,18 +108,3 @@ func validateEnvironments(applyEnvironments []string) {
85108
}
86109
}
87110
}
88-
89-
func makeAll(dir string, projectContext *projectconfig.ZeroProjectConfig, applyEnvironments []string) []string {
90-
environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ","))
91-
envList := []string{environmentArg}
92-
outputs := []string{}
93-
94-
for _, mod := range projectContext.Modules {
95-
modulePath := path.Join(dir, mod.Files.Directory)
96-
envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList)
97-
98-
output := util.ExecuteCommandOutput(exec.Command("make"), modulePath, envList)
99-
outputs = append(outputs, output)
100-
}
101-
return outputs
102-
}

internal/apply/apply_test.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
package apply_test
22

33
import (
4+
"io/ioutil"
5+
"os"
6+
"path/filepath"
47
"testing"
58

69
"github.com/commitdev/zero/internal/apply"
710
"github.com/commitdev/zero/internal/constants"
811
"github.com/stretchr/testify/assert"
12+
"github.com/termie/go-shutil"
913
)
1014

1115
func TestApply(t *testing.T) {
12-
// @TODO is there a way to do this without relative paths?
13-
dir := "../../tests/test_data/sample_project/"
16+
dir := "../../tests/test_data/apply/"
1417
applyConfigPath := constants.ZeroProjectYml
1518
applyEnvironments := []string{"staging", "production"}
1619

17-
want := []string{
18-
"make module zero-aws-eks-stack\n",
19-
"make module zero-deployable-backend\n",
20-
"make module zero-deployable-react-frontend\n",
21-
}
20+
tmpDir := filepath.Join(os.TempDir(), "apply")
21+
22+
err := os.RemoveAll(tmpDir)
23+
assert.NoError(t, err)
24+
25+
err = shutil.CopyTree(dir, tmpDir, nil)
26+
assert.NoError(t, err)
2227

2328
t.Run("Should run apply and execute make on each folder module", func(t *testing.T) {
24-
got := apply.Apply(dir, applyConfigPath, applyEnvironments)
25-
assert.ElementsMatch(t, want, got)
29+
apply.Apply(tmpDir, applyConfigPath, applyEnvironments)
30+
assert.FileExists(t, filepath.Join(tmpDir, "project1/project.out"))
31+
assert.FileExists(t, filepath.Join(tmpDir, "project2/project.out"))
32+
33+
content, err := ioutil.ReadFile(filepath.Join(tmpDir, "project1/project.out"))
34+
assert.NoError(t, err)
35+
assert.Equal(t, "foo: bar\nrepo: github.com/commitdev/project1\n", string(content))
36+
37+
content, err = ioutil.ReadFile(filepath.Join(tmpDir, "project2/project.out"))
38+
assert.NoError(t, err)
39+
assert.Equal(t, "baz: qux\n", string(content))
2640
})
2741
}

internal/config/globalconfig/global_config.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"os/user"
99
"path"
10+
"reflect"
1011

1112
"github.com/commitdev/zero/internal/constants"
1213
"github.com/commitdev/zero/pkg/util/exit"
@@ -25,14 +26,14 @@ type ProjectCredential struct {
2526
}
2627

2728
type AWSResourceConfig struct {
28-
AccessKeyId string `yaml:"accessKeyId,omitempty"`
29-
SecretAccessKey string `yaml:"secretAccessKey,omitempty"`
29+
AccessKeyID string `yaml:"accessKeyId,omitempty" env:"AWS_ACCESS_KEY_ID"`
30+
SecretAccessKey string `yaml:"secretAccessKey,omitempty" env:"AWS_SECRET_ACCESS_KEY"`
3031
}
3132
type GithubResourceConfig struct {
32-
AccessToken string `yaml:"accessToken,omitempty"`
33+
AccessToken string `yaml:"accessToken,omitempty" env:"GITHUB_ACCESS_TOKEN"`
3334
}
3435
type CircleCiResourceConfig struct {
35-
ApiKey string `yaml:"apiKey,omitempty"`
36+
ApiKey string `yaml:"apiKey,omitempty" env:"CIRCLECI_API_KEY"`
3637
}
3738

3839
func (p ProjectCredentials) Unmarshal(data []byte) error {
@@ -50,6 +51,35 @@ func (p ProjectCredentials) Unmarshal(data []byte) error {
5051
return nil
5152
}
5253

54+
// AsEnvVars marshals ProjectCredential as a map of key/value strings suitable for environment variables
55+
func (p ProjectCredential) AsEnvVars() map[string]string {
56+
t := reflect.ValueOf(p)
57+
58+
list := make(map[string]string)
59+
list = gatherFieldTags(t, list)
60+
61+
return list
62+
}
63+
64+
func gatherFieldTags(t reflect.Value, list map[string]string) map[string]string {
65+
reflectType := t.Type()
66+
67+
for i := 0; i < t.NumField(); i++ {
68+
fieldValue := t.Field(i)
69+
fieldType := reflectType.Field(i)
70+
71+
if fieldType.Type.Kind() == reflect.Struct {
72+
list = gatherFieldTags(fieldValue, list)
73+
continue
74+
}
75+
76+
if env := fieldType.Tag.Get("env"); env != "" {
77+
list[env] = fieldValue.String()
78+
}
79+
}
80+
return list
81+
}
82+
5383
func LoadUserCredentials() ProjectCredentials {
5484
data := readOrCreateUserCredentialsFile()
5585

internal/config/globalconfig/global_config_test.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func TestGetUserCredentials(t *testing.T) {
6666
project := globalconfig.GetProjectCredentials(projectName)
6767

6868
// Reading from fixtures: tests/test_data/configs/credentials.yml
69-
assert.Equal(t, "AKIAABCD", project.AWSResourceConfig.AccessKeyId)
69+
assert.Equal(t, "AKIAABCD", project.AWSResourceConfig.AccessKeyID)
7070
assert.Equal(t, "ZXCV", project.AWSResourceConfig.SecretAccessKey)
7171
assert.Equal(t, "0987", project.GithubResourceConfig.AccessToken)
7272
assert.Equal(t, "SOME_API_KEY", project.CircleCiResourceConfig.ApiKey)
@@ -87,17 +87,36 @@ func TestEditUserCredentials(t *testing.T) {
8787
t.Run("Should create new project if not exist", func(t *testing.T) {
8888
projectName := "test-project3"
8989
project := globalconfig.GetProjectCredentials(projectName)
90-
project.AWSResourceConfig.AccessKeyId = "TEST_KEY_ID_1"
90+
project.AWSResourceConfig.AccessKeyID = "TEST_KEY_ID_1"
9191
globalconfig.Save(project)
92-
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyId
92+
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyID
9393
assert.Equal(t, "TEST_KEY_ID_1", newKeyID)
9494
})
9595
t.Run("Should edit old project if already exist", func(t *testing.T) {
9696
projectName := "my-project"
9797
project := globalconfig.GetProjectCredentials(projectName)
98-
project.AWSResourceConfig.AccessKeyId = "EDITED_ACCESS_KEY_ID"
98+
project.AWSResourceConfig.AccessKeyID = "EDITED_ACCESS_KEY_ID"
9999
globalconfig.Save(project)
100-
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyId
100+
newKeyID := globalconfig.GetProjectCredentials(projectName).AWSResourceConfig.AccessKeyID
101101
assert.Equal(t, "EDITED_ACCESS_KEY_ID", newKeyID)
102102
})
103103
}
104+
105+
func TestMarshalProjectCredentialAsEnvVars(t *testing.T) {
106+
t.Run("Should be able to marshal a ProjectCredential into env vars", func(t *testing.T) {
107+
pc := globalconfig.ProjectCredential{
108+
AWSResourceConfig: globalconfig.AWSResourceConfig{
109+
AccessKeyID: "AKID",
110+
SecretAccessKey: "SAK",
111+
},
112+
CircleCiResourceConfig: globalconfig.CircleCiResourceConfig{
113+
ApiKey: "APIKEY",
114+
},
115+
}
116+
117+
envVars := pc.AsEnvVars()
118+
assert.Equal(t, "AKID", envVars["AWS_ACCESS_KEY_ID"])
119+
assert.Equal(t, "SAK", envVars["AWS_SECRET_ACCESS_KEY"])
120+
assert.Equal(t, "APIKEY", envVars["CIRCLECI_API_KEY"])
121+
})
122+
}

0 commit comments

Comments
 (0)