Skip to content

Commit

Permalink
imagetools: use docker/cli formatting
Browse files Browse the repository at this point in the history
Use the formatting packages provided by docker/cli to comply with the official
formatting documentation.

https://docs.docker.com/config/formatting/

The use of the official formatting packages also fixes the format templating.

Fixes: #1175
  • Loading branch information
uhthomas committed Jun 14, 2023
1 parent 687feca commit 58baf9e
Show file tree
Hide file tree
Showing 46 changed files with 13,069 additions and 310 deletions.
191 changes: 191 additions & 0 deletions commands/imagetools/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
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{}, 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

0 comments on commit 58baf9e

Please sign in to comment.