Skip to content

Commit 81a8016

Browse files
authored
Merge pull request #662 from iamhopaul123/addons/parse-to-template
chore(addons): parse addons folder to generate template
2 parents 920d6b0 + 905ebfb commit 81a8016

File tree

9 files changed

+404
-3
lines changed

9 files changed

+404
-3
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,4 @@ gen-mocks: tools
151151
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/aws/secretsmanager/mocks/mock_secretsmanager.go -source=./internal/pkg/aws/secretsmanager/secretsmanager.go
152152
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/aws/cloudwatchlogs/mocks/mock_cloudwatchlogs.go -source=./internal/pkg/aws/cloudwatchlogs/cloudwatchlogs.go
153153
${GOBIN}/mockgen -source=./internal/pkg/build/docker/docker.go -package=mocks -destination=./internal/pkg/build/docker/mocks/mock_docker.go
154+
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/addons/mocks/mock_addons.go -source=./internal/pkg/addons/addons.go

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ go 1.13
55
require (
66
github.com/AlecAivazis/survey/v2 v2.0.5
77
github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667 // indirect
8-
github.com/briandowns/spinner v1.9.0
98
github.com/aws/aws-sdk-go v1.29.3
9+
github.com/briandowns/spinner v1.9.0
1010
github.com/fatih/color v1.9.0
1111
github.com/fatih/structs v1.1.0
1212
github.com/gobuffalo/packd v0.4.0

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
227227
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
228228
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
229229
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
230+
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
230231
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
231232
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
232233
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

internal/pkg/addons/addons.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Package addons contains the service to manage addons.
5+
package addons
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"text/template"
11+
12+
"github.com/aws/amazon-ecs-cli-v2/internal/pkg/workspace"
13+
"github.com/aws/amazon-ecs-cli-v2/templates"
14+
"github.com/gobuffalo/packd"
15+
)
16+
17+
const (
18+
addonsTemplatePath = "addons/cf.yml"
19+
)
20+
21+
type workspaceService interface {
22+
ReadAddonFiles(appName string) (*workspace.ReadAddonFilesOutput, error)
23+
}
24+
25+
// Addons represent additional resources for an application.
26+
type Addons struct {
27+
appName string
28+
29+
box packd.Box
30+
ws workspaceService
31+
}
32+
33+
// New creates an Addons struct given an application name.
34+
func New(appName string) (*Addons, error) {
35+
ws, err := workspace.New()
36+
if err != nil {
37+
return nil, fmt.Errorf("workspace cannot be created: %w", err)
38+
}
39+
return &Addons{
40+
appName: appName,
41+
box: templates.Box(),
42+
ws: ws,
43+
}, nil
44+
}
45+
46+
// Template parses params.yml, policy.yml, {resource}.yml, and outputs.yml to generate
47+
// the addons CloudFormation template.
48+
func (a *Addons) Template() (string, error) {
49+
addonContent, err := a.ws.ReadAddonFiles(a.appName)
50+
if err != nil {
51+
return "", err
52+
}
53+
content, err := a.box.FindString(addonsTemplatePath)
54+
if err != nil {
55+
return "", fmt.Errorf("failed to find the cloudformation template at %s", addonsTemplatePath)
56+
}
57+
tpl, err := template.New("template").Parse(content)
58+
if err != nil {
59+
return "", fmt.Errorf("parse CloudFormation template for %s: %w", a.appName, err)
60+
}
61+
var buf bytes.Buffer
62+
templateData := struct {
63+
AppName string
64+
AddonContent *workspace.ReadAddonFilesOutput
65+
}{
66+
AppName: a.appName,
67+
AddonContent: addonContent,
68+
}
69+
if err := tpl.Execute(&buf, templateData); err != nil {
70+
return "", fmt.Errorf("execute CloudFormation template for %s: %w", a.appName, err)
71+
}
72+
return buf.String(), nil
73+
}

internal/pkg/addons/addons_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package addons
5+
6+
import (
7+
"testing"
8+
9+
"github.com/aws/amazon-ecs-cli-v2/internal/pkg/addons/mocks"
10+
"github.com/aws/amazon-ecs-cli-v2/internal/pkg/workspace"
11+
"github.com/gobuffalo/packd"
12+
"github.com/golang/mock/gomock"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func TestTemplate(t *testing.T) {
17+
testCases := map[string]struct {
18+
appName string
19+
20+
mockWorkspace func(m *mocks.MockworkspaceService)
21+
mockBox func(box *packd.MemoryBox)
22+
23+
wantTemplate string
24+
wantErr error
25+
}{
26+
"should return addon template": {
27+
appName: "my-app",
28+
29+
mockBox: func(box *packd.MemoryBox) {
30+
box.AddString(addonsTemplatePath, `Description: Additional resources for application '{{.AppName}}'
31+
Parameters:
32+
{{.AddonContent.Parameters}}
33+
Resources:
34+
{{.AddonContent.Resources}}
35+
Outputs:
36+
{{.AddonContent.Outputs}}`)
37+
},
38+
39+
mockWorkspace: func(m *mocks.MockworkspaceService) {
40+
m.EXPECT().ReadAddonFiles("my-app").Return(&workspace.ReadAddonFilesOutput{
41+
Outputs: []string{"outputs"},
42+
Parameters: []string{"params"},
43+
Resources: []string{"resources"},
44+
}, nil)
45+
},
46+
47+
wantTemplate: `Description: Additional resources for application 'my-app'
48+
Parameters:
49+
[params]
50+
Resources:
51+
[resources]
52+
Outputs:
53+
[outputs]`,
54+
},
55+
}
56+
57+
for name, tc := range testCases {
58+
t.Run(name, func(t *testing.T) {
59+
// GIVEN
60+
ctrl := gomock.NewController(t)
61+
defer ctrl.Finish()
62+
63+
mockWorkspace := mocks.NewMockworkspaceService(ctrl)
64+
tc.mockWorkspace(mockWorkspace)
65+
box := packd.NewMemoryBox()
66+
tc.mockBox(box)
67+
68+
service := Addons{
69+
appName: tc.appName,
70+
ws: mockWorkspace,
71+
box: box,
72+
}
73+
74+
gotTemplate, gotErr := service.Template()
75+
76+
if gotErr != nil {
77+
require.Equal(t, tc.wantErr, gotErr)
78+
} else {
79+
require.Equal(t, tc.wantTemplate, gotTemplate)
80+
}
81+
})
82+
}
83+
}

internal/pkg/addons/mocks/mock_addons.go

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/pkg/workspace/workspace.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
// Package workspace contains functionality to manage a user's local workspace. This includes
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"os"
2323
"path/filepath"
24+
"strings"
2425

2526
"github.com/spf13/afero"
2627
"gopkg.in/yaml.v3"
@@ -29,12 +30,17 @@ import (
2930
const (
3031
// ProjectDirectoryName is the name of the directory where generated infrastructure code will be stored.
3132
ProjectDirectoryName = "ecs-project"
33+
// AddonsFileSuffix is the suffix of the addons files.
34+
AddonsFileSuffix = ".yml"
3235

36+
addonsDirName = "addons"
3337
workspaceSummaryFileName = ".ecs-workspace"
3438
maximumParentDirsToSearch = 5
3539
pipelineFileName = "pipeline.yml"
3640
manifestFileName = "manifest.yml"
3741
buildspecFileName = "buildspec.yml"
42+
paramsFileName = "params.yml"
43+
outputsFileName = "outputs.yml"
3844
)
3945

4046
// Summary is a description of what's associated with this workspace.
@@ -49,6 +55,13 @@ type Workspace struct {
4955
fsUtils *afero.Afero
5056
}
5157

58+
// ReadAddonFilesOutput is the output for ReadAddonFiles.
59+
type ReadAddonFilesOutput struct {
60+
Parameters []string
61+
Resources []string
62+
Outputs []string
63+
}
64+
5265
// New returns a workspace, used for reading and writing to
5366
// user's local workspace.
5467
func New() (*Workspace, error) {
@@ -203,6 +216,54 @@ func (ws *Workspace) DeleteAll() error {
203216
return ws.fsUtils.RemoveAll(ProjectDirectoryName)
204217
}
205218

219+
// ListAddonFiles lists addon file names for an application.
220+
func (ws *Workspace) ListAddonFiles(appName string) ([]string, error) {
221+
projectDir, err := ws.projectDirPath()
222+
if err != nil {
223+
return nil, err
224+
}
225+
addonsFiles, err := ws.fsUtils.ReadDir(filepath.Join(projectDir, appName, addonsDirName))
226+
if err != nil {
227+
return nil, err
228+
}
229+
var addonsFileNames []string
230+
for _, file := range addonsFiles {
231+
if !file.IsDir() && strings.HasSuffix(file.Name(), AddonsFileSuffix) {
232+
addonsFileNames = append(addonsFileNames, file.Name())
233+
}
234+
}
235+
236+
return addonsFileNames, nil
237+
}
238+
239+
// ReadAddonFiles reads all addon files.
240+
func (ws *Workspace) ReadAddonFiles(appName string) (*ReadAddonFilesOutput, error) {
241+
addonFiles, err := ws.ListAddonFiles(appName)
242+
if err != nil {
243+
return nil, fmt.Errorf("list addon files: %w", err)
244+
}
245+
var resources, params, outputs string
246+
for _, fileName := range addonFiles {
247+
content, err := ws.read(appName, addonsDirName, fileName)
248+
if err != nil {
249+
return nil, fmt.Errorf("read addon file %s: %w", fileName, err)
250+
}
251+
switch fileName {
252+
case paramsFileName:
253+
params = string(content)
254+
case outputsFileName:
255+
outputs = string(content)
256+
default:
257+
resources += string(content)
258+
}
259+
}
260+
return &ReadAddonFilesOutput{
261+
Parameters: strings.Split(params, "\n"),
262+
Resources: strings.Split(resources, "\n"),
263+
Outputs: strings.Split(outputs, "\n"),
264+
}, nil
265+
}
266+
206267
func (ws *Workspace) writeSummary(projectName string) error {
207268
summaryPath, err := ws.summaryPath()
208269
if err != nil {

0 commit comments

Comments
 (0)