Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit b99a484

Browse files
authored
Merge pull request #775 from zappy-shu/APP-308-rm-multi-apps
[WIP] Allow rm of multiple apps in one command
2 parents d119541 + 518754b commit b99a484

File tree

2 files changed

+77
-29
lines changed

2 files changed

+77
-29
lines changed

e2e/commands_test.go

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,8 @@ func TestDockerAppLifecycle(t *testing.T) {
341341

342342
// Install a Docker Application Package with an existing failed installation is fine
343343
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
344-
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
345-
[]string{
346-
fmt.Sprintf("WARNING: installing over previously failed installation %q", appName),
347-
fmt.Sprintf("Creating network %s_back", appName),
348-
fmt.Sprintf("Creating network %s_front", appName),
349-
fmt.Sprintf("Creating service %s_db", appName),
350-
fmt.Sprintf("Creating service %s_api", appName),
351-
fmt.Sprintf("Creating service %s_web", appName),
352-
})
353-
assertAppLabels(t, &cmd, appName, "db")
344+
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), expectedAppRunOutput(appName, true))
345+
assertAppDbLabels(t, &cmd, appName)
354346

355347
// List the installed application
356348
cmd.Command = dockerCli.Command("app", "ls")
@@ -375,21 +367,64 @@ func TestDockerAppLifecycle(t *testing.T) {
375367
fmt.Sprintf("Updating service %s_api", appName),
376368
fmt.Sprintf("Updating service %s_web", appName),
377369
})
378-
assertAppLabels(t, &cmd, appName, "db")
370+
assertAppDbLabels(t, &cmd, appName)
379371

380372
// Uninstall the application
381373
cmd.Command = dockerCli.Command("app", "rm", appName)
382-
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
383-
[]string{
384-
fmt.Sprintf("Removing service %s_api", appName),
385-
fmt.Sprintf("Removing service %s_db", appName),
386-
fmt.Sprintf("Removing service %s_web", appName),
387-
fmt.Sprintf("Removing network %s_front", appName),
388-
fmt.Sprintf("Removing network %s_back", appName),
389-
})
374+
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), expectedAppRmOutput(appName))
375+
})
376+
}
377+
378+
func TestDockerAppLifecycleMultiRm(t *testing.T) {
379+
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
380+
cmd := info.configuredCmd
381+
appName := strings.ToLower(strings.Replace(t.Name(), "/", "_", 1))
382+
tmpDir := fs.NewDir(t, appName)
383+
defer tmpDir.Remove()
384+
385+
cmd.Command = dockerCli.Command("app", "build", "--tag", appName, "testdata/simple")
386+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
387+
388+
// Install multiple applications
389+
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
390+
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), expectedAppRunOutput(appName, false))
391+
assertAppDbLabels(t, &cmd, appName)
392+
appName2 := appName + "2"
393+
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName2, "--set", "web_port=8083")
394+
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), expectedAppRunOutput(appName2, false))
395+
assertAppDbLabels(t, &cmd, appName2)
396+
397+
// Uninstall multiple applications
398+
cmd.Command = dockerCli.Command("app", "rm", appName, appName2)
399+
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), append(expectedAppRmOutput(appName), expectedAppRmOutput(appName2)...))
390400
})
391401
}
392402

403+
func expectedAppRunOutput(appName string, prevFailed bool) []string {
404+
expected := []string{}
405+
if prevFailed {
406+
expected = append(expected, fmt.Sprintf("WARNING: installing over previously failed installation %q", appName))
407+
}
408+
expected = append(expected,
409+
fmt.Sprintf("Creating network %s_back", appName),
410+
fmt.Sprintf("Creating network %s_front", appName),
411+
fmt.Sprintf("Creating service %s_db", appName),
412+
fmt.Sprintf("Creating service %s_api", appName),
413+
fmt.Sprintf("Creating service %s_web", appName),
414+
)
415+
return expected
416+
}
417+
418+
func expectedAppRmOutput(appName string) []string {
419+
return []string{
420+
fmt.Sprintf("Removing service %s_api", appName),
421+
fmt.Sprintf("Removing service %s_db", appName),
422+
fmt.Sprintf("Removing service %s_web", appName),
423+
fmt.Sprintf("Removing network %s_front", appName),
424+
fmt.Sprintf("Removing network %s_back", appName),
425+
}
426+
}
427+
393428
func TestCredentials(t *testing.T) {
394429
credSet := &credentials.CredentialSet{
395430
Name: "test-creds",
@@ -499,8 +534,8 @@ func TestCredentials(t *testing.T) {
499534
})
500535
}
501536

502-
func assertAppLabels(t *testing.T, cmd *icmd.Cmd, appName, containerName string) {
503-
cmd.Command = dockerCli.Command("inspect", fmt.Sprintf("%s_%s", appName, containerName))
537+
func assertAppDbLabels(t *testing.T, cmd *icmd.Cmd, appName string) {
538+
cmd.Command = dockerCli.Command("inspect", fmt.Sprintf("%s_db", appName))
504539
checkContains(t, icmd.RunCmd(*cmd).Assert(t, icmd.Success).Combined(),
505540
[]string{
506541
fmt.Sprintf(`"%s": "%s"`, internal.LabelAppNamespace, appName),

internal/commands/remove.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"github.com/docker/app/internal/cliopts"
88
"github.com/docker/app/internal/cnab"
99
"github.com/docker/app/internal/packager"
10+
"github.com/docker/app/internal/store"
1011

1112
"github.com/deislabs/cnab-go/driver"
1213

1314
"github.com/deislabs/cnab-go/action"
1415
"github.com/deislabs/cnab-go/credentials"
1516
"github.com/docker/cli/cli"
1617
"github.com/docker/cli/cli/command"
18+
multierror "github.com/hashicorp/go-multierror"
1719
"github.com/spf13/cobra"
1820
)
1921

@@ -30,9 +32,20 @@ func removeCmd(dockerCli command.Cli, installerContext *cliopts.InstallerContext
3032
Short: "Remove a running App",
3133
Aliases: []string{"remove"},
3234
Example: `$ docker app rm myrunningapp`,
33-
Args: cli.ExactArgs(1),
35+
Args: cli.RequiresMinArgs(1),
3436
RunE: func(cmd *cobra.Command, args []string) error {
35-
return runRemove(dockerCli, args[0], opts, installerContext)
37+
_, installationStore, credentialStore, err := prepareStores(dockerCli.CurrentContext())
38+
if err != nil {
39+
return err
40+
}
41+
42+
var failures *multierror.Error
43+
for _, arg := range args {
44+
if err := runRemove(dockerCli, arg, opts, installerContext, installationStore, credentialStore); err != nil {
45+
failures = multierror.Append(failures, err)
46+
}
47+
}
48+
return failures.ErrorOrNil()
3649
},
3750
}
3851
opts.credentialOptions.addFlags(cmd.Flags())
@@ -41,12 +54,12 @@ func removeCmd(dockerCli command.Cli, installerContext *cliopts.InstallerContext
4154
return cmd
4255
}
4356

44-
func runRemove(dockerCli command.Cli, installationName string, opts removeOptions, installerContext *cliopts.InstallerContextOptions) (mainErr error) {
45-
_, installationStore, credentialStore, err := prepareStores(dockerCli.CurrentContext())
46-
if err != nil {
47-
return err
48-
}
49-
57+
func runRemove(dockerCli command.Cli,
58+
installationName string,
59+
opts removeOptions,
60+
installerContext *cliopts.InstallerContextOptions,
61+
installationStore store.InstallationStore,
62+
credentialStore store.CredentialStore) (mainErr error) {
5063
installation, err := installationStore.Read(installationName)
5164
if err != nil {
5265
return err

0 commit comments

Comments
 (0)