Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Move the inspect command to "image inspect" #699

Merged
merged 2 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pipeline {
sh 'docker load -i coverage-invocation-image.tar'
}
ansiColor('xterm') {
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run || true'
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run'
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-results'
}
archiveArtifacts '_build/ci-cov/all.out'
Expand Down
2 changes: 1 addition & 1 deletion Jenkinsfile.baguette
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pipeline {
sh 'docker load -i coverage-invocation-image.tar'
}
ansiColor('xterm') {
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run || true'
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-run'
sh 'make -f docker.Makefile TAG=$TAG-coverage coverage-results'
}
archiveArtifacts '_build/ci-cov/all.out'
Expand Down
6 changes: 3 additions & 3 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,19 @@ func TestInspectApp(t *testing.T) {
fs.WithDir("attachments.dockerapp", fs.FromDir("testdata/attachments.dockerapp")))
defer dir.Remove()

cmd.Command = dockerCli.Command("app", "inspect")
cmd.Command = dockerCli.Command("app", "image", "inspect")
cmd.Dir = dir.Path()
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Err: `"docker app inspect" requires exactly 1 argument.`,
Err: `"docker app image inspect" requires exactly 1 argument.`,
})

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

cmd.Command = dockerCli.Command("app", "inspect", "simple-app:1.0.0")
cmd.Command = dockerCli.Command("app", "image", "inspect", "simple-app:1.0.0")
cmd.Dir = dir.Path()
output := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
golden.Assert(t, output, "app-inspect.golden")
Expand Down
1 change: 0 additions & 1 deletion e2e/testdata/plugin-usage.golden
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Management Commands:
Commands:
build Build service images for the application
init Initialize Docker Application definition
inspect Shows metadata, parameters and a summary of the Compose file for a given application
ls List the installations and their last known installation result
pull Pull an application package from a registry
push Push an application package to a registry
Expand Down
138 changes: 138 additions & 0 deletions internal/cnab/cnab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package cnab

import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/app/internal"
"github.com/docker/app/internal/log"
"github.com/docker/app/internal/packager"
"github.com/docker/app/internal/store"
appstore "github.com/docker/app/internal/store"
"github.com/docker/cli/cli/command"
"github.com/docker/cnab-to-oci/remotes"
"github.com/docker/distribution/reference"
)

type nameKind uint

const (
_ nameKind = iota
nameKindEmpty
nameKindFile
nameKindDir
nameKindReference
)

func getAppNameKind(name string) (string, nameKind) {
if name == "" {
return name, nameKindEmpty
}
// name can be a bundle.json or bundle.cnab file, or a dockerapp directory
st, err := os.Stat(name)
if os.IsNotExist(err) {
// try with .dockerapp extension
st, err = os.Stat(name + internal.AppExtension)
if err == nil {
name += internal.AppExtension
}
}
if err != nil {
return name, nameKindReference
}
if st.IsDir() {
return name, nameKindDir
}
return name, nameKindFile
}

func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.Bundle, string, error) {
app, err := packager.Extract(name)
if err != nil {
return nil, "", err
}
defer app.Cleanup()
bndl, err := packager.MakeBundleFromApp(dockerCli, app, nil)
return bndl, "", err
}

func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
b := &bundle.Bundle{}
data, err := ioutil.ReadFile(filename)
if err != nil {
return b, err
}
return bundle.Unmarshal(data)
}

// ResolveBundle looks for a CNAB bundle which can be in a Docker App Package format or
// a bundle stored locally or in the bundle store. It returns a built or found bundle,
// a reference to the bundle if it is found in the bundlestore, and an error.
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, string, error) {
// resolution logic:
// - if there is a docker-app package in working directory, or an http:// / https:// prefix, use packager.Extract result
// - the name has a .json or .cnab extension and refers to an existing file or web resource: load the bundle
// - name matches a bundle name:version stored in the bundle store: use it
// - pull the bundle from the registry and add it to the bundle store
name, kind := getAppNameKind(name)
switch kind {
case nameKindFile:
if strings.HasSuffix(name, internal.AppExtension) {
return extractAndLoadAppBasedBundle(dockerCli, name)
}
bndl, err := loadBundleFromFile(name)
return bndl, "", err
case nameKindDir, nameKindEmpty:
return extractAndLoadAppBasedBundle(dockerCli, name)
case nameKindReference:
bndl, tagRef, err := GetBundle(dockerCli, bundleStore, name)
if err != nil {
return nil, "", err
}
return bndl, tagRef.String(), err
}
return nil, "", fmt.Errorf("could not resolve bundle %q", name)
}

// GetBundle searches for the bundle locally and tries to pull it if not found
func GetBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, reference.Reference, error) {
ref, err := store.StringToRef(name)
if err != nil {
return nil, nil, err
}
bndl, err := bundleStore.Read(ref)
if err != nil {
fmt.Fprintf(dockerCli.Err(), "Unable to find application image %q locally\n", reference.FamiliarString(ref))

fmt.Fprintf(dockerCli.Out(), "Pulling from registry...\n")
if named, ok := ref.(reference.Named); ok {
bndl, err = PullBundle(dockerCli, bundleStore, named)
if err != nil {
return nil, nil, err
}
}
}

return bndl, ref, nil
}

// PullBundle pulls the bundle and stores it into the bundle store
func PullBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, tagRef reference.Named) (*bundle.Bundle, error) {
insecureRegistries, err := internal.InsecureRegistriesFromEngine(dockerCli)
if err != nil {
return nil, fmt.Errorf("could not retrieve insecure registries: %v", err)
}

bndl, err := remotes.Pull(log.WithLogContext(context.Background()), reference.TagNameOnly(tagRef), remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...))
if err != nil {
return nil, err
}
if _, err := bundleStore.Store(tagRef, bndl); err != nil {
return nil, err
}
return bndl, nil
}
115 changes: 115 additions & 0 deletions internal/cnab/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cnab

import (
"bytes"
"io"
"os"
"strings"

"github.com/deislabs/cnab-go/claim"
"github.com/deislabs/cnab-go/driver"
dockerDriver "github.com/deislabs/cnab-go/driver/docker"
"github.com/docker/app/internal"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/docker"
"github.com/docker/cli/cli/context/store"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
)

// BindMount
type BindMount struct {
required bool
endpoint string
}

const defaultSocketPath string = "/var/run/docker.sock"

func RequiredClaimBindMount(c claim.Claim, targetContextName string, dockerCli command.Cli) (BindMount, error) {
var specifiedOrchestrator string
if rawOrchestrator, ok := c.Parameters[internal.ParameterOrchestratorName]; ok {
specifiedOrchestrator = rawOrchestrator.(string)
}

return RequiredBindMount(targetContextName, specifiedOrchestrator, dockerCli.ContextStore())
}

// RequiredBindMount Returns the path required to bind mount when running
// the invocation image.
func RequiredBindMount(targetContextName string, targetOrchestrator string, s store.Store) (BindMount, error) {
if targetOrchestrator == "kubernetes" {
return BindMount{}, nil
}

if targetContextName == "" {
targetContextName = "default"
}

// in case of docker desktop, we want to rewrite the context in cases where it targets the local swarm or Kubernetes
s = &internal.DockerDesktopAwareStore{Store: s}

ctxMeta, err := s.GetMetadata(targetContextName)
if err != nil {
return BindMount{}, err
}
dockerCtx, err := command.GetDockerContext(ctxMeta)
if err != nil {
return BindMount{}, err
}
if dockerCtx.StackOrchestrator == command.OrchestratorKubernetes {
return BindMount{}, nil
}
dockerEndpoint, err := docker.EndpointFromContext(ctxMeta)
if err != nil {
return BindMount{}, err
}

host := dockerEndpoint.Host
return BindMount{isDockerHostLocal(host), socketPath(host)}, nil
}

func socketPath(host string) string {
if strings.HasPrefix(host, "unix://") {
return strings.TrimPrefix(host, "unix://")
}

return defaultSocketPath
}

func isDockerHostLocal(host string) bool {
return host == "" || strings.HasPrefix(host, "unix://") || strings.HasPrefix(host, "npipe://")
}

// PrepareDriver prepares a driver per the user's request.
func PrepareDriver(dockerCli command.Cli, bindMount BindMount, stdout io.Writer) (driver.Driver, *bytes.Buffer) {
d := &dockerDriver.Driver{}
errBuf := bytes.NewBuffer(nil)
d.SetDockerCli(dockerCli)
if stdout != nil {
d.SetContainerOut(stdout)
}
d.SetContainerErr(errBuf)
if bindMount.required {
d.AddConfigurationOptions(func(config *container.Config, hostConfig *container.HostConfig) error {
config.User = "0:0"
mounts := []mount.Mount{
{
Type: mount.TypeBind,
Source: bindMount.endpoint,
Target: bindMount.endpoint,
},
}
hostConfig.Mounts = mounts
return nil
})
}

// Load any driver-specific config out of the environment.
driverCfg := map[string]string{}
for env := range d.Config() {
driverCfg[env] = os.Getenv(env)
}
d.SetConfig(driverCfg)

return d, errBuf
}
Loading