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

Add containerd support #1793

Merged
merged 8 commits into from
Sep 18, 2023
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Sources can be explicitly provided with a scheme:
```
docker:yourrepo/yourimage:tag use images from the Docker daemon
podman:yourrepo/yourimage:tag use images from the Podman daemon
containerd:yourrepo/yourimage:tag use images from the Containerd daemon
docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
Expand Down Expand Up @@ -671,7 +672,7 @@ source:
# the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
digests: ["sha256"]

# options when pulling directly from a registry via the "registry:" scheme
# options when pulling directly from a registry via the "registry:" or "containerd:" scheme
registry:
# skip TLS verification when communicating with the registry
# SYFT_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var
Expand Down
190 changes: 190 additions & 0 deletions cmd/syft/cli/ui/handle_pull_containerd_image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package ui

import (
"fmt"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/dustin/go-humanize"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"

"github.com/anchore/bubbly/bubbles/taskprogress"
stereoscopeParsers "github.com/anchore/stereoscope/pkg/event/parsers"
"github.com/anchore/stereoscope/pkg/image/containerd"
"github.com/anchore/syft/internal/log"
)

var _ interface {
progress.Stager
progress.Progressable
} = (*containerdPullProgressAdapter)(nil)

type containerdPullStatus interface {
Complete() bool
Layers() []containerd.LayerID
Current(containerd.LayerID) progress.Progressable
}

type containerdPullProgressAdapter struct {
status containerdPullStatus
formatter containerdPullStatusFormatter
}

type containerdPullStatusFormatter struct {
auxInfoStyle lipgloss.Style
pullCompletedStyle lipgloss.Style
pullDownloadStyle lipgloss.Style
pullStageChars []string
layerCaps []string
}

func (m *Handler) handlePullContainerdImage(e partybus.Event) []tea.Model {
_, pullStatus, err := stereoscopeParsers.ParsePullContainerdImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
return nil
}

if pullStatus == nil {
return nil
}

tsk := m.newTaskProgress(
taskprogress.Title{
Default: "Pull image",
Running: "Pulling image",
Success: "Pulled image",
},
taskprogress.WithStagedProgressable(
newContainerdPullProgressAdapter(pullStatus),
),
)

tsk.HintStyle = lipgloss.NewStyle()
tsk.HintEndCaps = nil

return []tea.Model{tsk}
}

func newContainerdPullProgressAdapter(status *containerd.PullStatus) *containerdPullProgressAdapter {
return &containerdPullProgressAdapter{
status: status,
formatter: newContainerdPullStatusFormatter(),
}
}

func newContainerdPullStatusFormatter() containerdPullStatusFormatter {
return containerdPullStatusFormatter{
auxInfoStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
pullCompletedStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#fcba03")),
pullDownloadStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
pullStageChars: strings.Split("▁▃▄▅▆▇█", ""),
layerCaps: strings.Split("▕▏", ""),
}
}

func (d containerdPullProgressAdapter) Size() int64 {
return -1
}

func (d containerdPullProgressAdapter) Current() int64 {
return 1
}

func (d containerdPullProgressAdapter) Error() error {
if d.status.Complete() {
return progress.ErrCompleted
}
// TODO: return intermediate error indications
return nil
}

func (d containerdPullProgressAdapter) Stage() string {
return d.formatter.Render(d.status)
}

// Render crafts the given docker image pull status summarized into a single line.
func (f containerdPullStatusFormatter) Render(pullStatus containerdPullStatus) string {
var size, current uint64

layers := pullStatus.Layers()
status := make(map[containerd.LayerID]progress.Progressable)
completed := make([]string, len(layers))

// fetch the current state
for idx, layer := range layers {
completed[idx] = " "
status[layer] = pullStatus.Current(layer)
}

numCompleted := 0
for idx, layer := range layers {
prog := status[layer]
curN := prog.Current()
curSize := prog.Size()

if progress.IsCompleted(prog) {
input := f.pullStageChars[len(f.pullStageChars)-1]
completed[idx] = f.formatPullPhase(prog.Error() != nil, input)
} else if curN != 0 {
var ratio float64
switch {
case curN == 0 || curSize < 0:
ratio = 0
case curN >= curSize:
ratio = 1
default:
ratio = float64(curN) / float64(curSize)
}

i := int(ratio * float64(len(f.pullStageChars)-1))
input := f.pullStageChars[i]
completed[idx] = f.formatPullPhase(status[layer].Error() != nil, input)
}

if progress.IsErrCompleted(status[layer].Error()) {
numCompleted++
}
}

for _, layer := range layers {
prog := status[layer]
size += uint64(prog.Size())
current += uint64(prog.Current())
}

var progStr, auxInfo string
if len(layers) > 0 {
render := strings.Join(completed, "")
prefix := f.pullCompletedStyle.Render(fmt.Sprintf("%d Layers", len(layers)))
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size)))
if len(layers) == numCompleted {
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s] Extracting...", humanize.Bytes(size)))
}

progStr = fmt.Sprintf("%s%s%s%s", prefix, f.layerCap(false), render, f.layerCap(true))
}

return progStr + auxInfo
}

// formatPullPhase returns a single character that represents the status of a layer pull.
func (f containerdPullStatusFormatter) formatPullPhase(completed bool, inputStr string) string {
if completed {
return f.pullCompletedStyle.Render(f.pullStageChars[len(f.pullStageChars)-1])
}
return f.pullDownloadStyle.Render(inputStr)
}

func (f containerdPullStatusFormatter) layerCap(end bool) string {
l := len(f.layerCaps)
if l == 0 {
return ""
}
if end {
return f.layerCaps[l-1]
}
return f.layerCaps[0]
}
1 change: 1 addition & 0 deletions cmd/syft/cli/ui/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func New(cfg HandlerConfig) *Handler {
// register all supported event types with the respective handler functions
d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{
stereoscopeEvent.PullDockerImage: h.handlePullDockerImage,
stereoscopeEvent.PullContainerdImage: h.handlePullContainerdImage,
stereoscopeEvent.ReadImage: h.handleReadImage,
stereoscopeEvent.FetchImage: h.handleFetchImage,
syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted,
Expand Down
39 changes: 30 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ require (
github.com/Masterminds/sprig/v3 v3.2.3
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acobaugh/osrelease v0.1.0
github.com/adrg/xdg v0.4.0 // indirect
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe
Expand All @@ -17,10 +16,9 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b
github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/charmbracelet/bubbletea v0.24.2
Expand All @@ -47,7 +45,6 @@ require (
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/archiver/v3 v3.5.1
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
github.com/mitchellh/go-homedir v1.1.0
Expand All @@ -56,27 +53,22 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/opencontainers/go-digest v1.0.0
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1 // indirect
github.com/saferwall/pe v1.4.5
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/sassoftware/go-rpmutils v0.2.0
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.3.1
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spdx/tools-golang v0.5.3
github.com/spf13/afero v1.9.5
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/stretchr/testify v1.8.4
github.com/vbatts/go-mtree v0.5.3
github.com/vifraa/gopom v1.0.0
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
golang.org/x/mod v0.12.0
golang.org/x/net v0.15.0
Expand All @@ -87,28 +79,39 @@ require (

require (
dario.cat/mergo v1.0.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/adrg/xdg v0.4.0 // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/charmbracelet/bubbles v0.16.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/containerd/ttrpc v1.2.1 // indirect
github.com/containerd/typeurl/v2 v2.1.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v24.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
Expand All @@ -119,6 +122,8 @@ require (
github.com/gkampitakis/ciinfo v0.2.5 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
Expand All @@ -145,27 +150,39 @@ require (
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.16.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/sylabs/sif/v2 v2.11.5 // indirect
github.com/sylabs/squashfs v0.6.1 // indirect
Expand All @@ -182,6 +199,10 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
Expand Down
Loading
Loading