Skip to content

Commit 0a7c643

Browse files
authored
feat: add svc exec and task exec (#2053)
* chore: add custom aws-sdk-go for ECS cr https://code.amazon.com/reviews/CR-39348057 * commit e19046ca2668f00fdbd4d328d86ef2b9e576b9d5 Author: penghaoh <penghaoh@amazon.com> Date: 2020-11-30T22:14:28.000Z Address feedback commit c09bbba6bd10d646417510d998451a1f2e141bbe Author: penghaoh <penghaoh@amazon.com> Date: 2020-11-23T23:23:21.000Z chore: add boilerplate for copilot exec cr https://code.amazon.com/reviews/CR-39357784 commit 46a0f2bea06e6436e1ecf333546f5a3fa50411e2 Author: penghaoh <penghaoh@amazon.com> Date: 2020-11-23T20:14:22.000Z chore: add custom aws-sdk-go for ECS cr https://code.amazon.com/reviews/CR-39348057 commit 25be354 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: 2020-11-30T17:07:40.000Z chore: Bump github.com/briandowns/spinner from 1.11.1 to 1.12.0 (#1729) Bumps [github.com/briandowns/spinner](https://github.com/briandowns/spinner) from 1.11.1 to 1.12.0. - [Release notes](https://github.com/briandowns/spinner/releases) - [Commits](briandowns/spinner@v1.11.1...v1.12) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> commit ef735c9 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: 2020-11-30T17:02:37.000Z chore: Bump github.com/aws/aws-sdk-go from 1.35.33 to 1.35.35 (#1730) Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.35.33 to 1.35.35. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/master/CHANGELOG.md) - [Commits](aws/aws-sdk-go@v1.35.33...v1.35.35) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit ce836bd Author: Janice Huang <60631893+huanjani@users.noreply.github.com> Date: 2020-11-24T22:43:38.000Z chore(cli): make the dashes under column headings consistent (#1705) Wherever command output is in tabular format, this brings consistency to our formatting-- dashes (same length as heading, not longest element) below the headings. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. commit 6128e9c Author: Janice Huang <60631893+huanjani@users.noreply.github.com> Date: 2020-11-24T20:22:47.000Z feat(cli): display secrets as subcategory in `svc_show` (#1704) This change displays secrets as a separate category (not within "Variables") in `svc show`. If the secret value comes from Parameter Store, it is preceded by `parameter/`. Otherwise, if it comes from Secrets Manager, it is displayed as an ARN. Addresses #720 By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. commit 42567bf Author: Efe Karakus <karakuse@amazon.com> Date: 2020-11-23T21:39:32.000Z docs(README): remove preview program description (#1722) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ * chore(cli): boilerplate for svc/job/task exec cr https://code.amazon.com/reviews/CR-39984997 * Address feedback and add --cluster flag to task exec * chore(cli): add validate and ask for svc exec cr https://code.amazon.com/reviews/CR-40079015 * chore(cli): implement svc exec Execute() cr https://code.amazon.com/reviews/CR-40177002 * Address comment * chore(ecs): implement ExecuteCommand API cr https://code.amazon.com/reviews/CR-40448327 * Remove log in ecs pkg * Add log prompting users to delete the orphan session * Remove ErrExecuteCommand * chore(exec): implement StartSession for OS cr https://code.amazon.com/reviews/CR-40615982 * Use http/Get to download the binary instead of curl to local * Move ssm plugin validate to cli pkg * chore(ecs): remove terminate session call in ExecuteCommand cr https://code.amazon.com/reviews/CR-40863766 * chore(exec): add plugin management for non macos platforms cr https://code.amazon.com/reviews/CR-40966835 * chore: use gamma endpoint for ECS and RGTA client * Avoid outputting when validating ssm plugin on windows * Update frontend model * Address feedback * Address feedback * chore(exec): apply interactive run to start session cr https://code.amazon.com/reviews/CR-41395554 * chore(manifest): add exec field to the manifest cr https://code.amazon.com/reviews/CR-41475925 * chore(stack): update stack pkg and CFN template for services and environment cr https://code.amazon.com/reviews/CR-41546916 * Address iyerv@ feedback * Rename exec to executeCommand and use go template instead of CFN param * chore(cli): remove interactive flag and job exec cr https://code.amazon.com/reviews/CR-41851301 * chore(cli): add task exec validate cr https://code.amazon.com/reviews/CR-41860865 * chore(cli): add ssm plugin validation for task exec cr https://code.amazon.com/reviews/CR-41862462 * chore(selector): add running tasks selector (#1792) <!-- Provide summary of changes --> Add `RunningTask()` to `selector` pkg, allowing to select a running copilot task. <!-- Issue number, if available. E.g. "Fixes #31", "Addresses #42, 77" --> By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. * Resolve merging conflict cr https://code.amazon.com/reviews/CR-41929264 * chore(cli): add Ask() for task exec cr https://code.amazon.com/reviews/CR-41994838 * Fix prompt text cr https://code.amazon.com/reviews/CR-42292095 * Address feedback * chore(templates): add resource comments to v1.2.0 env template * chore(cli): implement Execute() for task exec cr https://code.amazon.com/reviews/CR-42377497 * chore: enable execute-command for task run cr https://code.amazon.com/reviews/CR-42467741 * chore(cli): remove exec related unused code cr https://code.amazon.com/reviews/CR-42531908 * docs: add docs for svc/task exec cr https://code.amazon.com/reviews/CR-42539124 * Address feedback * chore(cli): task exec should be able to use --default in workspace cr https://code.amazon.com/reviews/CR-43124690 * chore(env): update v1.2.0 env template cr https://code.amazon.com/reviews/CR-43574909 * chore(doc): add info for svc-exec cr https://code.amazon.com/reviews/CR-43631507 * chore: fix template bugs and remove gamma endpoint usage cr https://code.amazon.com/reviews/CR-43826647 * Address feedback * Update the doc * chore(task): add default task role for exec cr https://code.amazon.com/reviews/CR-43838665 * Update task exec doc * chore(exec): enable svc exec by default cr https://code.amazon.com/reviews/CR-43849472 * Change execute_command default to false * chore(exec): task exec only lists one-off task cr https://code.amazon.com/reviews/CR-43918751 * chore: fix ubuntu ssm plugin install and exec doc cr https://code.amazon.com/reviews/CR-44013235 * Address feedback * chore(manifest): rename execute_command to exec cr https://code.amazon.com/reviews/CR-44072460 * chore(exec): UI changes and test fixes cr https://code.amazon.com/reviews/CR-44358240 * chore: fix unit test cr https://code.amazon.com/reviews/CR-44369866 * chore(template): switch AMZN::ECS::Service to AWS::ECS::Service cr https://code.amazon.com/reviews/CR-44443109 * chore(exec): add flag to skip confirmation for SSM plugin * chore(exec): task exec task family name should be prefixed * chore(e2e): add exec suite cr https://code.amazon.com/reviews/CR-44552067 * Address feedback * Add to the e2e buildspec * chore(doc): add exec video cr https://code.amazon.com/reviews/CR-47049800 * Disable exec e2e test * Remove new-sdk-go and update aws ecs sdk go
1 parent 2479051 commit 0a7c643

File tree

137 files changed

+5372
-1645
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+5372
-1645
lines changed

cmd/copilot/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ func buildRootCmd() *cobra.Command {
7070
// "Release" command group.
7171
cmd.AddCommand(cli.BuildPipelineCmd())
7272
cmd.AddCommand(cli.BuildDeployCmd())
73-
cmd.SetUsageTemplate(template.RootUsage)
7473

74+
// "Debug" command group.
75+
cmd.SetUsageTemplate(template.RootUsage)
7576
return cmd
7677
}

e2e/exec/DockerfileTask

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM python:3.7-slim
2+
RUN touch hello
3+
ENTRYPOINT ["tail", "-f", "/dev/null"]

e2e/exec/copilot/hello/manifest.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# We're creating the manifest ahead of time as we want to test the service with a count > 1.
2+
name: hello
3+
type: Load Balanced Web Service
4+
image:
5+
build:
6+
dockerfile: hello/Dockerfile
7+
context: hello
8+
port: 80
9+
10+
http:
11+
path: '/'
12+
13+
cpu: 256
14+
memory: 512
15+
count: 2
16+
exec: true

e2e/exec/exec_suite_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package exec_test
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
"time"
10+
11+
"github.com/aws/copilot-cli/e2e/internal/client"
12+
. "github.com/onsi/ginkgo"
13+
. "github.com/onsi/gomega"
14+
)
15+
16+
var cli *client.CLI
17+
var appName, envName, svcName, groupName string
18+
19+
// The Exec suite runs creates a new service and task and exec into them.
20+
func TestExec(t *testing.T) {
21+
RegisterFailHandler(Fail)
22+
RunSpecs(t, "Exec Suite")
23+
}
24+
25+
var _ = BeforeSuite(func() {
26+
ecsCli, err := client.NewCLI()
27+
cli = ecsCli
28+
Expect(err).NotTo(HaveOccurred())
29+
appName = fmt.Sprintf("e2e-exec-%d", time.Now().Unix())
30+
groupName = fmt.Sprintf("e2e-exec-task-%d", time.Now().Unix())
31+
svcName = "hello"
32+
envName = "test"
33+
})
34+
35+
var _ = AfterSuite(func() {
36+
_, err := cli.AppDelete()
37+
Expect(err).NotTo(HaveOccurred())
38+
})
39+
40+
func BeforeAll(fn func()) {
41+
first := true
42+
BeforeEach(func() {
43+
if first {
44+
fn()
45+
first = false
46+
}
47+
})
48+
}

e2e/exec/exec_test.go

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package exec_test
5+
6+
import (
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"strings"
11+
"time"
12+
13+
"github.com/aws/copilot-cli/e2e/internal/client"
14+
. "github.com/onsi/ginkgo"
15+
. "github.com/onsi/gomega"
16+
)
17+
18+
var _ = Describe("exec flow", func() {
19+
Context("when creating a new app", func() {
20+
var (
21+
initErr error
22+
)
23+
BeforeAll(func() {
24+
_, initErr = cli.AppInit(&client.AppInitRequest{
25+
AppName: appName,
26+
})
27+
})
28+
29+
It("app init succeeds", func() {
30+
Expect(initErr).NotTo(HaveOccurred())
31+
})
32+
33+
It("app init creates an copilot directory and workspace file", func() {
34+
Expect("./copilot").Should(BeADirectory())
35+
Expect("./copilot/.workspace").Should(BeAnExistingFile())
36+
})
37+
38+
It("app ls includes new app", func() {
39+
Eventually(cli.AppList, "30s", "5s").Should(ContainSubstring(appName))
40+
})
41+
42+
It("app show includes app name", func() {
43+
appShowOutput, err := cli.AppShow(appName)
44+
Expect(err).NotTo(HaveOccurred())
45+
Expect(appShowOutput.Name).To(Equal(appName))
46+
Expect(appShowOutput.URI).To(BeEmpty())
47+
})
48+
})
49+
50+
Context("when creating a new environment", func() {
51+
var (
52+
testEnvInitErr error
53+
)
54+
BeforeAll(func() {
55+
_, testEnvInitErr = cli.EnvInit(&client.EnvInitRequest{
56+
AppName: appName,
57+
EnvName: envName,
58+
Profile: "default",
59+
Prod: false,
60+
})
61+
})
62+
63+
It("env init should succeed", func() {
64+
Expect(testEnvInitErr).NotTo(HaveOccurred())
65+
})
66+
})
67+
68+
Context("when adding a svc", func() {
69+
var (
70+
svcInitErr error
71+
)
72+
BeforeAll(func() {
73+
_, svcInitErr = cli.SvcInit(&client.SvcInitRequest{
74+
Name: svcName,
75+
SvcType: "Load Balanced Web Service",
76+
Dockerfile: "./hello/Dockerfile",
77+
SvcPort: "80",
78+
})
79+
})
80+
81+
It("svc init should succeed", func() {
82+
Expect(svcInitErr).NotTo(HaveOccurred())
83+
})
84+
85+
It("svc init should create svc manifests", func() {
86+
Expect("./copilot/hello/manifest.yml").Should(BeAnExistingFile())
87+
})
88+
89+
It("svc ls should list the service", func() {
90+
svcList, svcListError := cli.SvcList(appName)
91+
Expect(svcListError).NotTo(HaveOccurred())
92+
Expect(len(svcList.Services)).To(Equal(1))
93+
94+
svcsByName := map[string]client.WkldDescription{}
95+
for _, svc := range svcList.Services {
96+
svcsByName[svc.Name] = svc
97+
}
98+
99+
for _, svc := range []string{svcName} {
100+
Expect(svcsByName[svc].AppName).To(Equal(appName))
101+
Expect(svcsByName[svc].Name).To(Equal(svc))
102+
}
103+
})
104+
})
105+
106+
Context("when deploying svc", func() {
107+
const newContent = "HELP I AM TRAPPED INSIDE A SHELL"
108+
var (
109+
appDeployErr error
110+
taskID1 string
111+
taskID2 string
112+
)
113+
BeforeAll(func() {
114+
_, appDeployErr = cli.SvcDeploy(&client.SvcDeployInput{
115+
Name: svcName,
116+
EnvName: envName,
117+
ImageTag: "gallopinggurdey",
118+
})
119+
})
120+
121+
It("svc deploy should succeed", func() {
122+
Expect(appDeployErr).NotTo(HaveOccurred())
123+
})
124+
125+
It("svc status should show two tasks running", func() {
126+
svc, svcStatusErr := cli.SvcStatus(&client.SvcStatusRequest{
127+
AppName: appName,
128+
Name: svcName,
129+
EnvName: envName,
130+
})
131+
Expect(svcStatusErr).NotTo(HaveOccurred())
132+
// Service should be active.
133+
Expect(svc.Service.Status).To(Equal("ACTIVE"))
134+
// Should have correct number of running tasks.
135+
Expect(len(svc.Tasks)).To(Equal(2))
136+
taskID1 = svc.Tasks[0].ID
137+
taskID2 = svc.Tasks[1].ID
138+
})
139+
140+
It("svc show should include a valid URL and description for test env", func() {
141+
svc, err := cli.SvcShow(&client.SvcShowRequest{
142+
AppName: appName,
143+
Name: svcName,
144+
})
145+
Expect(err).NotTo(HaveOccurred())
146+
Expect(len(svc.Routes)).To(Equal(1))
147+
148+
route := svc.Routes[0]
149+
Expect(route.Environment).To(Equal(envName))
150+
var resp *http.Response
151+
var fetchErr error
152+
Eventually(func() (int, error) {
153+
resp, fetchErr = http.Get(route.URL)
154+
return resp.StatusCode, fetchErr
155+
}, "60s", "1s").Should(Equal(200))
156+
// Read the response - our deployed service should return a body with its
157+
// name as the value.
158+
bodyBytes, err := ioutil.ReadAll(resp.Body)
159+
Expect(err).NotTo(HaveOccurred())
160+
Expect(string(bodyBytes)).To(Equal(svcName))
161+
})
162+
163+
It("svc exec should be able to modify the content of the website", func() {
164+
_, err := cli.SvcExec(&client.SvcExecRequest{
165+
Name: svcName,
166+
AppName: appName,
167+
TaskID: taskID1,
168+
Command: fmt.Sprintf(`/bin/sh -c "echo '%s' > /usr/share/nginx/html/index.html"`, newContent),
169+
EnvName: envName,
170+
})
171+
Expect(err).NotTo(HaveOccurred())
172+
_, err = cli.SvcExec(&client.SvcExecRequest{
173+
Name: svcName,
174+
AppName: appName,
175+
TaskID: taskID2,
176+
Command: fmt.Sprintf(`/bin/sh -c "echo '%s' > /usr/share/nginx/html/index.html"`, newContent),
177+
EnvName: envName,
178+
})
179+
Expect(err).NotTo(HaveOccurred())
180+
})
181+
182+
It("website content should be modified", func() {
183+
svc, err := cli.SvcShow(&client.SvcShowRequest{
184+
AppName: appName,
185+
Name: svcName,
186+
})
187+
Expect(err).NotTo(HaveOccurred())
188+
Expect(len(svc.Routes)).To(Equal(1))
189+
route := svc.Routes[0]
190+
191+
for i := 0; i < 5; i++ {
192+
var resp *http.Response
193+
var fetchErr error
194+
Eventually(func() (int, error) {
195+
resp, fetchErr = http.Get(route.URL)
196+
return resp.StatusCode, fetchErr
197+
}, "60s", "1s").Should(Equal(200))
198+
// Our deployed service should return a body with the new content
199+
// as the value.
200+
bodyBytes, err := ioutil.ReadAll(resp.Body)
201+
Expect(err).NotTo(HaveOccurred())
202+
Expect(strings.TrimSpace(string(bodyBytes))).To(Equal(newContent))
203+
time.Sleep(3 * time.Second)
204+
}
205+
})
206+
207+
It("svc logs should include exec logs", func() {
208+
var validTaskExecLogsCount int
209+
for i := 0; i < 10; i++ {
210+
var svcLogs []client.SvcLogsOutput
211+
var svcLogsErr error
212+
Eventually(func() ([]client.SvcLogsOutput, error) {
213+
svcLogs, svcLogsErr = cli.SvcLogs(&client.SvcLogsRequest{
214+
AppName: appName,
215+
Name: svcName,
216+
EnvName: envName,
217+
Since: "1m",
218+
})
219+
return svcLogs, svcLogsErr
220+
}, "60s", "10s").ShouldNot(BeEmpty())
221+
var prevExecLogStreamName string
222+
for _, logLine := range svcLogs {
223+
Expect(logLine.Message).NotTo(Equal(""))
224+
Expect(logLine.LogStreamName).NotTo(Equal(""))
225+
Expect(logLine.Timestamp).NotTo(Equal(0))
226+
Expect(logLine.IngestionTime).NotTo(Equal(0))
227+
if strings.Contains(logLine.LogStreamName, "ecs-execute-command") &&
228+
logLine.LogStreamName != prevExecLogStreamName {
229+
validTaskExecLogsCount++
230+
prevExecLogStreamName = logLine.LogStreamName
231+
}
232+
}
233+
if validTaskExecLogsCount == 2 {
234+
break
235+
}
236+
validTaskExecLogsCount = 0
237+
time.Sleep(5 * time.Second)
238+
}
239+
Expect(validTaskExecLogsCount).To(Equal(2))
240+
})
241+
})
242+
243+
Context("when running a one-off task", func() {
244+
var (
245+
taskRunErr error
246+
)
247+
BeforeAll(func() {
248+
_, taskRunErr = cli.TaskRun(&client.TaskRunInput{
249+
GroupName: groupName,
250+
Dockerfile: "./DockerfileTask",
251+
AppName: appName,
252+
Env: envName,
253+
})
254+
})
255+
256+
It("should succeed", func() {
257+
Expect(taskRunErr).NotTo(HaveOccurred())
258+
})
259+
260+
It("task exec should work", func() {
261+
var resp string
262+
var err error
263+
Eventually(func() (string, error) {
264+
resp, err = cli.TaskExec(&client.TaskExecRequest{
265+
Name: groupName,
266+
AppName: appName,
267+
Command: "ls",
268+
EnvName: envName,
269+
})
270+
return resp, err
271+
}, "60s", "10s").ShouldNot(BeEmpty())
272+
273+
Expect(err).NotTo(HaveOccurred())
274+
Expect(resp).To(ContainSubstring("hello"))
275+
})
276+
})
277+
})

e2e/exec/hello/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM nginx
2+
EXPOSE 80
3+
COPY index.html /usr/share/nginx/html

e2e/exec/hello/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello

0 commit comments

Comments
 (0)