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

Commit

Permalink
Support get local image by containerd
Browse files Browse the repository at this point in the history
  • Loading branch information
mowangdk committed Dec 30, 2021
1 parent 78ffd90 commit 28d03c5
Show file tree
Hide file tree
Showing 11 changed files with 484 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.idea
.vscode
main

vendor
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516
github.com/aquasecurity/tfsec v0.63.1
github.com/aws/aws-sdk-go v1.42.0
github.com/containerd/containerd v1.5.2
github.com/docker/docker v20.10.10+incompatible
github.com/docker/go-connections v0.4.0
github.com/go-git/go-git/v5 v5.4.2
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
Expand All @@ -348,6 +349,7 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
Expand Down Expand Up @@ -443,6 +445,7 @@ github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avu
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
Expand Down Expand Up @@ -576,6 +579,7 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
Expand Down Expand Up @@ -968,6 +972,7 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/buildkit v0.8.1 h1:zrGxLwffKM8nVxBvaJa7H404eQLfqlg1GB6YVIzXVQ0=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
Expand Down Expand Up @@ -1061,9 +1066,11 @@ github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d h1:pNa8metDkwZjb9g4T8s+krQ+HRgZAkqnXml+wNir/+s=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
Expand Down Expand Up @@ -1135,6 +1142,7 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
Expand Down Expand Up @@ -1300,6 +1308,7 @@ github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
Expand Down
11 changes: 11 additions & 0 deletions image/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ func tryPodmanDaemon(ref string) (types.Image, func(), error) {
}, cleanup, nil
}

func tryContainerdDaemon(imageName string, ref name.Reference) (types.Image, func(), error) {
img, cleanup, err := daemon.ContainerdImage(ref)
if err != nil {
return nil, nil, err
}
return daemonImage{
Image: img,
name: imageName,
}, cleanup, nil
}

type daemonImage struct {
daemon.Image
name string
Expand Down
265 changes: 265 additions & 0 deletions image/daemon/containerd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
package daemon

import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"time"

"github.com/containerd/containerd"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/namespaces"
"github.com/docker/docker/api/types/container"
"github.com/google/go-containerregistry/pkg/name"
"golang.org/x/xerrors"

api "github.com/docker/docker/api/types"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

const (
containerdNamespace = "default"
defaultContainerdSocket = "/run/containerd/containerd.sock"
tagTemplate = "%s:%s"
digestTemplate = "%s@sha256:%s"
)

type ImageReference struct {
Named string
Tag string
Digest string
}

type containerdClient struct {
client *containerd.Client
}

func NewContainerdClient(socket string) (containerdClient, error) {
cli, err := containerd.New(socket)
if err != nil {
return containerdClient{}, err
}
return containerdClient{client: cli}, nil
}

// ContainerdImage implements v1.Image by extending
func ContainerdImage(ref name.Reference) (Image, func(), error) {
ctx := context.Background()
cleanup := func() {}

cli, err := NewContainerdClient(defaultContainerdSocket)
if err != nil {
return nil, cleanup, xerrors.Errorf("ContainerImage: failed to initialize a docker client: %w", err)
}
defer func() {
if err != nil {
cli.client.Close()
}
}()

ctx = namespaces.WithNamespace(ctx, containerdNamespace)
img, err := cli.client.GetImage(ctx, ref.Name())
if err != nil {
return nil, cleanup, xerrors.Errorf("ContainerImage: unable to get the image (%s): %w", ref.Name(), err)
}
inspect, err := imageInspect(ctx, img)
if err != nil {
return nil, cleanup, err
}

f, err := os.CreateTemp("", "fanal-*")
if err != nil {
return nil, cleanup, xerrors.Errorf("ContainerImage: failed to create a temporary file")
}

cleanup = func() {
cli.client.Close()
f.Close()
_ = os.Remove(f.Name())
}

return &image{
opener: imageOpener(ctx, ref.Name(), f, cli.imageWriter),
inspect: inspect,
}, cleanup, nil
}

func (c *containerdClient) imageWriter(ctx context.Context, ref []string) (io.ReadCloser, error) {
if len(ref) == 0 {
return nil, xerrors.Errorf("imageWriter: failed to get iamge name: %v", ref)
}
img, err := c.client.GetImage(ctx, ref[0])
if err != nil {
return nil, err
}
pr, pw := io.Pipe()
go func() {
pw.CloseWithError(archive.Export(ctx, img.ContentStore(), pw))
}()
if err != nil {
return nil, err
}
return pr, nil
}

// imageInspect returns ImageInspect struct
func imageInspect(ctx context.Context, img containerd.Image) (inspect api.ImageInspect, err error) {
descriptor, err := img.Config(ctx)
if err != nil {
return api.ImageInspect{}, err
}
ociImage, err := containerToOci(ctx, img)
if err != nil {
return api.ImageInspect{}, err
}
var createAt string
if ociImage.Created != nil {
createAt = ociImage.Created.Format(time.RFC3339Nano)
}
repoDigests, repoTags := getRepoInfo(ctx, img)
var architecture string
if descriptor.Platform != nil {
architecture = descriptor.Platform.Architecture
} else {
architecture = ociImage.Architecture
}

return api.ImageInspect{
Architecture: architecture,
Config: getImageInfoConfigFromOciImage(ociImage),
Created: createAt,
ID: string(descriptor.Digest),
Os: ociImage.OS,
RepoDigests: repoDigests,
RepoTags: repoTags,
RootFS: api.RootFS{
Type: ociImage.RootFS.Type,
Layers: digestToString(ociImage.RootFS.DiffIDs),
},
Size: descriptor.Size,
}, nil
}

func digestToString(digests []digest.Digest) []string {
strs := make([]string, 0, len(digests))
for _, d := range digests {
strs = append(strs, d.String())
}
return strs
}

// getImageInfoConfigFromOciImage returns config of ImageConfig from oci image.
func getImageInfoConfigFromOciImage(img ocispec.Image) *container.Config {
volumes := make(map[string]struct{})
for k, obj := range img.Config.Volumes {
volumes[k] = obj
}

return &container.Config{
User: img.Config.User,
Env: img.Config.Env,
Entrypoint: img.Config.Entrypoint,
Cmd: img.Config.Cmd,
WorkingDir: img.Config.WorkingDir,
Labels: img.Config.Labels,
StopSignal: img.Config.StopSignal,
Volumes: volumes,
}
}

func containerToOci(ctx context.Context, img containerd.Image) (ocispec.Image, error) {
var ociImage ocispec.Image

cfg, err := img.Config(ctx)
if err != nil {
return ocispec.Image{}, err
}

switch cfg.MediaType {
case ocispec.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config, "application/octet-stream":
data, err := content.ReadBlob(ctx, img.ContentStore(), cfg)
if err != nil {
return ocispec.Image{}, err
}
err = json.Unmarshal(data, &ociImage)
if err != nil {
return ocispec.Image{}, err
}
default:
return ocispec.Image{}, xerrors.Errorf("containerToOci: invalid image config media type: %v", cfg.MediaType)
}
return ociImage, nil
}

// splitReference splits reference into name, tag and digest in string format.
func splitReference(ref string) (name string, tag string, digStr string) {
name = ref

if loc := regDigest.FindStringIndex(name); loc != nil {
name, digStr = name[:loc[0]], name[loc[0]+1:]
}

if loc := regTag.FindStringIndex(name); loc != nil {
name, tag = name[:loc[0]], name[loc[0]+1:]
}
return
}

// Parse parses ref into.
func Parse(ctx context.Context, img containerd.Image) (ImageReference, error) {
ref := img.Name()
if ok := regRef.MatchString(ref); !ok {
return ImageReference{}, xerrors.Errorf("Parse: invalid reference: %s", ref)
}

name, tag, digStr := splitReference(ref)

if digStr != "" {
dig, err := digest.Parse(digStr)
if err != nil {
return ImageReference{}, err
}

return ImageReference{
Named: name,
Digest: dig.String(),
Tag: tag,
}, nil
}

desp, err := img.Config(ctx)
if err != nil {
return ImageReference{}, xerrors.Errorf("Parse: get img config err: %v", err)
}
if desp.Digest != "" {
return ImageReference{
Named: name,
Digest: desp.Digest.String(),
Tag: tag,
}, nil
}

return ImageReference{
Named: name,
Tag: tag,
}, nil
}

func getRepoInfo(ctx context.Context, img containerd.Image) (repoDigests, repoTags []string) {

fmt.Printf("imageInspect name: %+v; \n", img.Name())
reference, _ := Parse(ctx, img)
fmt.Printf("imageInspect referenece: %+v; \n", reference)
if reference.Tag != "" {
repoTags = append(repoTags, fmt.Sprintf(tagTemplate, reference.Named, reference.Tag))
}
if reference.Digest != "" {
repoDigests = append(repoDigests, fmt.Sprintf(digestTemplate, reference.Named, reference.Digest))
}
return
}
Loading

0 comments on commit 28d03c5

Please sign in to comment.