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

Commit 8aacca2

Browse files
authored
Merge pull request #521 from jcsirot/offline-base-cnab
Provide support for using docker app offline
2 parents 137c8cd + e8e4bb3 commit 8aacca2

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)