github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/e2e/commands_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os/user"
     8  	"path/filepath"
     9  	"regexp"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/deislabs/cnab-go/credentials"
    14  	"github.com/docker/app/internal"
    15  	"github.com/docker/app/internal/image"
    16  	"github.com/docker/app/internal/yaml"
    17  	"gotest.tools/assert"
    18  	is "gotest.tools/assert/cmp"
    19  	"gotest.tools/fs"
    20  	"gotest.tools/golden"
    21  	"gotest.tools/icmd"
    22  )
    23  
    24  func TestExitErrorCode(t *testing.T) {
    25  	cmd, cleanup := dockerCli.createTestCmd()
    26  	defer cleanup()
    27  
    28  	cmd.Command = dockerCli.Command("app", "unknown_command")
    29  	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
    30  		ExitCode: 1,
    31  		Err:      "\"unknown_command\" is not a docker app command\nSee 'docker app --help'",
    32  	})
    33  }
    34  
    35  func TestRender(t *testing.T) {
    36  	appsPath := filepath.Join("testdata", "render")
    37  	apps, err := ioutil.ReadDir(appsPath)
    38  	assert.NilError(t, err, "unable to get apps")
    39  	for _, app := range apps {
    40  		appPath := filepath.Join(appsPath, app.Name())
    41  		t.Run(app.Name(), testRenderApp(appPath))
    42  	}
    43  }
    44  
    45  func testRenderApp(appPath string, env ...string) func(*testing.T) {
    46  	return func(t *testing.T) {
    47  		cmd, cleanup := dockerCli.createTestCmd()
    48  		defer cleanup()
    49  		dir := fs.NewDir(t, "")
    50  		defer dir.Remove()
    51  
    52  		// Build the App
    53  		cmd.Command = dockerCli.Command("app", "build", ".", "--file", filepath.Join(appPath, "my.dockerapp"), "--tag", "a-simple-tag", "--no-resolve-image")
    54  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
    55  
    56  		// Render the App
    57  		envParameters := map[string]string{}
    58  		data, err := ioutil.ReadFile(filepath.Join(appPath, "env.yml"))
    59  		assert.NilError(t, err)
    60  		assert.NilError(t, yaml.Unmarshal(data, &envParameters))
    61  		args := dockerCli.Command("app", "image", "render", "a-simple-tag", "--parameters-file", filepath.Join(appPath, "parameters-0.yml"))
    62  		for k, v := range envParameters {
    63  			args = append(args, "--set", fmt.Sprintf("%s=%s", k, v))
    64  		}
    65  		cmd.Command = args
    66  		cmd.Env = append(cmd.Env, env...)
    67  		t.Run("stdout", func(t *testing.T) {
    68  			result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
    69  			expected := readFile(t, filepath.Join(appPath, "expected.txt"))
    70  			actual := result.Stdout()
    71  			assert.Assert(t, is.Equal(expected, actual), "rendering mismatch")
    72  		})
    73  		t.Run("file", func(t *testing.T) {
    74  			cmd.Command = append(cmd.Command, "--output="+dir.Join("actual.yaml"))
    75  			icmd.RunCmd(cmd).Assert(t, icmd.Success)
    76  			expected := readFile(t, filepath.Join(appPath, "expected.txt"))
    77  			actual := readFile(t, dir.Join("actual.yaml"))
    78  			assert.Assert(t, is.Equal(expected, actual), "rendering mismatch")
    79  		})
    80  	}
    81  }
    82  
    83  func TestRenderAppNotFound(t *testing.T) {
    84  	cmd, cleanup := dockerCli.createTestCmd()
    85  	defer cleanup()
    86  
    87  	appName := "non_existing_app:some_tag"
    88  	cmd.Command = dockerCli.Command("app", "image", "render", appName)
    89  	checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Expected{ExitCode: 1}).Combined(),
    90  		[]string{fmt.Sprintf("could not render %q: no such App image", appName)})
    91  }
    92  
    93  func TestRenderFormatters(t *testing.T) {
    94  	runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
    95  		cmd := info.configuredCmd
    96  
    97  		contextPath := filepath.Join("testdata", "simple")
    98  		cmd.Command = dockerCli.Command("app", "build", "--tag", "a-simple-tag", "--no-resolve-image", contextPath)
    99  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
   100  
   101  		cmd.Command = dockerCli.Command("app", "image", "render", "--formatter", "json", "a-simple-tag")
   102  		result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
   103  		golden.Assert(t, result.Stdout(), "expected-json-render.golden")
   104  
   105  		cmd.Command = dockerCli.Command("app", "image", "render", "--formatter", "yaml", "a-simple-tag")
   106  		result = icmd.RunCmd(cmd).Assert(t, icmd.Success)
   107  		golden.Assert(t, result.Stdout(), "expected-yaml-render.golden")
   108  	})
   109  }
   110  
   111  func checkFileWarning(t *testing.T, goldenFile, composeData string) {
   112  	cmd, cleanup := dockerCli.createTestCmd()
   113  	defer cleanup()
   114  
   115  	tmpDir := fs.NewDir(t, "app_input",
   116  		fs.WithFile(internal.ComposeFileName, composeData),
   117  	)
   118  	defer tmpDir.Remove()
   119  
   120  	cmd.Dir = tmpDir.Path()
   121  	cmd.Command = dockerCli.Command("app", "init", "app-test",
   122  		"--compose-file", tmpDir.Join(internal.ComposeFileName))
   123  	stdErr := icmd.RunCmd(cmd).Assert(t, icmd.Success).Stderr()
   124  	golden.Assert(t, stdErr, goldenFile)
   125  }
   126  
   127  func TestInitWarningEnvFiles(t *testing.T) {
   128  	testCases := []struct {
   129  		name    string
   130  		golden  string
   131  		compose string
   132  	}{
   133  		{
   134  			name:   "initWarningSingleEnvFileTest",
   135  			golden: "init-output-warning-single-envfile.golden",
   136  			compose: `version: "3.2"
   137  services:
   138    nginx:
   139      image: nginx:latest
   140      env_file: myenv1.env`,
   141  		},
   142  		{
   143  			name:   "initWarningMultipleEnvFilesTest",
   144  			golden: "init-output-warning-multiple-envfiles.golden",
   145  			compose: `version: "3.2"
   146  services:
   147    nginx:
   148      image: nginx:latest
   149      env_file:
   150       - myenv1.env
   151       - myenv2.env`,
   152  		},
   153  		{
   154  			name:   "initNoEnvFilesTest",
   155  			golden: "init-output-no-envfile.golden",
   156  			compose: `version: "3.2"
   157  services:
   158    nginx:
   159      image: nginx:latest`,
   160  		},
   161  	}
   162  	for _, test := range testCases {
   163  		t.Run(test.name, func(t *testing.T) {
   164  			checkFileWarning(t, test.golden, test.compose)
   165  		})
   166  	}
   167  }
   168  
   169  func TestInit(t *testing.T) {
   170  	cmd, cleanup := dockerCli.createTestCmd()
   171  	defer cleanup()
   172  
   173  	userData, _ := user.Current()
   174  	currentUser := ""
   175  	if userData != nil {
   176  		currentUser = userData.Username
   177  	}
   178  
   179  	composeData := `version: "3.2"
   180  services:
   181    nginx:
   182      image: nginx:latest
   183      command: nginx $NGINX_ARGS ${NGINX_DRY_RUN}
   184  `
   185  	meta := fmt.Sprintf(`# Version of the application
   186  version: 0.1.0
   187  # Name of the application
   188  name: app-test
   189  # A short description of the application
   190  description: 
   191  # List of application maintainers with name and email for each
   192  maintainers:
   193    - name: %s
   194      email: 
   195  `, currentUser)
   196  
   197  	envData := "# some comment\nNGINX_DRY_RUN=-t"
   198  	tmpDir := fs.NewDir(t, "app_input",
   199  		fs.WithFile(internal.ComposeFileName, composeData),
   200  		fs.WithFile(".env", envData),
   201  	)
   202  	defer tmpDir.Remove()
   203  
   204  	testAppName := "app-test"
   205  	dirName := internal.DirNameFromAppName(testAppName)
   206  
   207  	cmd.Dir = tmpDir.Path()
   208  	cmd.Command = dockerCli.Command("app",
   209  		"init", testAppName,
   210  		"--compose-file", tmpDir.Join(internal.ComposeFileName))
   211  	stdOut := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
   212  	golden.Assert(t, stdOut, "init-output.golden")
   213  
   214  	manifest := fs.Expected(
   215  		t,
   216  		fs.WithMode(0755),
   217  		fs.WithFile(internal.MetadataFileName, meta, fs.WithMode(0644)), // too many variables, cheating
   218  		fs.WithFile(internal.ComposeFileName, composeData, fs.WithMode(0644)),
   219  		fs.WithFile(internal.ParametersFileName, "NGINX_ARGS: FILL ME\nNGINX_DRY_RUN: -t\n", fs.WithMode(0644)),
   220  	)
   221  	assert.Assert(t, fs.Equal(tmpDir.Join(dirName), manifest))
   222  
   223  	// validate metadata with JSON Schema
   224  	cmd.Command = dockerCli.Command("app", "validate", testAppName)
   225  	stdOut = icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
   226  	golden.Assert(t, stdOut, "validate-output.golden")
   227  }
   228  
   229  func TestInitWithInvalidCompose(t *testing.T) {
   230  	cmd, cleanup := dockerCli.createTestCmd()
   231  	defer cleanup()
   232  	composePath := filepath.Join("testdata", "invalid-compose", "docker-compose.yml")
   233  
   234  	cmd.Command = dockerCli.Command("app", "init", "invalid", "--compose-file", composePath)
   235  	stdOut := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   236  		ExitCode: 1,
   237  	}).Combined()
   238  	golden.Assert(t, stdOut, "init-invalid-output.golden")
   239  }
   240  
   241  func TestInspectApp(t *testing.T) {
   242  	runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
   243  		cmd := info.configuredCmd
   244  
   245  		// cwd = e2e
   246  		dir := fs.NewDir(t, "detect-app-binary",
   247  			fs.WithDir("attachments.dockerapp", fs.FromDir("testdata/attachments.dockerapp")))
   248  		defer dir.Remove()
   249  
   250  		cmd.Command = dockerCli.Command("app", "image", "inspect")
   251  		cmd.Dir = dir.Path()
   252  		icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   253  			ExitCode: 1,
   254  			Err:      `"docker app image inspect" requires exactly 1 argument.`,
   255  		})
   256  
   257  		contextPath := filepath.Join("testdata", "simple")
   258  		cmd.Command = dockerCli.Command("app", "build", "--tag", "simple-app:1.0.0", "--no-resolve-image", contextPath)
   259  		cmd.Dir = ""
   260  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
   261  
   262  		cmd.Command = dockerCli.Command("app", "image", "inspect", "simple-app:1.0.0")
   263  		cmd.Dir = dir.Path()
   264  		output := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
   265  		golden.Assert(t, output, "app-inspect.golden")
   266  	})
   267  }
   268  
   269  func TestRunOnlyOne(t *testing.T) {
   270  	cmd, cleanup := dockerCli.createTestCmd()
   271  	defer cleanup()
   272  
   273  	cmd.Command = dockerCli.Command("app", "run")
   274  	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   275  		ExitCode: 1,
   276  		Err:      `"docker app run" requires exactly 1 argument.`,
   277  	})
   278  
   279  	cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", image.BundleFilename, "myapp")
   280  	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   281  		ExitCode: 1,
   282  		Err:      `"docker app run" cannot run a bundle and an App image`,
   283  	})
   284  }
   285  
   286  func TestRunWithLabels(t *testing.T) {
   287  	runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
   288  		cmd := info.configuredCmd
   289  
   290  		contextPath := filepath.Join("testdata", "simple")
   291  		cmd.Command = dockerCli.Command("app", "build", "--tag", "myapp", contextPath)
   292  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
   293  
   294  		cmd.Command = dockerCli.Command("app", "run", "myapp", "--name", "myapp", "--label", "label.key=labelValue")
   295  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
   296  
   297  		services := []string{
   298  			"myapp_db", "myapp_web", "myapp_api",
   299  		}
   300  		for _, service := range services {
   301  			cmd.Command = dockerCli.Command("inspect", service)
   302  			icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   303  				ExitCode: 0,
   304  				Out:      `"label.key": "labelValue"`,
   305  			})
   306  		}
   307  	})
   308  }
   309  
   310  func TestDockerAppLifecycle(t *testing.T) {
   311  	runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
   312  		cmd := info.configuredCmd
   313  		appName := strings.ToLower(strings.Replace(t.Name(), "/", "_", 1))
   314  		tmpDir := fs.NewDir(t, appName)
   315  		defer tmpDir.Remove()
   316  
   317  		cmd.Command = dockerCli.Command("app", "build", "--tag", appName, "testdata/simple")
   318  		icmd.RunCmd(cmd).Assert(t, icmd.Success)
   319  
   320  		// Install an illformed Docker Application Package
   321  		cmd.Command = dockerCli.Command("app", "run", appName, "--set", "web_port=-1", "--name", appName)
   322  		icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   323  			ExitCode: 1,
   324  			Err:      "error decoding 'Ports': Invalid hostPort: -1",
   325  		})
   326  
   327  		// List the installation and check the failed status
   328  		cmd.Command = dockerCli.Command("app", "ls")
   329  		checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
   330  			[]string{
   331  				`RUNNING APP\s+APP NAME\s+SERVICES\s+LAST ACTION\s+RESULT\s+CREATED\s+MODIFIED\s+REFERENCE`,
   332  				fmt.Sprintf(`%s\s+simple \(1.1.0-beta1\)\s+0/3\s+install\s+failure\s+.+second[s]?\sago\s+.+second[s]?\sago\s+`, appName),
   333  			})
   334  
   335  		// Upgrading a failed installation is not allowed
   336  		cmd.Command = dockerCli.Command("app", "update", appName)
   337  		icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   338  			ExitCode: 1,
   339  			Err:      fmt.Sprintf("Running App %q cannot be updated, please use 'docker app run' instead", appName),
   340  		})
   341  
   342  		// Install a Docker Application Package with an existing failed installation is fine
   343  		cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
   344  		checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), expectedAppRunOutput(appName, true))
   345  		assertAppDbLabels(t, &cmd, appName)
   346  
   347  		// List the installed application
   348  		cmd.Command = dockerCli.Command("app", "ls")
   349  		checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
   350  			[]string{
   351  				`RUNNING APP\s+APP NAME\s+SERVICES\s+LAST ACTION\s+RESULT\s+CREATED\s+MODIFIED\s+REFERENCE`,
   352  				fmt.Sprintf(`%s\s+simple \(1.1.0-beta1\)\s+\d/3\s+install\s+success\s+.+second[s]?\sago\s+.+second[s]?\sago\s+`, appName),
   353  			})
   354  
   355  		// Installing again the same application is forbidden
   356  		cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
   357  		icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   358  			ExitCode: 1,
   359  			Err:      fmt.Sprintf("Installation %q already exists, use 'docker app update' instead", appName),
   360  		})
   361  
   362  		// Update the application, changing the port
   363  		cmd.Command = dockerCli.Command("app", "update", appName, "--set", "web_port=8081")
   364  		checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
   365  			[]string{
   366  				fmt.Sprintf("Updating service %s_db", appName),
   367  				fmt.Sprintf("Updating service %s_api", appName),
   368  				fmt.Sprintf("Updating service %s_web", appName),
   369  			})
   370  		assertAppDbLabels(t, &cmd, appName)
   371  
   372  		// Uninstall the application
   373  		cmd.Command = dockerCli.Command("app", "rm", appName)
   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)...))
   400  	})
   401  }
   402  
   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  
   428  func TestCredentials(t *testing.T) {
   429  	credSet := &credentials.CredentialSet{
   430  		Name: "test-creds",
   431  		Credentials: []credentials.CredentialStrategy{
   432  			{
   433  				Name: "secret1",
   434  				Source: credentials.Source{
   435  					Value: "secret1value",
   436  				},
   437  			},
   438  			{
   439  				Name: "secret2",
   440  				Source: credentials.Source{
   441  					Value: "secret2value",
   442  				},
   443  			},
   444  		},
   445  	}
   446  	// Create a tmp dir with a credential store
   447  	cmd, cleanup := dockerCli.createTestCmd(
   448  		withCredentialSet(t, "default", credSet),
   449  	)
   450  	defer cleanup()
   451  	// Create a local credentialSet
   452  
   453  	buf, err := json.Marshal(credSet)
   454  	assert.NilError(t, err)
   455  	bundleJSON := golden.Get(t, "credential-install-bundle.json")
   456  	tmpDir := fs.NewDir(t, t.Name(),
   457  		fs.WithFile(image.BundleFilename, "", fs.WithBytes(bundleJSON)),
   458  		fs.WithDir("local",
   459  			fs.WithFile("test-creds.yaml", "", fs.WithBytes(buf)),
   460  		),
   461  	)
   462  	defer tmpDir.Remove()
   463  
   464  	bundle := tmpDir.Join(image.BundleFilename)
   465  
   466  	t.Run("missing", func(t *testing.T) {
   467  		cmd.Command = dockerCli.Command(
   468  			"app", "run",
   469  			"--credential", "secret1=foo",
   470  			// secret2 deliberately omitted.
   471  			"--credential", "secret3=baz",
   472  			"--name", "missing",
   473  			"--cnab-bundle-json", bundle,
   474  		)
   475  		result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   476  			ExitCode: 1,
   477  			Out:      icmd.None,
   478  		})
   479  		golden.Assert(t, result.Stderr(), "credential-install-missing.golden")
   480  	})
   481  
   482  	t.Run("full", func(t *testing.T) {
   483  		cmd.Command = dockerCli.Command(
   484  			"app", "run",
   485  			"--credential", "secret1=foo",
   486  			"--credential", "secret2=bar",
   487  			"--credential", "secret3=baz",
   488  			"--name", "full",
   489  			"--cnab-bundle-json", bundle,
   490  		)
   491  		result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
   492  		golden.Assert(t, result.Stdout(), "credential-install-full.golden")
   493  	})
   494  
   495  	t.Run("mixed-credstore", func(t *testing.T) {
   496  		cmd.Command = dockerCli.Command(
   497  			"app", "run",
   498  			"--credential-set", "test-creds",
   499  			"--credential", "secret3=xyzzy",
   500  			"--name", "mixed-credstore",
   501  			"--cnab-bundle-json", bundle,
   502  		)
   503  		result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
   504  		golden.Assert(t, result.Stdout(), "credential-install-mixed-credstore.golden")
   505  	})
   506  
   507  	t.Run("mixed-local-cred", func(t *testing.T) {
   508  		cmd.Command = dockerCli.Command(
   509  			"app", "run",
   510  			"--credential-set", tmpDir.Join("local", "test-creds.yaml"),
   511  			"--credential", "secret3=xyzzy",
   512  			"--name", "mixed-local-cred",
   513  			"--cnab-bundle-json", bundle,
   514  		)
   515  		result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
   516  		stdout := result.Stdout()
   517  		golden.Assert(t, stdout, "credential-install-mixed-local-cred.golden")
   518  	})
   519  
   520  	t.Run("overload", func(t *testing.T) {
   521  		cmd.Command = dockerCli.Command(
   522  			"app", "run",
   523  			"--credential-set", "test-creds",
   524  			"--credential", "secret1=overload",
   525  			"--credential", "secret3=xyzzy",
   526  			"--name", "overload",
   527  			"--cnab-bundle-json", bundle,
   528  		)
   529  		result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
   530  			ExitCode: 1,
   531  			Out:      icmd.None,
   532  		})
   533  		golden.Assert(t, result.Stderr(), "credential-install-overload.golden")
   534  	})
   535  }
   536  
   537  func assertAppDbLabels(t *testing.T, cmd *icmd.Cmd, appName string) {
   538  	cmd.Command = dockerCli.Command("inspect", fmt.Sprintf("%s_db", appName))
   539  	checkContains(t, icmd.RunCmd(*cmd).Assert(t, icmd.Success).Combined(),
   540  		[]string{
   541  			fmt.Sprintf(`"%s": "%s"`, internal.LabelAppNamespace, appName),
   542  			fmt.Sprintf(`"%s": ".+"`, internal.LabelAppVersion),
   543  		})
   544  }
   545  
   546  func checkContains(t *testing.T, combined string, expectedLines []string) {
   547  	for _, expected := range expectedLines {
   548  		exp := regexp.MustCompile(expected)
   549  		assert.Assert(t, exp.MatchString(combined), "expected %q != actual %q", expected, combined)
   550  	}
   551  }