Skip to content

Commit

Permalink
image inspect fixes & cleanup
Browse files Browse the repository at this point in the history
Signed-off-by: apostasie <spam_blackhole@farcloser.world>
  • Loading branch information
apostasie committed May 15, 2024
1 parent 4fff6b2 commit 3ff5a90
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 67 deletions.
95 changes: 62 additions & 33 deletions pkg/cmd/image/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,87 @@ package image
import (
"context"
"fmt"
"regexp"
"time"

"github.com/containerd/containerd"
"github.com/containerd/log"
"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/formatter"
"github.com/containerd/nerdctl/v2/pkg/idutil/imagewalker"
"github.com/containerd/nerdctl/v2/pkg/imageinspector"
"github.com/containerd/nerdctl/v2/pkg/imgutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
)

// Inspect prints detailed information of each image in `images`.
func Inspect(ctx context.Context, client *containerd.Client, images []string, options types.ImageInspectOptions) error {
f := &imageInspector{
mode: options.Mode,
func Inspect(ctx context.Context, client *containerd.Client, identifiers []string, options types.ImageInspectOptions) error {
// Verify we have a valid mode
// TODO: move this up to Cobra arg line validation
if options.Mode != "native" && options.Mode != "dockercompat" {
return fmt.Errorf("unknown mode %q", options.Mode)
}
walker := &imagewalker.ImageWalker{
Client: client,
OnFound: func(ctx context.Context, found imagewalker.Found) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

n, err := imageinspector.Inspect(ctx, client, found.Image, options.GOptions.Snapshotter)
if err != nil {
return err
}
switch f.mode {
case "native":
f.entries = append(f.entries, n)
case "dockercompat":
d, err := dockercompat.ImageFromNative(n)
objects := make(map[string]*dockercompat.Image)
var entries []interface{}

// Construct the filters
filters := []string{}
for _, identifier := range identifiers {
if canonicalRef, err := referenceutil.ParseAny(identifier); err == nil {
filters = append(filters, fmt.Sprintf("name==%s", canonicalRef.String()))
}
filters = append(filters,
fmt.Sprintf("name==%s", identifier),
fmt.Sprintf("target.digest~=^sha256:%s.*$", regexp.QuoteMeta(identifier)),
fmt.Sprintf("target.digest~=^%s.*$", regexp.QuoteMeta(identifier)),
)
}

// Set a timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// Query containerd image service to retrieve a slice of containerd images
images, err := client.ImageService().List(ctx, filters...)
if err != nil {
return fmt.Errorf("image inspect errored while trying to query containerd ImageService: %w", err)
}

// Iterate over the results
for _, image := range images {
// Query each image
nativeImage, err := imageinspector.Inspect(ctx, client, image, options.GOptions.Snapshotter)
if err != nil {
return fmt.Errorf("image inspect errored while trying to inspect image %s: %w", image.Name, err)
}
switch options.Mode {
case "native":
// If native, add the entry as-is
entries = append(entries, nativeImage)
case "dockercompat":
// If docker compat, possibly coalesce entries. First, get the image digest
newDigest := nativeImage.ImageConfigDesc.Digest.String()
// If we do not know about this yet, add it
if objects[newDigest] == nil {
dockerCompatImage, err := dockercompat.ImageFromNative(nativeImage)
if err != nil {
return err
return fmt.Errorf("image inspect failed to marshall native image: %w", err)
}
f.entries = append(f.entries, d)
default:
return fmt.Errorf("unknown mode %q", f.mode)
objects[newDigest] = dockerCompatImage
entries = append(entries, dockerCompatImage)
} else {
// If we do know about it, add the RepoTags to the existing entry
repository, tag := imgutil.ParseRepoTag(nativeImage.Image.Name)
objects[newDigest].RepoTags = append(objects[newDigest].RepoTags, fmt.Sprintf("%s:%s", repository, tag))
}
return nil
},
}
}

err := walker.WalkAll(ctx, images, true)
if len(f.entries) > 0 {
if formatErr := formatter.FormatSlice(options.Format, options.Stdout, f.entries); formatErr != nil {
if len(entries) > 0 {
if formatErr := formatter.FormatSlice(options.Format, options.Stdout, entries); formatErr != nil {
log.G(ctx).Error(formatErr)
}
}
return err
}

type imageInspector struct {
mode string
entries []interface{}
return nil
}
69 changes: 35 additions & 34 deletions pkg/inspecttypes/dockercompat/dockercompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import (

"github.com/containerd/containerd"
"github.com/containerd/containerd/runtime/restart"
gocni "github.com/containerd/go-cni"
"github.com/containerd/go-cni"
"github.com/containerd/log"
"github.com/containerd/nerdctl/v2/pkg/imgutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
Expand Down Expand Up @@ -294,48 +294,49 @@ func ContainerFromNative(n *native.Container) (*Container, error) {
return c, nil
}

func ImageFromNative(n *native.Image) (*Image, error) {
i := &Image{}

imgoci := n.ImageConfig
func ImageFromNative(nativeImage *native.Image) (*Image, error) {
imgOCI := nativeImage.ImageConfig
repository, tag := imgutil.ParseRepoTag(nativeImage.Image.Name)

image := &Image{
// Docker ID (digest of platform-specific config), not containerd ID (digest of multi-platform index or manifest)
ID: nativeImage.ImageConfigDesc.Digest.String(),
// Parent: nativeImage.Image.Labels["org.mobyproject.image.parent"],
Architecture: imgOCI.Architecture,
Os: imgOCI.OS,
Size: nativeImage.Size,
RepoTags: []string{fmt.Sprintf("%s:%s", repository, tag)},
RepoDigests: []string{fmt.Sprintf("%s@%s", repository, nativeImage.Image.Target.Digest.String())},
}

i.RootFS.Type = imgoci.RootFS.Type
diffIDs := imgoci.RootFS.DiffIDs
for _, d := range diffIDs {
i.RootFS.Layers = append(i.RootFS.Layers, d.String())
if len(imgOCI.History) > 0 {
image.Comment = imgOCI.History[len(imgOCI.History)-1].Comment
image.Created = imgOCI.History[len(imgOCI.History)-1].Created.Format(time.RFC3339Nano)
image.Author = imgOCI.History[len(imgOCI.History)-1].Author
}
if len(imgoci.History) > 0 {
i.Comment = imgoci.History[len(imgoci.History)-1].Comment
i.Created = imgoci.History[len(imgoci.History)-1].Created.Format(time.RFC3339Nano)
i.Author = imgoci.History[len(imgoci.History)-1].Author

image.RootFS.Type = imgOCI.RootFS.Type
for _, d := range imgOCI.RootFS.DiffIDs {
image.RootFS.Layers = append(image.RootFS.Layers, d.String())
}
i.Architecture = imgoci.Architecture
i.Os = imgoci.OS

portSet := make(nat.PortSet)
for k := range imgoci.Config.ExposedPorts {
for k := range imgOCI.Config.ExposedPorts {
portSet[nat.Port(k)] = struct{}{}
}

i.Config = &Config{
Cmd: imgoci.Config.Cmd,
Volumes: imgoci.Config.Volumes,
Env: imgoci.Config.Env,
User: imgoci.Config.User,
WorkingDir: imgoci.Config.WorkingDir,
Entrypoint: imgoci.Config.Entrypoint,
Labels: imgoci.Config.Labels,
image.Config = &Config{
Cmd: imgOCI.Config.Cmd,
Volumes: imgOCI.Config.Volumes,
Env: imgOCI.Config.Env,
User: imgOCI.Config.User,
WorkingDir: imgOCI.Config.WorkingDir,
Entrypoint: imgOCI.Config.Entrypoint,
Labels: imgOCI.Config.Labels,
ExposedPorts: portSet,
}

i.ID = n.ImageConfigDesc.Digest.String() // Docker ID (digest of platform-specific config), not containerd ID (digest of multi-platform index or manifest)

repository, tag := imgutil.ParseRepoTag(n.Image.Name)

i.RepoTags = []string{fmt.Sprintf("%s:%s", repository, tag)}
i.RepoDigests = []string{fmt.Sprintf("%s@%s", repository, n.Image.Target.Digest.String())}
i.Size = n.Size
return i, nil
return image, nil
}

// mountsFromNative only filters bind mount to transform from native container.
Expand Down Expand Up @@ -411,7 +412,7 @@ func networkSettingsFromNative(n *native.NetNS, sp *specs.Spec) (*NetworkSetting
res.Networks[fakeDockerNetworkName] = nes

if portsLabel, ok := sp.Annotations[labels.Ports]; ok {
var ports []gocni.PortMapping
var ports []cni.PortMapping
err := json.Unmarshal([]byte(portsLabel), &ports)
if err != nil {
return nil, err
Expand All @@ -437,7 +438,7 @@ func networkSettingsFromNative(n *native.NetNS, sp *specs.Spec) (*NetworkSetting
return res, nil
}

func convertToNatPort(portMappings []gocni.PortMapping) (*nat.PortMap, error) {
func convertToNatPort(portMappings []cni.PortMapping) (*nat.PortMap, error) {
portMap := make(nat.PortMap)
for _, portMapping := range portMappings {
ports := []nat.PortBinding{}
Expand Down

0 comments on commit 3ff5a90

Please sign in to comment.