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

Commit ab69992

Browse files
authored
Merge pull request #699 from rumpl/feat-refactor
Move the inspect command to "image inspect"
2 parents e4b9653 + 2cdb76e commit ab69992

26 files changed

+676
-610
lines changed

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pipeline {
107107
sh 'docker load -i coverage-invocation-image.tar'
108108
}
109109
ansiColor('xterm') {
110-
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run || true'
110+
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run'
111111
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-results'
112112
}
113113
archiveArtifacts '_build/ci-cov/all.out'

Jenkinsfile.baguette

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pipeline {
129129
sh 'docker load -i coverage-invocation-image.tar'
130130
}
131131
ansiColor('xterm') {
132-
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run || true'
132+
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run'
133133
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-results'
134134
}
135135
archiveArtifacts '_build/ci-cov/all.out'

e2e/commands_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,19 @@ func TestInspectApp(t *testing.T) {
158158
fs.WithDir("attachments.dockerapp", fs.FromDir("testdata/attachments.dockerapp")))
159159
defer dir.Remove()
160160

161-
cmd.Command = dockerCli.Command("app", "inspect")
161+
cmd.Command = dockerCli.Command("app", "image", "inspect")
162162
cmd.Dir = dir.Path()
163163
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
164164
ExitCode: 1,
165-
Err: `"docker app inspect" requires exactly 1 argument.`,
165+
Err: `"docker app image inspect" requires exactly 1 argument.`,
166166
})
167167

168168
contextPath := filepath.Join("testdata", "simple")
169169
cmd.Command = dockerCli.Command("app", "build", "--tag", "simple-app:1.0.0", contextPath)
170170
cmd.Dir = ""
171171
icmd.RunCmd(cmd).Assert(t, icmd.Success)
172172

173-
cmd.Command = dockerCli.Command("app", "inspect", "simple-app:1.0.0")
173+
cmd.Command = dockerCli.Command("app", "image", "inspect", "simple-app:1.0.0")
174174
cmd.Dir = dir.Path()
175175
output := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
176176
golden.Assert(t, output, "app-inspect.golden")

e2e/testdata/plugin-usage.golden

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ Management Commands:
1212
Commands:
1313
build Build service images for the application
1414
init Initialize Docker Application definition
15-
inspect Shows metadata, parameters and a summary of the Compose file for a given application
1615
ls List the installations and their last known installation result
1716
pull Pull an application package from a registry
1817
push Push an application package to a registry

internal/cnab/cnab.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package cnab
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"strings"
9+
10+
"github.com/deislabs/cnab-go/bundle"
11+
"github.com/docker/app/internal"
12+
"github.com/docker/app/internal/log"
13+
"github.com/docker/app/internal/packager"
14+
"github.com/docker/app/internal/store"
15+
appstore "github.com/docker/app/internal/store"
16+
"github.com/docker/cli/cli/command"
17+
"github.com/docker/cnab-to-oci/remotes"
18+
"github.com/docker/distribution/reference"
19+
)
20+
21+
type nameKind uint
22+
23+
const (
24+
_ nameKind = iota
25+
nameKindEmpty
26+
nameKindFile
27+
nameKindDir
28+
nameKindReference
29+
)
30+
31+
func getAppNameKind(name string) (string, nameKind) {
32+
if name == "" {
33+
return name, nameKindEmpty
34+
}
35+
// name can be a bundle.json or bundle.cnab file, or a dockerapp directory
36+
st, err := os.Stat(name)
37+
if os.IsNotExist(err) {
38+
// try with .dockerapp extension
39+
st, err = os.Stat(name + internal.AppExtension)
40+
if err == nil {
41+
name += internal.AppExtension
42+
}
43+
}
44+
if err != nil {
45+
return name, nameKindReference
46+
}
47+
if st.IsDir() {
48+
return name, nameKindDir
49+
}
50+
return name, nameKindFile
51+
}
52+
53+
func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.Bundle, string, error) {
54+
app, err := packager.Extract(name)
55+
if err != nil {
56+
return nil, "", err
57+
}
58+
defer app.Cleanup()
59+
bndl, err := packager.MakeBundleFromApp(dockerCli, app, nil)
60+
return bndl, "", err
61+
}
62+
63+
func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
64+
b := &bundle.Bundle{}
65+
data, err := ioutil.ReadFile(filename)
66+
if err != nil {
67+
return b, err
68+
}
69+
return bundle.Unmarshal(data)
70+
}
71+
72+
// ResolveBundle looks for a CNAB bundle which can be in a Docker App Package format or
73+
// a bundle stored locally or in the bundle store. It returns a built or found bundle,
74+
// a reference to the bundle if it is found in the bundlestore, and an error.
75+
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, string, error) {
76+
// resolution logic:
77+
// - if there is a docker-app package in working directory, or an http:// / https:// prefix, use packager.Extract result
78+
// - the name has a .json or .cnab extension and refers to an existing file or web resource: load the bundle
79+
// - name matches a bundle name:version stored in the bundle store: use it
80+
// - pull the bundle from the registry and add it to the bundle store
81+
name, kind := getAppNameKind(name)
82+
switch kind {
83+
case nameKindFile:
84+
if strings.HasSuffix(name, internal.AppExtension) {
85+
return extractAndLoadAppBasedBundle(dockerCli, name)
86+
}
87+
bndl, err := loadBundleFromFile(name)
88+
return bndl, "", err
89+
case nameKindDir, nameKindEmpty:
90+
return extractAndLoadAppBasedBundle(dockerCli, name)
91+
case nameKindReference:
92+
bndl, tagRef, err := GetBundle(dockerCli, bundleStore, name)
93+
if err != nil {
94+
return nil, "", err
95+
}
96+
return bndl, tagRef.String(), err
97+
}
98+
return nil, "", fmt.Errorf("could not resolve bundle %q", name)
99+
}
100+
101+
// GetBundle searches for the bundle locally and tries to pull it if not found
102+
func GetBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, reference.Reference, error) {
103+
ref, err := store.StringToRef(name)
104+
if err != nil {
105+
return nil, nil, err
106+
}
107+
bndl, err := bundleStore.Read(ref)
108+
if err != nil {
109+
fmt.Fprintf(dockerCli.Err(), "Unable to find application image %q locally\n", reference.FamiliarString(ref))
110+
111+
fmt.Fprintf(dockerCli.Out(), "Pulling from registry...\n")
112+
if named, ok := ref.(reference.Named); ok {
113+
bndl, err = PullBundle(dockerCli, bundleStore, named)
114+
if err != nil {
115+
return nil, nil, err
116+
}
117+
}
118+
}
119+
120+
return bndl, ref, nil
121+
}
122+
123+
// PullBundle pulls the bundle and stores it into the bundle store
124+
func PullBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, tagRef reference.Named) (*bundle.Bundle, error) {
125+
insecureRegistries, err := internal.InsecureRegistriesFromEngine(dockerCli)
126+
if err != nil {
127+
return nil, fmt.Errorf("could not retrieve insecure registries: %v", err)
128+
}
129+
130+
bndl, err := remotes.Pull(log.WithLogContext(context.Background()), reference.TagNameOnly(tagRef), remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...))
131+
if err != nil {
132+
return nil, err
133+
}
134+
if _, err := bundleStore.Store(tagRef, bndl); err != nil {
135+
return nil, err
136+
}
137+
return bndl, nil
138+
}

internal/cnab/driver.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package cnab
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"os"
7+
"strings"
8+
9+
"github.com/deislabs/cnab-go/claim"
10+
"github.com/deislabs/cnab-go/driver"
11+
dockerDriver "github.com/deislabs/cnab-go/driver/docker"
12+
"github.com/docker/app/internal"
13+
"github.com/docker/cli/cli/command"
14+
"github.com/docker/cli/cli/context/docker"
15+
"github.com/docker/cli/cli/context/store"
16+
"github.com/docker/docker/api/types/container"
17+
"github.com/docker/docker/api/types/mount"
18+
)
19+
20+
// BindMount
21+
type BindMount struct {
22+
required bool
23+
endpoint string
24+
}
25+
26+
const defaultSocketPath string = "/var/run/docker.sock"
27+
28+
func RequiredClaimBindMount(c claim.Claim, targetContextName string, dockerCli command.Cli) (BindMount, error) {
29+
var specifiedOrchestrator string
30+
if rawOrchestrator, ok := c.Parameters[internal.ParameterOrchestratorName]; ok {
31+
specifiedOrchestrator = rawOrchestrator.(string)
32+
}
33+
34+
return RequiredBindMount(targetContextName, specifiedOrchestrator, dockerCli.ContextStore())
35+
}
36+
37+
// RequiredBindMount Returns the path required to bind mount when running
38+
// the invocation image.
39+
func RequiredBindMount(targetContextName string, targetOrchestrator string, s store.Store) (BindMount, error) {
40+
if targetOrchestrator == "kubernetes" {
41+
return BindMount{}, nil
42+
}
43+
44+
if targetContextName == "" {
45+
targetContextName = "default"
46+
}
47+
48+
// in case of docker desktop, we want to rewrite the context in cases where it targets the local swarm or Kubernetes
49+
s = &internal.DockerDesktopAwareStore{Store: s}
50+
51+
ctxMeta, err := s.GetMetadata(targetContextName)
52+
if err != nil {
53+
return BindMount{}, err
54+
}
55+
dockerCtx, err := command.GetDockerContext(ctxMeta)
56+
if err != nil {
57+
return BindMount{}, err
58+
}
59+
if dockerCtx.StackOrchestrator == command.OrchestratorKubernetes {
60+
return BindMount{}, nil
61+
}
62+
dockerEndpoint, err := docker.EndpointFromContext(ctxMeta)
63+
if err != nil {
64+
return BindMount{}, err
65+
}
66+
67+
host := dockerEndpoint.Host
68+
return BindMount{isDockerHostLocal(host), socketPath(host)}, nil
69+
}
70+
71+
func socketPath(host string) string {
72+
if strings.HasPrefix(host, "unix://") {
73+
return strings.TrimPrefix(host, "unix://")
74+
}
75+
76+
return defaultSocketPath
77+
}
78+
79+
func isDockerHostLocal(host string) bool {
80+
return host == "" || strings.HasPrefix(host, "unix://") || strings.HasPrefix(host, "npipe://")
81+
}
82+
83+
// PrepareDriver prepares a driver per the user's request.
84+
func PrepareDriver(dockerCli command.Cli, bindMount BindMount, stdout io.Writer) (driver.Driver, *bytes.Buffer) {
85+
d := &dockerDriver.Driver{}
86+
errBuf := bytes.NewBuffer(nil)
87+
d.SetDockerCli(dockerCli)
88+
if stdout != nil {
89+
d.SetContainerOut(stdout)
90+
}
91+
d.SetContainerErr(errBuf)
92+
if bindMount.required {
93+
d.AddConfigurationOptions(func(config *container.Config, hostConfig *container.HostConfig) error {
94+
config.User = "0:0"
95+
mounts := []mount.Mount{
96+
{
97+
Type: mount.TypeBind,
98+
Source: bindMount.endpoint,
99+
Target: bindMount.endpoint,
100+
},
101+
}
102+
hostConfig.Mounts = mounts
103+
return nil
104+
})
105+
}
106+
107+
// Load any driver-specific config out of the environment.
108+
driverCfg := map[string]string{}
109+
for env := range d.Config() {
110+
driverCfg[env] = os.Getenv(env)
111+
}
112+
d.SetConfig(driverCfg)
113+
114+
return d, errBuf
115+
}

0 commit comments

Comments
 (0)