Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imagetools: use docker/cli formatting #1882

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
199 changes: 199 additions & 0 deletions commands/imagetools/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package commands

import (
"encoding/json"
"fmt"

"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/util/appcontext"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
)

const (
tableFormat = "table {{ .Name }}\t{{ .MediaType }}\t{{ .Digest }}"
prettyTemplate = `{{- if .Name }}
Name: {{ .Name }}
{{- end }}
{{- if .MediaType }}
MediaType: {{ .MediaType }}
{{- end }}
{{- if .Digest }}
Digest: {{ .Digest }}
{{- end }}
{{- if .Manifests }}
Manifests:
{{- range $manifest := .Manifests }}
{{ if $manifest.Name }}
Name: {{ $manifest.Name }}
{{- end }}
{{- if $manifest.MediaType }}
MediaType: {{ $manifest.MediaType }}
{{- end }}
{{- if $manifest.Platform }}
Platform: {{ $manifest.Platform }}
{{- end }}
{{- if $manifest.OSVersion }}
OSVersion: {{ $manifest.OSVersion }}
{{- end }}
{{- if $manifest.OSFeatures }}
OSFeatures: {{ $manifest.OSFeatures }}
{{- end }}
{{- if $manifest.URLs }}
URLs: {{ $manifest.URLs }}
{{- end }}
{{- if $manifest.Annotations }}
{{ range $key, $value := $manifest.Annotations }}
{{ $key }}: {{ $value }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}`
)

func makeFormat(source string) formatter.Format {
switch source {
case formatter.PrettyFormatKey:
return prettyTemplate
case formatter.TableFormatKey:
return tableFormat
case formatter.RawFormatKey:
return formatter.JSONFormat
}
return formatter.Format(source)
}

func inspectFormatWrite(ctx formatter.Context, name string, opt imagetools.Opt) error {
resolver := imagetools.New(opt)
return ctx.Write(&inspectContext{
HeaderContext: formatter.HeaderContext{
Header: formatter.SubHeaderContext{
"Name": "NAME",
"MediaType": "MEDIA TYPE",
"Digest": "DIGEST",
},
},
}, func(format func(formatter.SubContext) error) error {
ref, err := imagetools.ParseRef(name)
if err != nil {
return fmt.Errorf("parse ref: %w", err)
}

dt, mfst, err := resolver.Get(appcontext.Context(), ref.String())
if err != nil {
return err
}

var index ocispecs.Index
if err := json.Unmarshal(dt, &index); err != nil {
return err
}

return format(&inspectContext{
ref: ref,
index: index,
descriptor: mfst,
resolver: resolver,
})
})
}

type inspectContext struct {
formatter.HeaderContext
ref reference.Named
index ocispecs.Index
descriptor ocispecs.Descriptor
resolver *imagetools.Resolver
}

func (ctx *inspectContext) MarshalJSON() ([]byte, error) {
return formatter.MarshalJSON(ctx)
}

func (ctx *inspectContext) Name() string {
return ctx.ref.String()
}

func (ctx *inspectContext) Manifest() manifestList {
return manifestList{
SchemaVersion: ctx.index.Versioned.SchemaVersion,
MediaType: ctx.index.MediaType,
Digest: ctx.descriptor.Digest,
Size: ctx.descriptor.Size,
Manifests: ctx.index.Manifests,
Annotations: ctx.descriptor.Annotations,
}
}

func (ctx *inspectContext) Image() (*ocispecs.Image, error) {
res, err := imagetools.
NewLoader(ctx.resolver.Resolver()).
Load(appcontext.Context(), ctx.ref.String())
if err != nil {
return nil, fmt.Errorf("load: %w", err)
}
var img *ocispecs.Image
for _, v := range res.Configs() {
img = v
break
}
return img, nil
}

type manifestList struct {
SchemaVersion int
MediaType string
Digest digest.Digest
Size int64
Manifests []ocispecs.Descriptor
Annotations map[string]string
}

// type inspectManifestContext struct {
// ref reference.Named
// descriptor ocispecs.Descriptor
// }

// func (ctx *inspectManifestContext) MarshalJSON() ([]byte, error) {
// return formatter.MarshalJSON(ctx)
// }

// func (ctx *inspectManifestContext) Name() (string, error) {
// cc, err := reference.WithDigest(ctx.ref, ctx.descriptor.Digest)
// if err != nil {
// return "", fmt.Errorf("with digest: %w", err)
// }
// return cc.String(), nil
// }

// func (ctx *inspectManifestContext) MediaType() string {
// return ctx.descriptor.MediaType
// }

// func (ctx *inspectManifestContext) Platform() *string {
// if ctx.descriptor.Platform != nil {
// s := platforms.Format(*ctx.descriptor.Platform)
// return &s
// }
// return nil
// }

// func (ctx *inspectManifestContext) OSVersion() string {
// if ctx.descriptor.Platform != nil {
// return ctx.descriptor.Platform.OSVersion
// }
// return ""
// }

// func (ctx *inspectManifestContext) OSFeatures() []string {
// if ctx.descriptor.Platform != nil {
// return ctx.descriptor.Platform.OSFeatures
// }
// return nil
// }

// func (ctx *inspectManifestContext) URLs() []string {
// return ctx.descriptor.URLs
// }
91 changes: 52 additions & 39 deletions commands/imagetools/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,79 @@ package commands

import (
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/docker/cli/cli/command/formatter"
cliflags "github.com/docker/cli/cli/flags"
"github.com/spf13/cobra"
)

type inspectOptions struct {
builder string
format string
raw bool
builderName string
format string
refs []string
}

func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
ctx := appcontext.Context()
func inspectCmd(dockerCLI command.Cli, rootOpts RootOptions) *cobra.Command {
var opts inspectOptions

if in.format != "" && in.raw {
return errors.Errorf("format and raw cannot be used together")
cmd := &cobra.Command{
Use: "inspect [OPTIONS] IMAGE [IMAGE...]",
Short: "Show detailed information on one or more images in the registry",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.builderName = *rootOpts.Builder
opts.refs = args
return runInspect(dockerCLI, opts, args[0])
},
}

b, err := builder.New(dockerCli, builder.WithName(in.builder))
if err != nil {
return err
}
imageopt, err := b.ImageOpt()
flags := cmd.Flags()
flags.StringVarP(&opts.format, "format", "f", "", cliflags.InspectFormatHelp)

return cmd
}

func runInspect(dockerCli command.Cli, opts inspectOptions, name string) error {
b, err := builder.New(dockerCli, builder.WithName(opts.builderName))
if err != nil {
return err
}

p, err := imagetools.NewPrinter(ctx, imageopt, name, in.format)
imageopt, err := b.ImageOpt()
if err != nil {
return err
}

return p.Print(in.raw, dockerCli.Out())
return inspectFormatWrite(formatter.Context{
Output: dockerCli.Out(),
Format: makeFormat(opts.format),
}, name, imageopt)
}

func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options inspectOptions
// func runInspect(dockerCLI command.Cli, opts inspectOptions, name string) error {
// b, err := builder.New(dockerCLI, builder.WithName(opts.builderName))
// if err != nil {
// return fmt.Errorf("new builder: %w", err)
// }

cmd := &cobra.Command{
Use: "inspect [OPTIONS] NAME",
Short: "Show details of an image in the registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = *rootOpts.Builder
return runInspect(dockerCli, options, args[0])
},
ValidArgsFunction: completion.Disable,
}
// imgopt, err := b.ImageOpt()
// if err != nil {
// return fmt.Errorf("image opt: %w", err)
// }

flags := cmd.Flags()

flags.StringVar(&options.format, "format", "", "Format the output using the given Go template")
flags.SetAnnotation("format", annotation.DefaultValue, []string{`"{{.Manifest}}"`})

flags.BoolVar(&options.raw, "raw", false, "Show original, unformatted JSON manifest")
// resolver := imagetools.New(imgopt)
// ctx := appcontext.Context()
// return inspect.Inspect(dockerCLI.Out(), opts.refs, opts.format, func(ref string) (interface{}, []byte, error) {
// newref, err := imagetools.ParseRef(ref)
// if err != nil {
// return nil, nil, err
// }

return cmd
}
// dt, mfst, err := resolver.Get(ctx, newref.String())
// if err != nil {
// return nil, nil, err
// }
// return mfst, dt, err
// })
// }
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ require (
github.com/klauspost/compress v1.16.3 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
Expand All @@ -133,6 +134,7 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -440,6 +442,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down
6 changes: 3 additions & 3 deletions util/imagetools/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (r *Resolver) Push(ctx context.Context, ref reference.Named, desc ocispec.D
ctx = remotes.WithMediaTypeKeyPrefix(ctx, "application/vnd.in-toto+json", "intoto")

ref = reference.TagNameOnly(ref)
p, err := r.resolver().Pusher(ctx, ref.String())
p, err := r.Resolver().Pusher(ctx, ref.String())
if err != nil {
return err
}
Expand All @@ -183,13 +183,13 @@ func (r *Resolver) Copy(ctx context.Context, src *Source, dest reference.Named)
ctx = remotes.WithMediaTypeKeyPrefix(ctx, "application/vnd.in-toto+json", "intoto")

dest = reference.TagNameOnly(dest)
p, err := r.resolver().Pusher(ctx, dest.String())
p, err := r.Resolver().Pusher(ctx, dest.String())
if err != nil {
return err
}

srcRef := reference.TagNameOnly(src.Ref)
f, err := r.resolver().Fetcher(ctx, srcRef.String())
f, err := r.Resolver().Fetcher(ctx, srcRef.String())
if err != nil {
return err
}
Expand Down
Loading