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

Commit e8e4bb3

Browse files
author
Jean-Christophe Sirot
committed
Add the possibility to configure the base invocation image in the CLI config file.
Print invocation base image version in the version command output. The docker app version command has a new --base-invocation-image flag which prints only the name the of image. Useful in order to pull or save the image. Signed-off-by: Jean-Christophe Sirot <[email protected]>
1 parent 137c8cd commit e8e4bb3

File tree

7 files changed

+97
-22
lines changed

7 files changed

+97
-22
lines changed

e2e/base_invocation_image_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package e2e
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"testing"
7+
8+
"github.com/docker/app/internal"
9+
"github.com/docker/app/internal/packager"
10+
11+
dockerConfigFile "github.com/docker/cli/cli/config/configfile"
12+
"gotest.tools/assert"
13+
"gotest.tools/icmd"
14+
)
15+
16+
func TestBaseInvocationImageVersion(t *testing.T) {
17+
t.Run("default", func(t *testing.T) {
18+
cmd, cleanup := dockerCli.createTestCmd()
19+
defer cleanup()
20+
cmd.Command = dockerCli.Command("app", "version", "--base-invocation-image")
21+
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
22+
output := strings.TrimSpace(result.Stdout())
23+
assert.Equal(t, output, fmt.Sprintf("%s:%s", packager.DefaultCNABBaseImageName, internal.Version))
24+
})
25+
26+
t.Run("config", func(t *testing.T) {
27+
imageName := "some-base-image:some-tag"
28+
cmd, cleanup := dockerCli.createTestCmd(func(config *dockerConfigFile.ConfigFile) {
29+
config.SetPluginConfig("app", "base-invocation-image", imageName)
30+
})
31+
defer cleanup()
32+
cmd.Command = dockerCli.Command("app", "version", "--base-invocation-image")
33+
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
34+
output := strings.TrimSpace(result.Stdout())
35+
assert.Equal(t, output, imageName)
36+
})
37+
}

e2e/main_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@ type dockerCliCommand struct {
2929
cliPluginDir string
3030
}
3131

32-
func (d dockerCliCommand) createTestCmd() (icmd.Cmd, func()) {
32+
type ConfigFileOperator func(configFile *dockerConfigFile.ConfigFile)
33+
34+
func (d dockerCliCommand) createTestCmd(ops ...ConfigFileOperator) (icmd.Cmd, func()) {
3335
configDir, err := ioutil.TempDir("", "config")
3436
if err != nil {
3537
panic(err)
3638
}
3739
config := dockerConfigFile.ConfigFile{CLIPluginsExtraDirs: []string{d.cliPluginDir}}
40+
for _, op := range ops {
41+
op(&config)
42+
}
3843
configFile, err := os.Create(filepath.Join(configDir, "config.json"))
3944
if err != nil {
4045
panic(err)

internal/commands/bundle.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"io/ioutil"
99

1010
"github.com/deislabs/cnab-go/bundle"
11-
"github.com/docker/app/internal"
1211
"github.com/docker/app/internal/packager"
1312
"github.com/docker/app/types"
1413
"github.com/docker/app/types/metadata"
@@ -78,7 +77,7 @@ func makeBundleFromApp(dockerCli command.Cli, app *types.App) (*bundle.Bundle, e
7877
}
7978

8079
buildContext := bytes.NewBuffer(nil)
81-
if err := packager.PackInvocationImageContext(app, buildContext); err != nil {
80+
if err := packager.PackInvocationImageContext(dockerCli, app, buildContext); err != nil {
8281
return nil, err
8382
}
8483

@@ -94,8 +93,8 @@ func makeBundleFromApp(dockerCli command.Cli, app *types.App) (*bundle.Bundle, e
9493
if err := jsonmessage.DisplayJSONMessagesStream(buildResp.Body, ioutil.Discard, 0, false, func(jsonmessage.JSONMessage) {}); err != nil {
9594
// If the invocation image can't be found we will get an error of the form:
9695
// manifest for docker/cnab-app-base:v0.6.0-202-gbaf0b246c7 not found
97-
if err.Error() == fmt.Sprintf("manifest for %s:%s not found", packager.CNABBaseImageName, internal.Version) {
98-
return nil, fmt.Errorf("unable to resolve Docker App base image: %s:%s", packager.CNABBaseImageName, internal.Version)
96+
if err.Error() == fmt.Sprintf("manifest for %s not found", packager.BaseInvocationImage(dockerCli)) {
97+
return nil, fmt.Errorf("unable to resolve Docker App base image: %s", packager.BaseInvocationImage(dockerCli))
9998
}
10099
return nil, err
101100
}

internal/commands/version.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,33 @@ import (
44
"fmt"
55

66
"github.com/docker/app/internal"
7+
"github.com/docker/app/internal/packager"
78
"github.com/docker/cli/cli/command"
89
"github.com/spf13/cobra"
910
)
1011

1112
func versionCmd(dockerCli command.Cli) *cobra.Command {
12-
return &cobra.Command{
13+
var onlyBaseImage bool
14+
cmd := &cobra.Command{
1315
Use: "version",
1416
Short: "Print version information",
17+
Long: `Print version information
18+
19+
The --base-invocation-image will return the base invocation image name only. This can be useful for
20+
21+
docker pull $(docker app version --base-invocation-image)
22+
23+
In order to be able to build an invocation images when using docker app from an offline system.
24+
`,
1525
Run: func(cmd *cobra.Command, args []string) {
16-
fmt.Fprintln(dockerCli.Out(), internal.FullVersion())
26+
image := packager.BaseInvocationImage(dockerCli)
27+
if onlyBaseImage {
28+
fmt.Fprintln(dockerCli.Out(), image)
29+
} else {
30+
fmt.Fprintln(dockerCli.Out(), internal.FullVersion(image))
31+
}
1732
},
1833
}
34+
cmd.Flags().BoolVar(&onlyBaseImage, "base-invocation-image", false, "Print CNAB base invocation image to be used")
35+
return cmd
1936
}

internal/packager/packing.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,18 @@ import (
1010

1111
"github.com/docker/app/internal"
1212
"github.com/docker/app/types"
13+
"github.com/docker/cli/cli/command"
1314
"github.com/docker/docker/pkg/archive"
1415
"github.com/pkg/errors"
1516
)
1617

1718
const (
18-
// CNABBaseImageName is the name of the base invocation image.
19-
CNABBaseImageName = "docker/cnab-app-base"
19+
// DefaultCNABBaseImageName is the name of the default base invocation image.
20+
DefaultCNABBaseImageName = "docker/cnab-app-base"
2021

2122
dockerIgnore = "Dockerfile"
2223
)
2324

24-
var dockerFile = `FROM ` + CNABBaseImageName + `:` + internal.Version + `
25-
COPY . .`
26-
2725
func tarAdd(tarout *tar.Writer, path, file string) error {
2826
payload, err := ioutil.ReadFile(file)
2927
if err != nil {
@@ -48,7 +46,7 @@ func tarAddBytes(tarout *tar.Writer, path string, payload []byte) error {
4846
}
4947

5048
// PackInvocationImageContext creates a Docker build context for building a CNAB invocation image
51-
func PackInvocationImageContext(app *types.App, target io.Writer) error {
49+
func PackInvocationImageContext(cli command.Cli, app *types.App, target io.Writer) error {
5250
tarout := tar.NewWriter(target)
5351
defer tarout.Close()
5452
prefix := fmt.Sprintf("%s%s/", app.Metadata().Name, internal.AppExtension)
@@ -58,7 +56,7 @@ func PackInvocationImageContext(app *types.App, target io.Writer) error {
5856
if len(app.ParametersRaw()) != 1 {
5957
return errors.New("app should have one and only one parameters file")
6058
}
61-
if err := tarAddBytes(tarout, "Dockerfile", []byte(dockerFile)); err != nil {
59+
if err := tarAddBytes(tarout, "Dockerfile", []byte(dockerFile(cli))); err != nil {
6260
return errors.Wrap(err, "failed to add Dockerfile to the invocation image build context")
6361
}
6462
if err := tarAddBytes(tarout, ".dockerignore", []byte(dockerIgnore)); err != nil {
@@ -148,3 +146,18 @@ func Unpack(appname, targetDir string) error {
148146
NoLchown: true,
149147
})
150148
}
149+
150+
// BaseInvocationImage returns the name and tag of the CNAB base invocation image
151+
func BaseInvocationImage(cli command.Cli) string {
152+
img := DefaultCNABBaseImageName + `:` + internal.Version
153+
if cfg := cli.ConfigFile(); cfg != nil {
154+
if v, ok := cfg.PluginConfig("app", "base-invocation-image"); ok {
155+
return v
156+
}
157+
}
158+
return img
159+
}
160+
161+
func dockerFile(cli command.Cli) string {
162+
return fmt.Sprintf("FROM %s\nCOPY . .", BaseInvocationImage(cli))
163+
}

internal/packager/packing_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ import (
99
"testing"
1010

1111
"github.com/docker/app/types"
12+
"github.com/docker/cli/cli/command"
1213
"gotest.tools/assert"
1314
)
1415

1516
func TestPackInvocationImageContext(t *testing.T) {
1617
app, err := types.NewAppFromDefaultFiles("testdata/packages/packing.dockerapp")
1718
assert.NilError(t, err)
1819
buf := bytes.NewBuffer(nil)
19-
assert.NilError(t, PackInvocationImageContext(app, buf))
20+
dockerCli, err := command.NewDockerCli()
21+
assert.NilError(t, err)
22+
assert.NilError(t, PackInvocationImageContext(dockerCli, app, buf))
2023
assert.NilError(t, hasExpectedFiles(buf, map[string]bool{
2124
"Dockerfile": true,
2225
".dockerignore": true,

internal/version.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ var (
1919
)
2020

2121
// FullVersion returns a string of version information.
22-
func FullVersion() string {
22+
func FullVersion(invocationBaseImage string) string {
2323
res := []string{
24-
fmt.Sprintf("Version: %s", Version),
25-
fmt.Sprintf("Git commit: %s", GitCommit),
26-
fmt.Sprintf("Built: %s", reformatDate(BuildTime)),
27-
fmt.Sprintf("OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH),
28-
fmt.Sprintf("Experimental: %s", Experimental),
29-
fmt.Sprintf("Renderers: %s", strings.Join(renderer.Drivers(), ", ")),
24+
fmt.Sprintf("Version: %s", Version),
25+
fmt.Sprintf("Git commit: %s", GitCommit),
26+
fmt.Sprintf("Built: %s", reformatDate(BuildTime)),
27+
fmt.Sprintf("OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH),
28+
fmt.Sprintf("Experimental: %s", Experimental),
29+
fmt.Sprintf("Renderers: %s", strings.Join(renderer.Drivers(), ", ")),
30+
fmt.Sprintf("Invocation Base Image: %s", invocationBaseImage),
3031
}
3132
return strings.Join(res, "\n")
3233
}

0 commit comments

Comments
 (0)