Skip to content

Commit 5510cad

Browse files
APPS-9765 Add command to restart apps (#1608)
* APPS-9765 Add command to restart apps * add test * bump godo and vendor
1 parent 3d3b6ec commit 5510cad

File tree

10 files changed

+218
-6
lines changed

10 files changed

+218
-6
lines changed

args.go

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ const (
5858
ArgNoPrefix = "no-prefix"
5959
// ArgAppForceRebuild forces a deployment rebuild
6060
ArgAppForceRebuild = "force-rebuild"
61+
// ArgAppComponents is a list of components to restart.
62+
ArgAppComponents = "components"
6163
// ArgAppAlertDestinations is a path to an app alert destination file.
6264
ArgAppAlertDestinations = "app-alert-destinations"
6365
// ArgClusterName is a cluster name argument.

commands/apps.go

+57
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,21 @@ This permanently deletes the app and all of its associated deployments.`,
127127
AddBoolFlag(deleteApp, doctl.ArgForce, doctl.ArgShortForce, false, "Delete the App without a confirmation prompt")
128128
deleteApp.Example = `The following example deletes an app with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + `: doctl apps delete f81d4fae-7dec-11d0-a765-00a0c91e6bf6`
129129

130+
restartApp := CmdBuilder(
131+
cmd,
132+
RunAppsRestart,
133+
"restart <app id>",
134+
"Restarts an app",
135+
`Restarts the specified app or some of its components.`,
136+
Writer,
137+
aliasOpt("r"),
138+
displayerType(&displayers.Deployments{}),
139+
)
140+
AddStringSliceFlag(restartApp, doctl.ArgAppComponents, "", nil, "The components to restart. If not provided, all components are restarted.")
141+
AddBoolFlag(restartApp, doctl.ArgCommandWait, "", false,
142+
"Boolean that specifies whether to wait for the restart to complete before allowing further terminal input. This can be helpful for scripting.")
143+
restartApp.Example = `The following example restarts an app with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + `. Additionally, the command returns the app's ID and status: doctl apps restart f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --format ID,Status`
144+
130145
deploymentCreate := CmdBuilder(
131146
cmd,
132147
RunAppsCreateDeployment,
@@ -469,6 +484,48 @@ func RunAppsDelete(c *CmdConfig) error {
469484
return nil
470485
}
471486

487+
// RunAppsRestart restarts an app.
488+
func RunAppsRestart(c *CmdConfig) error {
489+
if len(c.Args) < 1 {
490+
return doctl.NewMissingArgsErr(c.NS)
491+
}
492+
appID := c.Args[0]
493+
components, err := c.Doit.GetStringSlice(c.NS, doctl.ArgAppComponents)
494+
if err != nil {
495+
return err
496+
}
497+
498+
wait, err := c.Doit.GetBool(c.NS, doctl.ArgCommandWait)
499+
if err != nil {
500+
return err
501+
}
502+
503+
deployment, err := c.Apps().Restart(appID, components)
504+
if err != nil {
505+
return err
506+
}
507+
508+
var errs error
509+
510+
if wait {
511+
apps := c.Apps()
512+
notice("Restart is in progress, waiting for the restart to complete")
513+
err := waitForActiveDeployment(apps, appID, deployment.ID)
514+
if err != nil {
515+
errs = multierror.Append(errs, fmt.Errorf("app deployment couldn't enter `running` state: %v", err))
516+
if err := c.Display(displayers.Deployments{deployment}); err != nil {
517+
errs = multierror.Append(errs, err)
518+
}
519+
return errs
520+
}
521+
deployment, _ = c.Apps().GetDeployment(appID, deployment.ID)
522+
}
523+
524+
notice("Restarted")
525+
526+
return c.Display(displayers.Deployments{deployment})
527+
}
528+
472529
// RunAppsCreateDeployment creates a deployment for an app.
473530
func RunAppsCreateDeployment(c *CmdConfig) error {
474531
if len(c.Args) < 1 {

commands/apps_test.go

+105
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func TestAppsCommand(t *testing.T) {
3636
"list-regions",
3737
"logs",
3838
"propose",
39+
"restart",
3940
"spec",
4041
"tier",
4142
"list-alerts",
@@ -344,6 +345,110 @@ func TestRunAppsCreateDeploymentWithWait(t *testing.T) {
344345
})
345346
}
346347

348+
func TestRunAppsRestart(t *testing.T) {
349+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
350+
appID := uuid.New().String()
351+
deployment := &godo.Deployment{
352+
ID: uuid.New().String(),
353+
Spec: &testAppSpec,
354+
Services: []*godo.DeploymentService{{
355+
Name: "service",
356+
SourceCommitHash: "commit",
357+
}},
358+
Cause: "Manual",
359+
Phase: godo.DeploymentPhase_PendingDeploy,
360+
Progress: &godo.DeploymentProgress{
361+
PendingSteps: 1,
362+
RunningSteps: 0,
363+
SuccessSteps: 0,
364+
ErrorSteps: 0,
365+
TotalSteps: 1,
366+
367+
Steps: []*godo.DeploymentProgressStep{{
368+
Name: "name",
369+
Status: "pending",
370+
StartedAt: time.Now(),
371+
}},
372+
},
373+
CreatedAt: time.Now(),
374+
UpdatedAt: time.Now(),
375+
}
376+
377+
tm.apps.EXPECT().Restart(appID, []string{"component1", "component2"}).Times(1).Return(deployment, nil)
378+
379+
config.Args = append(config.Args, appID)
380+
config.Doit.Set(config.NS, doctl.ArgAppComponents, []string{"component1", "component2"})
381+
382+
err := RunAppsRestart(config)
383+
require.NoError(t, err)
384+
})
385+
}
386+
387+
func TestRunAppsRestartWithWait(t *testing.T) {
388+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
389+
appID := uuid.New().String()
390+
deployment := &godo.Deployment{
391+
ID: uuid.New().String(),
392+
Spec: &testAppSpec,
393+
Services: []*godo.DeploymentService{{
394+
Name: "service",
395+
SourceCommitHash: "commit",
396+
}},
397+
Cause: "Manual",
398+
Phase: godo.DeploymentPhase_PendingDeploy,
399+
Progress: &godo.DeploymentProgress{
400+
PendingSteps: 1,
401+
RunningSteps: 0,
402+
SuccessSteps: 0,
403+
ErrorSteps: 0,
404+
TotalSteps: 1,
405+
406+
Steps: []*godo.DeploymentProgressStep{{
407+
Name: "name",
408+
Status: "pending",
409+
StartedAt: time.Now(),
410+
}},
411+
},
412+
CreatedAt: time.Now(),
413+
UpdatedAt: time.Now(),
414+
}
415+
activeDeployment := &godo.Deployment{
416+
ID: uuid.New().String(),
417+
Spec: &testAppSpec,
418+
Services: []*godo.DeploymentService{{
419+
Name: "service",
420+
SourceCommitHash: "commit",
421+
}},
422+
Cause: "Manual",
423+
Phase: godo.DeploymentPhase_Active,
424+
Progress: &godo.DeploymentProgress{
425+
PendingSteps: 1,
426+
RunningSteps: 0,
427+
SuccessSteps: 1,
428+
ErrorSteps: 0,
429+
TotalSteps: 1,
430+
431+
Steps: []*godo.DeploymentProgressStep{{
432+
Name: "name",
433+
Status: "pending",
434+
StartedAt: time.Now(),
435+
}},
436+
},
437+
CreatedAt: time.Now(),
438+
UpdatedAt: time.Now(),
439+
}
440+
441+
tm.apps.EXPECT().Restart(appID, nil).Times(1).Return(deployment, nil)
442+
tm.apps.EXPECT().GetDeployment(appID, deployment.ID).Times(2).Return(activeDeployment, nil)
443+
444+
config.Args = append(config.Args, appID)
445+
config.Doit.Set(config.NS, doctl.ArgCommandWait, true)
446+
447+
err := RunAppsRestart(config)
448+
require.NoError(t, err)
449+
})
450+
}
451+
347452
func TestRunAppsGetDeployment(t *testing.T) {
348453
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
349454
appID := uuid.New().String()

do/apps.go

+11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type AppsService interface {
2828
Delete(appID string) error
2929
Propose(req *godo.AppProposeRequest) (*godo.AppProposeResponse, error)
3030

31+
Restart(appID string, components []string) (*godo.Deployment, error)
3132
CreateDeployment(appID string, forceRebuild bool) (*godo.Deployment, error)
3233
GetDeployment(appID, deploymentID string) (*godo.Deployment, error)
3334
ListDeployments(appID string) ([]*godo.Deployment, error)
@@ -132,6 +133,16 @@ func (s *appsService) Propose(req *godo.AppProposeRequest) (*godo.AppProposeResp
132133
return res, nil
133134
}
134135

136+
func (s *appsService) Restart(appID string, components []string) (*godo.Deployment, error) {
137+
deployment, _, err := s.client.Apps.Restart(s.ctx, appID, &godo.AppRestartRequest{
138+
Components: components,
139+
})
140+
if err != nil {
141+
return nil, err
142+
}
143+
return deployment, nil
144+
}
145+
135146
func (s *appsService) CreateDeployment(appID string, forceRebuild bool) (*godo.Deployment, error) {
136147
deployment, _, err := s.client.Apps.CreateDeployment(s.ctx, appID, &godo.DeploymentCreateRequest{
137148
ForceBuild: forceRebuild,

do/mocks/AppsService.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.22
55
require (
66
github.com/blang/semver v3.5.1+incompatible
77
github.com/creack/pty v1.1.21
8-
github.com/digitalocean/godo v1.130.0
8+
github.com/digitalocean/godo v1.130.1-0.20241119155329-45ad288c38bd
99
github.com/docker/cli v24.0.5+incompatible
1010
github.com/docker/docker v25.0.6+incompatible
1111
github.com/docker/docker-credential-helpers v0.7.0 // indirect

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
9191
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9292
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
9393
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
94-
github.com/digitalocean/godo v1.130.0 h1:DbJg0wvBxTkYjY5Q9S1mwzAZLd5Wht3r57yFH4yeMCk=
95-
github.com/digitalocean/godo v1.130.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
94+
github.com/digitalocean/godo v1.130.1-0.20241119155329-45ad288c38bd h1:3TCd+SNAbaRHQSiWmMJWtPitvZt2lTq3th87CxMl9Xo=
95+
github.com/digitalocean/godo v1.130.1-0.20241119155329-45ad288c38bd/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
9696
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
9797
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
9898
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=

vendor/github.com/digitalocean/godo/apps.gen.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/digitalocean/godo/apps.go

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/modules.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ github.com/creack/pty
6161
# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
6262
## explicit
6363
github.com/davecgh/go-spew/spew
64-
# github.com/digitalocean/godo v1.130.0
64+
# github.com/digitalocean/godo v1.130.1-0.20241119155329-45ad288c38bd
6565
## explicit; go 1.22
6666
github.com/digitalocean/godo
6767
github.com/digitalocean/godo/metrics

0 commit comments

Comments
 (0)