diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b9f6a6417..5786457d7 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -12,11 +12,16 @@ jobs: BENCHMARK_LOG_DIR: ${{ github.workspace }}/log/ BENCHMARK_RESULT_DIR: ${{ github.workspace }}/benchmark/ BENCHMARK_REGISTRY: ghcr.io - BENCHMARK_USER: stargz-containers + BENCHMARK_USER: ktock BENCHMARK_TARGETS: python:3.9 gcc:10.2.0 postgres:13.1 tomcat:10.0.0-jdk15-openjdk-buster BENCHMARK_SAMPLES_NUM: 5 BENCHMARK_PERCENTILE: 95 BENCHMARK_PERCENTILES_GRANULARITY: 25 + strategy: + fail-fast: false + max-parallel: 1 + matrix: + runtime: ["podman", "containerd"] steps: - name: Install tools run: | @@ -31,9 +36,9 @@ jobs: jq '{ location : .compute.location, vmSize : .compute.vmSize }' | \ tee ${{ env.BENCHMARK_RESULT_DIR }}/instance.json - name: Run benchmark - run: make benchmark + run: make benchmark-${{ matrix.runtime }} - uses: actions/upload-artifact@v1 if: ${{ always() }} with: - name: benchmarking-result + name: benchmarking-result-${{ matrix.runtime }} path: ${{ env.BENCHMARK_RESULT_DIR }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index bc1995f83..000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: Tests -on: [push, pull_request] - -env: - DOCKER_BUILDKIT: 1 - -jobs: - build: - runs-on: ubuntu-20.04 - name: Build - steps: - - uses: actions/checkout@v2 - - name: Build all - run: ./script/util/make.sh build -j2 - - test: - runs-on: ubuntu-20.04 - name: Test - steps: - - uses: actions/checkout@v2 - - name: Test all - run: ./script/util/make.sh test-all -j2 - - linter: - runs-on: ubuntu-20.04 - name: Linter - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '0' - - name: Run Linter - run: ./script/util/make.sh install-check-tools check - - integration: - runs-on: ubuntu-20.04 - name: Integration - strategy: - fail-fast: false - matrix: - buildargs: ["", "--build-arg=CONTAINERD_VERSION=master"] # released version & master version - builtin: ["true", "false"] - exclude: - - buildargs: "" - builtin: "true" - steps: - - name: Install htpasswd for setting up private registry - run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils - - uses: actions/checkout@v2 - - name: Run integration test - env: - DOCKER_BUILD_ARGS: ${{ matrix.buildargs }} - BUILTIN_SNAPSHOTTER: ${{ matrix.builtin }} - run: make integration - - test-optimize: - runs-on: ubuntu-20.04 - name: Optimize - strategy: - fail-fast: false - matrix: - buildargs: ["", "--build-arg=CONTAINERD_VERSION=master"] # released version & master version - steps: - - name: Install htpasswd for setting up private registry - run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils - - uses: actions/checkout@v2 - - name: Run test for optimize subcommand of ctr-remote - env: - DOCKER_BUILD_ARGS: ${{ matrix.buildargs }} - run: make test-optimize - - test-pullsecrets: - runs-on: ubuntu-20.04 - name: PullSecrets - strategy: - fail-fast: false - matrix: - buildargs: ["", "--build-arg=CONTAINERD_VERSION=master"] # released version & master version - builtin: ["true", "false"] - exclude: - - buildargs: "" - builtin: "true" - steps: - - name: Install htpasswd for setting up private registry - run: sudo apt-get update -y && sudo apt-get --no-install-recommends install -y apache2-utils - - uses: actions/checkout@v2 - - name: Run test for pulling image from private registry on Kubernetes - env: - DOCKER_BUILD_ARGS: ${{ matrix.buildargs }} - BUILTIN_SNAPSHOTTER: ${{ matrix.builtin }} - run: make test-pullsecrets - - test-cri: - runs-on: ubuntu-20.04 - name: CRIValidation - strategy: - fail-fast: false - matrix: - buildargs: ["", "--build-arg=CONTAINERD_VERSION=master"] # released version & master version - builtin: ["true", "false"] - exclude: - - buildargs: "" - builtin: "true" - steps: - - uses: actions/checkout@v2 - - name: Varidate the runtime through CRI - env: - DOCKER_BUILD_ARGS: ${{ matrix.buildargs }} - BUILTIN_SNAPSHOTTER: ${{ matrix.builtin }} - run: make test-cri - - # - # Project checks - # NOTE: Jobs for project checks commonly used in containerd projects - # See https://github.com/containerd/project-checks - # - - project: - name: Project Checks - runs-on: ubuntu-20.04 - timeout-minutes: 5 - steps: - - uses: actions/checkout@v2 - with: - path: src/github.com/containerd/stargz-snapshotter - fetch-depth: 25 - - uses: containerd/project-checks@v1 - with: - working-directory: src/github.com/containerd/stargz-snapshotter diff --git a/Dockerfile b/Dockerfile index f2dbee07c..34ed4ddbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG CONTAINERD_VERSION=v1.4.3 -ARG RUNC_VERSION=v1.0.0-rc92 +ARG CONTAINERD_VERSION=v1.5.0-beta.2 +ARG RUNC_VERSION=v1.0.0-rc93 ARG CNI_PLUGINS_VERSION=v0.9.0 ARG NERDCTL_VERSION=0.6.0 +ARG PODMAN_VERSION=2314af70bdacf75135a11b48b87dba8e461a43ea +ARG CONTAINERS_IMAGE_VERSION=1d45144111969eb7160be0fd32a82ada5f3bca7a +ARG CONTAINERS_STORAGE_VERSION=4d4212f14a5cc5256b330e08e9f4c770a14c0a04 +ARG CRUN_VERSION=0.17 +ARG CONMON_VERSION=v2.0.26 +ARG SKOPEO_VERSION=v1.2.2 # Legacy builder that doesn't support TARGETARCH should set this explicitly using --build-arg. # If TARGETARCH isn't supported by the builder, the default value is "amd64". @@ -65,6 +71,55 @@ RUN cd $GOPATH/src/github.com/containerd/stargz-snapshotter && \ PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${SNAPSHOTTER_BUILD_FLAGS} make containerd-stargz-grpc && \ PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${CTR_REMOTE_BUILD_FLAGS} make ctr-remote +# Build registry storage +FROM golang-base AS registry-storage-dev +ARG TARGETARCH +ARG GOARM +ARG SNAPSHOTTER_BUILD_FLAGS +ARG CTR_REMOTE_BUILD_FLAGS +COPY . $GOPATH/src/github.com/containerd/stargz-snapshotter +RUN cd $GOPATH/src/github.com/containerd/stargz-snapshotter && \ + PREFIX=/out/ GOARCH=${TARGETARCH:-amd64} GO_BUILD_FLAGS=${SNAPSHOTTER_BUILD_FLAGS} make registry-storage + +# Build podman +FROM golang-base AS podman-dev +ARG PODMAN_VERSION +ARG CONTAINERS_IMAGE_VERSION +ARG CONTAINERS_STORAGE_VERSION +RUN apt-get update -y && apt-get install -y libseccomp-dev libgpgme-dev && \ + git clone https://github.com/ktock/storage $GOPATH/src/github.com/containers/storage && \ + cd $GOPATH/src/github.com/containers/storage && \ + git checkout ${CONTAINERS_STORAGE_VERSION} && \ + git clone https://github.com/ktock/image $GOPATH/src/github.com/containers/image && \ + cd $GOPATH/src/github.com/containers/image && \ + git checkout ${CONTAINERS_IMAGE_VERSION} && \ + git clone https://github.com/containers/podman $GOPATH/src/github.com/containers/podman && \ + cd $GOPATH/src/github.com/containers/podman && \ + git checkout ${PODMAN_VERSION} && \ + sed -i "s/-mod=vendor//g" $GOPATH/src/github.com/containers/podman/Makefile && \ + echo "replace github.com/containers/image/v5 => /go/src/github.com/containers/image\nreplace github.com/containers/storage => /go/src/github.com/containers/storage" >> $GOPATH/src/github.com/containers/podman/go.mod && \ + make && make install PREFIX=/out/ + +# Build crun +FROM golang-base AS crun-dev +ARG CRUN_VERSION +RUN apt-get update -y && apt-get install -y make git gcc build-essential pkgconf libtool \ + libsystemd-dev libcap-dev libseccomp-dev libyajl-dev \ + go-md2man libtool autoconf python3 automake && \ + git clone -b ${CRUN_VERSION} --depth 1 \ + https://github.com/containers/crun $GOPATH/src/github.com/containers/crun && \ + cd $GOPATH/src/github.com/containers/crun && \ + ./autogen.sh && ./configure --prefix=/out/ && make && make install + +# Build conmon +FROM golang-base AS conmon-dev +ARG CONMON_VERSION +RUN apt-get update -y && apt-get install -y gcc git libc6-dev libglib2.0-dev pkg-config make && \ + git clone -b ${CONMON_VERSION} --depth 1 \ + https://github.com/containers/conmon $GOPATH/src/github.com/containers/conmon && \ + cd $GOPATH/src/github.com/containers/conmon && \ + mkdir /out/ && make && make install PREFIX=/out/ + # Binaries for release FROM scratch AS release-binaries COPY --from=snapshotter-dev /out/* / @@ -98,6 +153,28 @@ COPY --from=runc-dev /out/sbin/* /usr/local/sbin/ COPY --from=snapshotter-dev /out/ctr-remote /usr/local/bin/ RUN ln -s /usr/local/bin/ctr-remote /usr/local/bin/ctr +# Base image which contains podman with registry-storage +FROM golang-base AS podman-base +ARG TARGETARCH +ARG CNI_PLUGINS_VERSION +ARG PODMAN_VERSION +ARG SKOPEO_VERSION +RUN apt-get update -y && apt-get --no-install-recommends install -y fuse libgpgme-dev \ + iptables libyajl-dev && \ + # Make CNI plugins manipulate iptables instead of nftables + # as this test runs in a Docker container that network is configured with iptables. + # c.f. https://github.com/moby/moby/issues/26824 + update-alternatives --set iptables /usr/sbin/iptables-legacy && \ + mkdir -p /etc/containers /etc/cni/net.d /opt/cni/bin && \ + curl -L -o /etc/containers/policy.json https://raw.githubusercontent.com/containers/skopeo/${SKOPEO_VERSION}/default-policy.json && \ + curl -qsSL https://raw.githubusercontent.com/containers/podman/${PODMAN_VERSION}/cni/87-podman-bridge.conflist | tee /etc/cni/net.d/87-podman-bridge.conflist && \ + curl -Ls https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-${TARGETARCH:-amd64}-${CNI_PLUGINS_VERSION}.tgz | tar xzv -C /opt/cni/bin + +COPY --from=podman-dev /out/bin/* /usr/local/bin/ +COPY --from=crun-dev /out/bin/* /usr/local/bin/ +COPY --from=conmon-dev /out/bin/* /usr/local/bin/ +COPY --from=registry-storage-dev /out/* /usr/local/bin/ + # Image which can be used as all-in-one single node demo environment FROM snapshotter-base AS cind COPY ./script/config/ / diff --git a/Makefile b/Makefile index 6195d7bea..5aefcb436 100644 --- a/Makefile +++ b/Makefile @@ -36,13 +36,13 @@ build: $(CMD) FORCE: containerd-stargz-grpc: FORCE - GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/containerd-stargz-grpc + CGO_ENABLED=0 GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/containerd-stargz-grpc ctr-remote: FORCE - GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/ctr-remote + CGO_ENABLED=0 GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/ctr-remote registry-storage: FORCE - GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/registry-storage + CGO_ENABLED=0 GO111MODULE=$(GO111MODULE_VALUE) go build -o $(PREFIX)$@ $(GO_BUILD_FLAGS) $(GO_LD_FLAGS) -v ./cmd/registry-storage check: @echo "$@" @@ -82,9 +82,12 @@ integration: test-optimize: @./script/optimize/test.sh -benchmark: +benchmark-containerd: @./script/benchmark/test.sh +benchmark-podman: + @./script/benchmark2/test.sh + test-pullsecrets: @./script/pullsecrets/test.sh diff --git a/cmd/ctr-remote/commands/convert.go b/cmd/ctr-remote/commands/convert.go index b18489a15..31db4d4e8 100644 --- a/cmd/ctr-remote/commands/convert.go +++ b/cmd/ctr-remote/commands/convert.go @@ -28,6 +28,7 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containerd/stargz-snapshotter/estargz" estargzconvert "github.com/containerd/stargz-snapshotter/nativeconverter/estargz" + zstdchunkedconvert "github.com/containerd/stargz-snapshotter/nativeconverter/zstdchunked" "github.com/containerd/stargz-snapshotter/recorder" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -66,6 +67,11 @@ When '--all-platforms' is given all images in a manifest list must be available. Usage: "eStargz chunk size", Value: 0, }, + // zstd:chunked flags + cli.BoolFlag{ + Name: "zstdchunked", + Usage: "convert legacy tar(.gz) layers to zstd:chunked for lazy pulling. Must be used in conjunction with '--oci'", + }, // generic flags cli.BoolFlag{ Name: "uncompress", @@ -124,6 +130,19 @@ When '--all-platforms' is given all images in a manifest list must be available. if context.Bool("uncompress") { return errors.New("option --estargz conflicts with --uncompress") } + if context.Bool("zstdchunked") { + return errors.New("option --estargz conflicts with --zstdchunked") + } + } + + if context.Bool("zstdchunked") { + convertOpts = append(convertOpts, converter.WithLayerConvertFunc(zstdchunkedconvert.LayerConvertFunc())) + if !context.Bool("oci") { + return errors.New("option --zstdchunked must be used in conjunction with --oci") + } + if context.Bool("uncompress") { + return errors.New("option --zstdchunked conflicts with --uncompress") + } } if context.Bool("uncompress") { diff --git a/cmd/registry-storage/fs.go b/cmd/registry-storage/fs.go index 582349d84..a802f320a 100644 --- a/cmd/registry-storage/fs.go +++ b/cmd/registry-storage/fs.go @@ -36,6 +36,7 @@ const ( defaultLinkMode = syscall.S_IFLNK | 0400 // -r-------- defaultDirMode = syscall.S_IFDIR | 0500 // dr-x------ + poolLink = "pool" chainLink = "chain" layerLink = "diff" debugManifestLink = "manifest" @@ -57,6 +58,11 @@ var _ = (fusefs.NodeLookuper)((*rootnode)(nil)) // Lookup loads manifest and config of specified name (imgae reference) // and returns refnode of the specified name func (n *rootnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fusefs.Inode, syscall.Errno) { + switch name { + case poolLink: + return n.NewInode(ctx, + &linknode{linkname: n.pool.root()}, defaultLinkAttr(&out.Attr)), 0 + } refBytes, err := base64.StdEncoding.DecodeString(name) if err != nil { log.G(ctx).WithError(err).Debugf("failed to decode ref base64 %q", name) @@ -132,6 +138,7 @@ func (n *refnode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) ( pool: n.pool, layer: chain[len(chain)-1], chain: chain, + layers: n.manifest.Layers, refnode: n, }, defaultDirAttr(&out.Attr)), 0 } @@ -165,8 +172,9 @@ type layernode struct { fusefs.Inode pool *pool - layer ocispec.Descriptor - chain []ocispec.Descriptor + layer ocispec.Descriptor + chain []ocispec.Descriptor + layers []ocispec.Descriptor refnode *refnode } @@ -204,7 +212,7 @@ func (n *layernode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) } case layerLink: var layers []string - for _, l := range n.chain { + for _, l := range n.layers { if images.IsLayerType(l.MediaType) { layers = append(layers, l.Digest.String()) } diff --git a/cmd/registry-storage/main.go b/cmd/registry-storage/main.go index 4cdb3d678..29ba6bee2 100644 --- a/cmd/registry-storage/main.go +++ b/cmd/registry-storage/main.go @@ -19,6 +19,7 @@ package main import ( "context" "flag" + golog "log" "net/http" "os" "os/signal" @@ -92,6 +93,11 @@ func main() { ctx = log.WithLogger(context.Background(), log.L) config Config ) + // Streams log of standard lib (go-fuse uses this) into debug log + // Snapshotter should use "github.com/containerd/containerd/log" otherwize + // logs are always printed as "debug" mode. + golog.SetOutput(log.G(ctx).WriterLevel(logrus.DebugLevel)) + if mountPoint == "" { log.G(ctx).Fatalf("mount point must be specified") } diff --git a/cmd/registry-storage/pool.go b/cmd/registry-storage/pool.go index a5d40e432..140cd5a90 100644 --- a/cmd/registry-storage/pool.go +++ b/cmd/registry-storage/pool.go @@ -34,6 +34,7 @@ import ( "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" "github.com/containerd/stargz-snapshotter/estargz" + stargzfs "github.com/containerd/stargz-snapshotter/fs" "github.com/containerd/stargz-snapshotter/snapshot/types" "github.com/containers/storage/pkg/archive" digest "github.com/opencontainers/go-digest" @@ -51,6 +52,18 @@ const ( // targetImageLayersLabel is a label which contains layer digests contained in // the target image. targetImageLayersLabel = "containerd.io/snapshot/remote/stargz.layers" + + // remoteSnapshotLogKey is a key for log line, which indicates whether + // `Prepare` method successfully prepared targeting remote snapshot or not, as + // defined in the following: + // - "true" : indicates the snapshot has been successfully prepared as a + // remote snapshot + // - "false" : indicates the snapshot failed to be prepared as a remote + // snapshot + // - null : undetermined + remoteSnapshotLogKey = "remote-snapshot-prepared" + prepareSucceeded = "true" + prepareFailed = "false" ) type pool struct { @@ -63,6 +76,10 @@ type pool struct { refcounterMu sync.Mutex } +func (p *pool) root() string { + return p.path +} + func (p *pool) metadataDir(refspec reference.Spec) string { return filepath.Join(p.path, "metadata--"+colon2dash(digest.FromString(refspec.String()).String())) @@ -162,17 +179,28 @@ func (p *pool) loadChain(ctx context.Context, refspec reference.Spec, chain []oc return "", err } + commonLabels := make(map[string]string) var layers []string for _, l := range chain { if images.IsLayerType(l.MediaType) { layers = append(layers, l.Digest.String()) + if tocInfo, ok := l.Annotations[estargz.ZstdChunkedManifestInfoAnnotation]; ok { + var off, compressedSize, uncompressedSize, manifestType uint64 + if _, err := fmt.Sscanf(tocInfo, "%d:%d:%d:%d", + &off, &compressedSize, &uncompressedSize, &manifestType, + ); err == nil { + commonLabels[stargzfs.TocOffsetLabelPrefix+l.Digest.String()] = fmt.Sprintf("%d", off) + } + } } } + commonLabels[targetImageLayersLabel] = strings.Join(layers, ",") // TODO: unmount on failure chainPaths := make([]string, len(chain)) for i, l := range chain { - labels := map[string]string{ - targetImageLayersLabel: strings.Join(layers, ","), + labels := make(map[string]string) + for k, v := range commonLabels { + labels[k] = v } if l.Annotations != nil { tocDigest, ok := l.Annotations[estargz.TOCJSONDigestAnnotation] @@ -232,8 +260,11 @@ func (p *pool) loadLayer(ctx context.Context, refspec reference.Spec, dgst diges labels[targetRefLabel] = refspec.String() labels[targetDigestLabel] = dgst.String() if err := p.fs.Mount(ctx, mp, labels); err != nil { + log.G(ctx).WithField(remoteSnapshotLogKey, prepareFailed). + WithError(err).Debugf("failed to mount layer (context error: %v)", cErr) return "", errors.Wrapf(err, "failed to mount layer %q", dgst.String()) } + log.G(ctx).WithField(remoteSnapshotLogKey, prepareSucceeded).Debug("prepared remote snapshot") return mp, nil } diff --git a/estargz/estargz.go b/estargz/estargz.go index c45a7aaca..c175c1479 100644 --- a/estargz/estargz.go +++ b/estargz/estargz.go @@ -43,6 +43,7 @@ import ( "time" "github.com/containerd/stargz-snapshotter/estargz/errorutil" + "github.com/klauspost/compress/zstd" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) @@ -60,39 +61,54 @@ type Reader struct { // are split up. For a file with a single chunk, it's only // stored in m. chunks map[string][]*TOCEntry + + decompressor func(r io.Reader) (io.ReadCloser, error) } -// Open opens a stargz file for reading. -// -// Note that each entry name is normalized as the path that is relative to root. -func Open(sr *io.SectionReader) (*Reader, error) { - tocOff, footerSize, err := OpenFooter(sr) +func gzipDecompressor(r io.Reader) (io.ReadCloser, error) { + return gzip.NewReader(r) +} + +func zstdDecompressor(r io.Reader) (io.ReadCloser, error) { + decoder, err := zstd.NewReader(r) if err != nil { - return nil, errors.Wrapf(err, "error parsing footer") - } - tocTargz := make([]byte, sr.Size()-tocOff-footerSize) - if _, err := sr.ReadAt(tocTargz, tocOff); err != nil { - return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocTargz), err) + return nil, err } - zr, err := gzip.NewReader(bytes.NewReader(tocTargz)) - if err != nil { - return nil, fmt.Errorf("malformed TOC gzip header: %v", err) + return &zstdReadCloser{decoder}, nil +} + +type zstdReadCloser struct { + *zstd.Decoder +} + +func (z *zstdReadCloser) Close() error { + z.Decoder.Close() + return nil +} + +type openOpts struct { + tocOffset int64 +} + +// OpenOption is an option used during opening the layer +type OpenOption func(o *openOpts) error + +// WithTOCOffset option specifies the offset of TOC +func WithTOCOffset(tocOffset int64) OpenOption { + return func(o *openOpts) error { + o.tocOffset = tocOffset + return nil } - zr.Multistream(false) - tr := tar.NewReader(zr) - h, err := tr.Next() +} + +// Open opens a stargz file for reading. +// +// Note that each entry name is normalized as the path that is relative to root. +func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { + r, err := newReader(sr, opt...) if err != nil { - return nil, fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err) - } - if h.Name != TOCTarName { - return nil, fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName) + return nil, err } - dgstr := digest.Canonical.Digester() - toc := new(jtoc) - if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil { - return nil, fmt.Errorf("error decoding TOC JSON: %v", err) - } - r := &Reader{sr: sr, toc: toc, tocDigest: dgstr.Digest()} if err := r.initFields(); err != nil { return nil, fmt.Errorf("failed to initialize fields of entries: %v", err) } @@ -252,33 +268,69 @@ func (r *Reader) VerifyTOC(tocDigest digest.Digest) (TOCEntryVerifier, error) { if r.tocDigest != tocDigest { return nil, fmt.Errorf("invalid TOC JSON %q; want %q", r.tocDigest, tocDigest) } - digestMap := make(map[int64]digest.Digest) // map from chunk offset to the digest + + chunkDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the chunk digest + regDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the reg file digest + var chunkDigestMapIncomplete bool + var regDigestMapIncomplete bool + var containsChunk bool for _, e := range r.toc.Entries { - if e.Type == "reg" || e.Type == "chunk" { - if e.Type == "reg" && e.Size == 0 { - continue // ignores empty file - } + if e.Type != "reg" && e.Type != "chunk" { + continue + } - // offset must be unique in stargz blob - if _, ok := digestMap[e.Offset]; ok { - return nil, fmt.Errorf("offset %d found twice", e.Offset) + // offset must be unique in stargz blob + _, dOK := chunkDigestMap[e.Offset] + _, rOK := regDigestMap[e.Offset] + if dOK || rOK { + return nil, fmt.Errorf("offset %d found twice", e.Offset) + } + + if e.Type == "reg" { + if e.Size == 0 { + continue // ignores empty file } - // all chunk entries must contain digest - if e.ChunkDigest == "" { - return nil, fmt.Errorf("ChunkDigest of %q(off=%d) not found in TOC JSON", - e.Name, e.Offset) + // record the digest of regular file payload + if e.Digest != "" { + d, err := digest.Parse(e.Digest) + if err != nil { + return nil, errors.Wrapf(err, + "failed to parse regular file digest %q", e.Digest) + } + regDigestMap[e.Offset] = d + } else { + regDigestMapIncomplete = true } + } else { + containsChunk = true // this layer contains "chunk" entries. + } + // "reg" also can contain ChunkDigest (e.g. when "reg" is the first entry of + // chunked file) + if e.ChunkDigest != "" { d, err := digest.Parse(e.ChunkDigest) if err != nil { - return nil, errors.Wrapf(err, "failed to parse digest %q", e.ChunkDigest) + return nil, errors.Wrapf(err, + "failed to parse chunk digest %q", e.ChunkDigest) } - digestMap[e.Offset] = d + chunkDigestMap[e.Offset] = d + } else { + chunkDigestMapIncomplete = true } } - return &verifier{digestMap: digestMap}, nil + if chunkDigestMapIncomplete { + // Though some chunk digests are not found, if this layer doesn't contain + // "chunk"s and all digest of "reg" files are recorded, we can use them instead. + if !containsChunk && !regDigestMapIncomplete { + return &verifier{digestMap: regDigestMap}, nil + } else { + return nil, fmt.Errorf("some ChunkDigest not found in TOC JSON") + } + } + + return &verifier{digestMap: chunkDigestMap}, nil } // verifier is an implementation of TOCEntryVerifier which holds verifiers keyed by @@ -413,17 +465,17 @@ func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { off -= ent.ChunkOffset finalEnt := fr.ents[len(fr.ents)-1] - gzOff := ent.Offset - // gzBytesRemain is the number of compressed gzip bytes in this - // file remaining, over 1+ gzip chunks. - gzBytesRemain := finalEnt.NextOffset() - gzOff + compressedOff := ent.Offset + // compressedBytesRemain is the number of compressed bytes in this + // file remaining, over 1+ chunks. + compressedBytesRemain := finalEnt.NextOffset() - compressedOff - sr := io.NewSectionReader(fr.r.sr, gzOff, gzBytesRemain) + sr := io.NewSectionReader(fr.r.sr, compressedOff, compressedBytesRemain) - const maxGZread = 2 << 20 - var bufSize = maxGZread - if gzBytesRemain < maxGZread { - bufSize = int(gzBytesRemain) + const maxRead = 2 << 20 + var bufSize = maxRead + if compressedBytesRemain < maxRead { + bufSize = int(compressedBytesRemain) } br := bufio.NewReaderSize(sr, bufSize) @@ -431,14 +483,14 @@ func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { return 0, fmt.Errorf("fileReader.ReadAt.peek: %v", err) } - gz, err := gzip.NewReader(br) + dr, err := fr.r.decompressor(br) if err != nil { - return 0, fmt.Errorf("fileReader.ReadAt.gzipNewReader: %v", err) + return 0, fmt.Errorf("fileReader.ReadAt.decompressor: %v", err) } - if n, err := io.CopyN(ioutil.Discard, gz, off); n != off || err != nil { + if n, err := io.CopyN(ioutil.Discard, dr, off); n != off || err != nil { return 0, fmt.Errorf("discard of %d bytes = %v, %v", off, n, err) } - return io.ReadFull(gz, p) + return io.ReadFull(dr, p) } // A Writer writes stargz files. @@ -771,6 +823,168 @@ func parseFooter(p []byte) (tocOffset int64, footerSize int64, rErr error) { return 0, 0, errorutil.Aggregate(append(allErr, err)) } +func newReader(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { + var opts openOpts + for _, o := range opt { + if err := o(&opts); err != nil { + return nil, err + } + } + maybeTocOffset := opts.tocOffset + + var fetchSize int64 + // tocOffset > FooterSize(50) > legacyFooterSize(47) > zstdFooterSize(40) + if maybeTocOffset > 0 { + if maybeTocOffset > sr.Size() { + return nil, fmt.Errorf("blob size %d is smaller than the toc offset", sr.Size()) + } + fetchSize = sr.Size() - maybeTocOffset + } else if sr.Size() >= FooterSize { + fetchSize = FooterSize + } else if sr.Size() >= legacyFooterSize { + fetchSize = legacyFooterSize + } else if sr.Size() >= zstdFooterSize { + fetchSize = zstdFooterSize + } else { + return nil, fmt.Errorf("blob size %d is smaller than the footer size", sr.Size()) + } + + // TODO: read a bigger chunk (1MB?) at once here to hopefully + // get the TOC + footer in one go. + footer := make([]byte, fetchSize) + if _, err := sr.ReadAt(footer, sr.Size()-fetchSize); err != nil { + return nil, fmt.Errorf("error reading footer: %v", err) + } + + var allErr []error + + pad := int64(len(footer) - FooterSize) + if pad < 0 { + pad = 0 + } + tocOffset, err := parseEStargzFooter(footer[pad:]) + if err == nil { + return parseTOC(sr, tocOffset, sr.Size()-tocOffset-FooterSize, footer[:pad]) + } + allErr = append(allErr, err) + + pad = int64(len(footer) - legacyFooterSize) + if pad < 0 { + pad = 0 + } + tocOffset, err = parseLegacyFooter(footer[pad:]) + if err == nil { + return parseTOC(sr, tocOffset, sr.Size()-tocOffset-legacyFooterSize, footer[:pad]) + } + allErr = append(allErr, err) + + pad = int64(len(footer) - zstdFooterSize) + if pad < 0 { + pad = 0 + } + tocOffset, tocCompressedSize, err := parseZstdChunkedFooter(footer[pad:]) + if err == nil { + if tocCompressedSize < pad { + pad = tocCompressedSize + } + return parseZstdChunkedTOC(sr, tocOffset, tocCompressedSize, footer[:pad]) + } + + return nil, errorutil.Aggregate(append(allErr, err)) +} + +func parseTOC(sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte) (*Reader, error) { + if len(tocBytes) > 0 { + toc, tocDgst, err := parseTOCBytes(tocBytes) + if err == nil { + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: gzipDecompressor, + }, nil + } + } + tocTargz := make([]byte, tocSize) + if _, err := sr.ReadAt(tocTargz, tocOff); err != nil { + return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocTargz), err) + } + toc, tocDgst, err := parseTOCBytes(tocTargz) + if err != nil { + return nil, err + } + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: gzipDecompressor, + }, nil +} + +func parseTOCBytes(tocTargz []byte) (toc *jtoc, tocDgst digest.Digest, err error) { + zr, err := gzip.NewReader(bytes.NewReader(tocTargz)) + if err != nil { + return nil, "", fmt.Errorf("malformed TOC gzip header: %v", err) + } + zr.Multistream(false) + tr := tar.NewReader(zr) + h, err := tr.Next() + if err != nil { + return nil, "", fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err) + } + if h.Name != TOCTarName { + return nil, "", fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName) + } + dgstr := digest.Canonical.Digester() + toc = new(jtoc) + if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil { + return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err) + } + return toc, dgstr.Digest(), nil +} + +func parseZstdChunkedTOC(sr *io.SectionReader, tocOff, tocCompressedSize int64, tocBytes []byte) (*Reader, error) { + if len(tocBytes) > 0 { + toc, tocDgst, err := parseZstdChunkedBytes(tocBytes) + if err == nil { + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: zstdDecompressor, + }, nil + } + } + tocZstd := make([]byte, tocCompressedSize) + if _, err := sr.ReadAt(tocZstd, tocOff); err != nil { + return nil, fmt.Errorf("error reading %d byte TOC zstd: %v", len(tocZstd), err) + } + toc, tocDgst, err := parseZstdChunkedBytes(tocZstd) + if err != nil { + return nil, err + } + return &Reader{ + sr: sr, + toc: toc, + tocDigest: tocDgst, + decompressor: zstdDecompressor, + }, nil +} + +func parseZstdChunkedBytes(tocBytes []byte) (toc *jtoc, tocDgst digest.Digest, err error) { + zr, err := zstd.NewReader(bytes.NewReader(tocBytes)) + if err != nil { + return nil, "", err + } + defer zr.Close() + toc = new(jtoc) + if err := json.NewDecoder(zr).Decode(&toc); err != nil { + return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err) + } + tocDgst = digest.Canonical.FromBytes(tocBytes) // must be "compressed" digest for zstd:chunked + return toc, tocDgst, nil +} + func parseEStargzFooter(p []byte) (tocOffset int64, err error) { if len(p) != FooterSize { return 0, fmt.Errorf("invalid length %d cannot be parsed", len(p)) @@ -811,6 +1025,15 @@ func parseLegacyFooter(p []byte) (tocOffset int64, err error) { return strconv.ParseInt(string(extra[:16]), 16, 64) } +func parseZstdChunkedFooter(p []byte) (tocOffset int64, tocCompressedSize int64, err error) { + offset := binary.LittleEndian.Uint64(p[0:8]) + length := binary.LittleEndian.Uint64(p[8:16]) + if !bytes.Equal(zstdChunkedFrameMagic, p[32:40]) { + return 0, 0, fmt.Errorf("invalid magic number") + } + return int64(offset), int64(length), nil +} + func formatModtime(t time.Time) string { if t.IsZero() || t.Unix() == 0 { return "" diff --git a/estargz/go.mod b/estargz/go.mod index e8e01f5cc..f490fc810 100644 --- a/estargz/go.mod +++ b/estargz/go.mod @@ -3,6 +3,7 @@ module github.com/containerd/stargz-snapshotter/estargz go 1.15 require ( + github.com/klauspost/compress v1.11.9 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a diff --git a/estargz/go.sum b/estargz/go.sum index d81890710..291b721a2 100644 --- a/estargz/go.sum +++ b/estargz/go.sum @@ -1,3 +1,5 @@ +github.com/klauspost/compress v1.11.9 h1:5OCMOdde1TCT2sookEuVeEZzA8bmRSFV3AwPDZAG8AA= +github.com/klauspost/compress v1.11.9/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/estargz/types.go b/estargz/types.go index 7b05ab53f..ef91a70dc 100644 --- a/estargz/types.go +++ b/estargz/types.go @@ -81,8 +81,15 @@ const ( NoPrefetchLandmark = ".no.prefetch.landmark" landmarkContents = 0xf + + zstdFooterSize = 40 + + ZstdChunkedManifestChecksumAnnotation = "io.containers.zstd-chunked.manifest-checksum" + ZstdChunkedManifestInfoAnnotation = "io.containers.zstd-chunked.manifest-position" ) +var zstdChunkedFrameMagic = []byte{0x47, 0x6e, 0x55, 0x6c, 0x49, 0x6e, 0x55, 0x78} + // jtoc is the JSON-serialized table of contents index of the files in the stargz file. type jtoc struct { Version int `json:"version"` diff --git a/fs/fs.go b/fs/fs.go index b47955d25..0a51ba2a7 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -87,6 +87,9 @@ const ( defaultMaxConcurrency = 2 statFileMode = syscall.S_IFREG | 0400 // -r-------- stateDirMode = syscall.S_IFDIR | 0500 // dr-x------ + + // TocOffsetLabelPrefix is a label that tells the offset of TOC to the filesystem + TocOffsetLabelPrefix = "containerd.io/snapshot/stargz/toc-offset." ) type Option func(*options) @@ -218,7 +221,7 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s go func() { rErr := fmt.Errorf("failed to resolve target") for _, s := range src { - l, err := fs.resolveLayer(ctx, s.Hosts, s.Name, s.Target) + l, err := fs.resolveLayer(ctx, s.Hosts, s.Name, s.Target, labels) if err == nil { resultChan <- l return @@ -232,8 +235,14 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s // Also resolve other layers in parallel preResolve := src[0] // TODO: should we pre-resolve blobs in other sources as well? for _, desc := range preResolve.Manifest.Layers { + desc := desc if desc.Digest.String() != preResolve.Target.Digest.String() { - go fs.resolveLayer(ctx, preResolve.Hosts, preResolve.Name, desc) + // Avoids to get canceled by client. Otherwise client possibly cacnel the + // context as soon as this function returns even if the pre-resolve is + // on-going. If a mountpoint refers to this singleflight for mounting, it'll + // fail because of the cancelalation. + ctx := context.Background() + go fs.resolveLayer(ctx, preResolve.Hosts, preResolve.Name, desc, labels) } } @@ -364,10 +373,9 @@ func (fs *filesystem) Mount(ctx context.Context, mountpoint string, labels map[s return server.WaitMount() } -func (fs *filesystem) resolveLayer(ctx context.Context, hosts docker.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor) (*layer, error) { +func (fs *filesystem) resolveLayer(ctx context.Context, hosts docker.RegistryHosts, refspec reference.Spec, desc ocispec.Descriptor, labels map[string]string) (*layer, error) { name := refspec.String() + "/" + desc.Digest.String() - ctx, cancel := context.WithCancel(log.WithLogger(ctx, log.G(ctx).WithField("src", name))) - defer cancel() + ctx = log.WithLogger(ctx, log.G(ctx).WithField("src", name)) fs.resolveResultMu.Lock() c, ok := fs.resolveResult.Get(name) @@ -408,7 +416,14 @@ func (fs *filesystem) resolveLayer(ctx context.Context, hosts docker.RegistryHos defer fs.backgroundTaskManager.DonePrioritizedTask() return blob.ReadAt(p, offset) }), 0, blob.Size()) - vr, root, err := reader.NewReader(sr, fs.fsCache) + var readerOpts []reader.ReaderOption + if tocOffStr, ok := labels[TocOffsetLabelPrefix+desc.Digest.String()]; ok { + if tocOff, err := strconv.ParseInt(tocOffStr, 10, 64); err == nil { + readerOpts = append(readerOpts, + reader.WithEstargzOpenOptions(estargz.WithTOCOffset(tocOff))) + } + } + vr, root, err := reader.NewReader(sr, fs.fsCache, readerOpts...) if err != nil { log.G(ctx).WithError(err).Debugf("failed to resolve: layer cannot be read") return nil, errors.Wrap(err, "failed to read layer") diff --git a/fs/reader/reader.go b/fs/reader/reader.go index dcd6a10cf..9f6e29043 100644 --- a/fs/reader/reader.go +++ b/fs/reader/reader.go @@ -83,11 +83,30 @@ func (nv nopVerifier) Verified() bool { return true } +type ReaderOption func(o *readerOpts) error + +type readerOpts struct { + estargzOpenOpts []estargz.OpenOption +} + +func WithEstargzOpenOptions(openOpts ...estargz.OpenOption) ReaderOption { + return func(o *readerOpts) error { + o.estargzOpenOpts = openOpts + return nil + } +} + // NewReader creates a Reader based on the given stargz blob and cache implementation. // It returns VerifiableReader so the caller must provide a estargz.TOCEntryVerifier // to use for verifying file or chunk contained in this stargz blob. -func NewReader(sr *io.SectionReader, cache cache.BlobCache) (*VerifiableReader, *estargz.TOCEntry, error) { - r, err := estargz.Open(sr) +func NewReader(sr *io.SectionReader, cache cache.BlobCache, readOpts ...ReaderOption) (*VerifiableReader, *estargz.TOCEntry, error) { + var opts readerOpts + for _, o := range readOpts { + if err := o(&opts); err != nil { + return nil, nil, err + } + } + r, err := estargz.Open(sr, opts.estargzOpenOpts...) if err != nil { return nil, nil, errors.Wrap(err, "failed to parse stargz") } diff --git a/go.mod b/go.mod index 00035d2ea..7993abb38 100644 --- a/go.mod +++ b/go.mod @@ -14,22 +14,28 @@ require ( github.com/coreos/go-systemd/v22 v22.1.0 github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompatible // indirect + github.com/docker/docker-credential-helpers v0.6.3 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e - github.com/google/go-containerregistry v0.4.1 github.com/hanwen/go-fuse/v2 v2.0.4-0.20201208195215-4a458845028b github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/golang-lru v0.5.3 // indirect + github.com/kr/text v0.2.0 // indirect github.com/moby/sys/mountinfo v0.4.0 + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/onsi/ginkgo v1.12.0 // indirect + github.com/onsi/gomega v1.9.0 // indirect github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.1 - github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 + github.com/opencontainers/image-spec v1.0.2-0.20200206005212-79b036d80240 + github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d github.com/pkg/errors v0.9.1 github.com/rs/xid v1.2.1 github.com/sirupsen/logrus v1.7.0 github.com/urfave/cli v1.22.2 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 + google.golang.org/appengine v1.6.6 // indirect google.golang.org/grpc v1.35.0 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect k8s.io/api v0.20.1 k8s.io/apimachinery v0.20.1 k8s.io/client-go v0.20.1 @@ -39,6 +45,8 @@ replace ( // Import local package for estargz. github.com/containerd/stargz-snapshotter/estargz => ./estargz + github.com/containers/storage => github.com/giuseppe/storage v1.19.2-0.20210217133341-463ec672f700 + // NOTE: github.com/containerd/containerd v1.4.0 depends on github.com/urfave/cli v1.22.1 // because of https://github.com/urfave/cli/issues/1092 github.com/urfave/cli => github.com/urfave/cli v1.22.1 diff --git a/go.sum b/go.sum index a07411dbd..1f35bec81 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,6 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -53,10 +52,7 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2 github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da h1:sqPBuX6WumurdHaSRbS8xyyyP8Rf7kUpLUlJaN7rztw= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -64,7 +60,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -82,17 +77,15 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 h1:cHzBGGVew0ezFsq2grfy2RsB8hO/eNyBgOLHBCqfR1U= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0 h1:Fv93L3KKckEcEHR3oApXVzyBTDA8WAm6VXhPE00N3f8= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -156,10 +149,6 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M= github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/storage v1.24.4 h1:QJn/C/4eNbYNpxYdnIn1u4lElIB7V9IesRraLf68JjY= -github.com/containers/storage v1.24.4/go.mod h1:Y793GKrV3RVM1Jt4QejXtCJHGUPLrDvQ9LAbCyJ9OKs= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -189,19 +178,16 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 h1:2HQmlpI3yI9deH18Q6xiSOIjXD4sLI55Y/gfpa8/558= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompatible h1:UcwmC9cQLDd+vVRaZ9K06KzmMBWGTqa/lpdkoIAyjt8= github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -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= @@ -232,6 +218,8 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/giuseppe/storage v1.19.2-0.20210217133341-463ec672f700 h1:WsFKEGeK9gZq+S1mnkQdunk7Wkxu7cJcgUwgQpH8nyg= +github.com/giuseppe/storage v1.19.2-0.20210217133341-463ec672f700/go.mod h1:NXVcxyRN2sRKfMaPlyuzslA7enBu/vcU+lKg3LoWERs= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -243,15 +231,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -272,7 +256,6 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= @@ -282,7 +265,6 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -308,8 +290,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-containerregistry v0.4.1 h1:Lrcj2AOoZ7WKawsoKAh2O0dH0tBqMW2lTEmozmK4Z3k= -github.com/google/go-containerregistry v0.4.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -319,7 +299,6 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -332,15 +311,11 @@ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3i github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= @@ -357,7 +332,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -369,7 +343,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -383,8 +356,9 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.9 h1:5OCMOdde1TCT2sookEuVeEZzA8bmRSFV3AwPDZAG8AA= +github.com/klauspost/compress v1.11.9/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -400,8 +374,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= @@ -410,13 +382,13 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.11 h1:vCoR9VPpsk/TZFW2JwK5I9S0xdrtUq2bph6/YjEPnaw= +github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= @@ -431,9 +403,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -441,12 +412,10 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -454,7 +423,6 @@ github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= @@ -466,28 +434,27 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20200206005212-79b036d80240 h1:SCj6omNRmcflKljYD2u38p+NMOHylupEMEpt3OfsF8g= +github.com/opencontainers/image-spec v1.0.2-0.20200206005212-79b036d80240/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8= -github.com/opencontainers/runc v1.0.0-rc92 h1:+IczUKCRzDzFDnw99O/PAqrcBBCoRp9xN3cB1SYSNS4= github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= +github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 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.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 h1:NhsM2gc769rVWDqJvapK37r+7+CBXI8xHhnfnt8uQsg= 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.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= 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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -501,7 +468,6 @@ github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 h1:gGBSHPOU7g8YjTbh github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -511,14 +477,11 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -527,7 +490,6 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= @@ -536,7 +498,6 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -551,21 +512,15 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -576,18 +531,21 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -600,13 +558,9 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -661,7 +615,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -679,7 +632,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -691,11 +643,10 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -708,7 +659,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -730,7 +680,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -755,8 +704,6 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -764,10 +711,12 @@ golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -779,12 +728,10 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -800,7 +747,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -819,10 +765,6 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -839,7 +781,6 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -868,9 +809,7 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4 google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -885,7 +824,6 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= @@ -948,18 +886,13 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/client-go v0.20.1 h1:Qquik0xNFbK9aUG92pxHYsyfea5/RPO9o9bSywNor+M= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= @@ -968,7 +901,6 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/nativeconverter/zstdchunked/zstdchunked.go b/nativeconverter/zstdchunked/zstdchunked.go new file mode 100644 index 000000000..f5a5f0f5b --- /dev/null +++ b/nativeconverter/zstdchunked/zstdchunked.go @@ -0,0 +1,171 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package zstdchunked + +import ( + "context" + "fmt" + "io" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/images/converter" + "github.com/containerd/containerd/images/converter/uncompress" + "github.com/containerd/containerd/labels" + "github.com/containerd/stargz-snapshotter/estargz" + "github.com/containers/storage/pkg/zstdchunked" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// LayerConvertFunc converts legacy tar.gz layers into zstd:chunked layers. +// +// This changes Docker MediaType to OCI MediaType so this should be used in +// conjunction with WithDockerToOCI(). +// +// Otherwise "io.containers.zstd-chunked.manifest-checksum" annotation will be lost, +// because the Docker media type does not support layer annotations. +func LayerConvertFunc() converter.ConvertFunc { + return func(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (*ocispec.Descriptor, error) { + if !images.IsLayerType(desc.MediaType) { + // No conversion. No need to return an error here. + return nil, nil + } + uncompressedDesc := &desc + // We need to uncompress the archive first + if !uncompress.IsUncompressedType(desc.MediaType) { + var err error + uncompressedDesc, err = uncompress.LayerConvertFunc(ctx, cs, desc) + if err != nil { + return nil, err + } + if uncompressedDesc == nil { + return nil, errors.Errorf("unexpectedly got the same blob aftr compression (%s, %q)", desc.Digest, desc.MediaType) + } + defer func() { + if err := cs.Delete(ctx, uncompressedDesc.Digest); err != nil { + logrus.WithError(err).WithField("uncompressedDesc", uncompressedDesc).Warn("failed to remove tmp uncompressed layer") + } + }() + logrus.Debugf("zstdchunked: uncompressed %s into %s", desc.Digest, uncompressedDesc.Digest) + } + + info, err := cs.Info(ctx, desc.Digest) + if err != nil { + return nil, err + } + labelz := info.Labels + if labelz == nil { + labelz = make(map[string]string) + } + + ref := fmt.Sprintf("convert-zstdchunked-from-%s", desc.Digest) + w, err := cs.Writer(ctx, content.WithRef(ref)) + if err != nil { + return nil, err + } + defer w.Close() + + // Reset the writing position + // Old writer possibly remains without aborted + // (e.g. conversion interrupted by a signal) + if err := w.Truncate(0); err != nil { + return nil, err + } + + metadata := make(map[string]string) + cw := &countWriter{w, 0} + compressW, err := zstdchunked.Compressor(cw, metadata, nil) + if err != nil { + return nil, err + } + + uncompressedReaderAt, err := cs.ReaderAt(ctx, *uncompressedDesc) + if err != nil { + compressW.Close() + return nil, err + } + defer uncompressedReaderAt.Close() + uncompressedSR := io.NewSectionReader(uncompressedReaderAt, 0, uncompressedDesc.Size) + diffID := digest.Canonical.Digester() + _, err = io.Copy(compressW, io.TeeReader(uncompressedSR, diffID.Hash())) + if err != nil { + compressW.Close() + return nil, err + } + if err := compressW.Close(); err != nil { + return nil, err + } + + // update diffID label + labelz[labels.LabelUncompressed] = diffID.Digest().String() + if err = w.Commit(ctx, 0, "", content.WithLabels(labelz)); err != nil && !errdefs.IsAlreadyExists(err) { + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + newDesc := desc + if uncompress.IsUncompressedType(newDesc.MediaType) { + if images.IsDockerType(newDesc.MediaType) { + newDesc.MediaType += ".zstd" + } else { + newDesc.MediaType += "+zstd" + } + } else { + newDesc.MediaType, err = convertMediaType(newDesc.MediaType) + if err != nil { + return nil, err + } + } + newDesc.Digest = w.Digest() + newDesc.Size = cw.n + if newDesc.Annotations == nil { + newDesc.Annotations = make(map[string]string, 1) + } + newDesc.Annotations[estargz.TOCJSONDigestAnnotation] = metadata[estargz.ZstdChunkedManifestChecksumAnnotation] + newDesc.Annotations[estargz.ZstdChunkedManifestChecksumAnnotation] = metadata[estargz.ZstdChunkedManifestChecksumAnnotation] + newDesc.Annotations[estargz.ZstdChunkedManifestInfoAnnotation] = metadata[estargz.ZstdChunkedManifestInfoAnnotation] + return &newDesc, nil + } +} + +type countWriter struct { + w io.Writer + n int64 +} + +func (cw *countWriter) Write(p []byte) (n int, err error) { + n, err = cw.w.Write(p) + cw.n += int64(n) + return +} + +// NOTE: this forcefully converts docker mediatype to OCI mediatype +func convertMediaType(mt string) (string, error) { + switch mt { + case ocispec.MediaTypeImageLayerGzip, images.MediaTypeDockerSchema2LayerGzip: + return ocispec.MediaTypeImageLayerZstd, nil + case ocispec.MediaTypeImageLayerNonDistributableGzip, images.MediaTypeDockerSchema2LayerForeignGzip: + return ocispec.MediaTypeImageLayerNonDistributableZstd, nil + default: + return mt, fmt.Errorf("unknown mediatype %q", mt) + } +} diff --git a/script/benchmark/hello-bench/run.sh b/script/benchmark/hello-bench/run.sh index 989952f57..a7d7da480 100755 --- a/script/benchmark/hello-bench/run.sh +++ b/script/benchmark/hello-bench/run.sh @@ -19,6 +19,7 @@ set -euo pipefail LEGACY_MODE="legacy" ESTARGZ_NOOPT_MODE="estargz-noopt" ESTARGZ_MODE="estargz" +ZSTDCHUNKED_MODE="zstdchunked" CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" REPO="${CONTEXT}../../../" @@ -91,7 +92,7 @@ for SAMPLE_NO in $(seq ${NUM_OF_SAMPLES}) ; do echo -n "" > "${WORKLOADS_LIST}" # Randomize workloads for IMAGE in ${TARGET_IMAGES} ; do - for MODE in ${LEGACY_MODE} ${ESTARGZ_NOOPT_MODE} ${ESTARGZ_MODE} ; do + for MODE in ${LEGACY_MODE} ${ESTARGZ_NOOPT_MODE} ${ESTARGZ_MODE} ${ZSTDCHUNKED_MODE} ; do echo "${IMAGE},${MODE}" >> "${WORKLOADS_LIST}" done done @@ -128,6 +129,14 @@ for SAMPLE_NO in $(seq ${NUM_OF_SAMPLES}) ; do measure "--mode=estargz" ${TARGET_REPOSITORY} ${IMAGE} check_remote_snapshots "${TMP_LOG_FILE}" fi + + if [ "${MODE}" == "${ZSTDCHUNKED_MODE}" ] ; then + echo -n "" > "${TMP_LOG_FILE}" + set_noprefetch "true" # disable prefetch (zstd:chunked doesn't support landmarks yet) + LOG_FILE="${TMP_LOG_FILE}" "${REBOOT_CONTAINERD_SCRIPT}" + measure "--mode=zstdchunked" ${TARGET_REPOSITORY} ${IMAGE} + check_remote_snapshots "${TMP_LOG_FILE}" + fi done done diff --git a/script/benchmark/hello-bench/src/hello.py b/script/benchmark/hello-bench/src/hello.py index 8cdcfa548..b2d5b21ea 100755 --- a/script/benchmark/hello-bench/src/hello.py +++ b/script/benchmark/hello-bench/src/hello.py @@ -42,6 +42,7 @@ LEGACY_MODE = "legacy" ESTARGZ_NOOPT_MODE = "estargz-noopt" ESTARGZ_MODE = "estargz" +ZSTDCHUNKED_MODE = "zstdchunked" DEFAULT_OPTIMIZER = "ctr-remote image optimize --oci" DEFAULT_PULLER = "nerdctl image pull" DEFAULT_PUSHER = "nerdctl image push" @@ -147,7 +148,7 @@ def __init__(self, repository='docker.io/library', srcrepository='docker.io/libr self.pusher = pusher def lazypull(self): - if self.mode == ESTARGZ_NOOPT_MODE or self.mode == ESTARGZ_MODE: + if self.mode == ESTARGZ_NOOPT_MODE or self.mode == ESTARGZ_MODE or self.mode == ZSTDCHUNKED_MODE: return True else: return False @@ -181,6 +182,8 @@ def add_suffix(self, repo): return "%s-esgz" % repo elif self.mode == ESTARGZ_NOOPT_MODE: return "%s-esgz-noopt" % repo + elif self.mode == ZSTDCHUNKED_MODE: + return "%s-zstdchunked" % repo else: return "%s-org" % repo @@ -437,7 +440,7 @@ def main(): print '--list-json' print '--experiments' print '--op=(prepare|run)' - print '--mode=(%s|%s|%s)' % (LEGACY_MODE, ESTARGZ_NOOPT_MODE, ESTARGZ_MODE) + print '--mode=(%s|%s|%s)' % (LEGACY_MODE, ESTARGZ_NOOPT_MODE, ESTARGZ_MODE, ZSTDCHUNKED_MODE) exit(1) benches = [] diff --git a/script/benchmark/tools/csv.sh b/script/benchmark/tools/csv.sh index 4c14d0f7e..da7e5919d 100755 --- a/script/benchmark/tools/csv.sh +++ b/script/benchmark/tools/csv.sh @@ -24,7 +24,7 @@ source "${CONTEXT}/util.sh" MODES=( ${TARGET_MODES:-} ) if [ ${#MODES[@]} -eq 0 ] ; then - MODES=("legacy" "estargz-noopt" "estargz") + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") fi IMAGES=( ${TARGET_IMAGES:-} ) diff --git a/script/benchmark/tools/percentiles.sh b/script/benchmark/tools/percentiles.sh index a5f8782f7..54dc91214 100755 --- a/script/benchmark/tools/percentiles.sh +++ b/script/benchmark/tools/percentiles.sh @@ -32,7 +32,7 @@ echo "output into: ${DATADIR}" MODES=( ${TARGET_MODES:-} ) if [ ${#MODES[@]} -eq 0 ] ; then - MODES=("legacy" "estargz-noopt" "estargz") + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") fi IMAGES=( ${TARGET_IMAGES:-} ) diff --git a/script/benchmark/tools/plot.sh b/script/benchmark/tools/plot.sh index dfddaff59..66471ab7f 100755 --- a/script/benchmark/tools/plot.sh +++ b/script/benchmark/tools/plot.sh @@ -32,7 +32,7 @@ echo "output into: ${DATADIR}" MODES=( ${TARGET_MODES:-} ) if [ ${#MODES[@]} -eq 0 ] ; then - MODES=("legacy" "estargz-noopt" "estargz") + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") fi IMAGES=( ${TARGET_IMAGES:-} ) diff --git a/script/benchmark/tools/table.sh b/script/benchmark/tools/table.sh index 477840898..110cb96c3 100755 --- a/script/benchmark/tools/table.sh +++ b/script/benchmark/tools/table.sh @@ -24,7 +24,7 @@ source "${CONTEXT}/util.sh" MODES=( ${TARGET_MODES:-} ) if [ ${#MODES[@]} -eq 0 ] ; then - MODES=("legacy" "estargz-noopt" "estargz") + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") fi IMAGES=( ${TARGET_IMAGES:-} ) diff --git a/script/benchmark2/config/config.stargz.toml b/script/benchmark2/config/config.stargz.toml new file mode 100644 index 000000000..3defb02db --- /dev/null +++ b/script/benchmark2/config/config.stargz.toml @@ -0,0 +1 @@ +noprefetch = true diff --git a/script/benchmark2/hello-bench/prepare.sh b/script/benchmark2/hello-bench/prepare.sh new file mode 100755 index 000000000..bd14d6954 --- /dev/null +++ b/script/benchmark2/hello-bench/prepare.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +REPO="${CONTEXT}../../../" +MEASURING_SCRIPT="${REPO}/script/benchmark2/hello-bench/src/hello.py" +REBOOT_CONTAINERD_SCRIPT="${REPO}/script/benchmark/hello-bench/reboot_containerd.sh" +NERDCTL_VERSION="0.5.0" + +if [ $# -lt 1 ] ; then + echo "Specify benchmark target." + echo "Ex) ${0} --all" + echo "Ex) ${0} alpine busybox" + exit 1 +fi +TARGET_REPOSITORY="${1}" +TARGET_IMAGES=${@:2} + +if ! which ctr-remote ; then + echo "ctr-remote not found, installing..." + mkdir -p /tmp/out + PREFIX=/tmp/out/ make clean && \ + PREFIX=/tmp/out/ make ctr-remote && \ + install /tmp/out/ctr-remote /usr/local/bin +fi + +if ! which nerdctl ; then + wget -O /tmp/nerdctl.tar.gz "https://github.com/AkihiroSuda/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz" + tar zxvf /tmp/nerdctl.tar.gz -C /usr/local/bin/ +fi + +NO_STARGZ_SNAPSHOTTER="true" "${REBOOT_CONTAINERD_SCRIPT}" +"${MEASURING_SCRIPT}" --repository=${TARGET_REPOSITORY} --op=prepare ${TARGET_IMAGES} diff --git a/script/benchmark2/hello-bench/reboot_store.sh b/script/benchmark2/hello-bench/reboot_store.sh new file mode 100755 index 000000000..055db8f6c --- /dev/null +++ b/script/benchmark2/hello-bench/reboot_store.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +PODMAN_CONFIG_DIR=/etc/containers/ +PODMAN_STORAGE_CONFIG_FILE="${PODMAN_CONFIG_DIR}storage.conf" +REG_STORAGE_CONFIG_FILE="/etc/registry-storage/config.toml" +REG_STORAGE_ROOT=/var/lib/registry-storage/ +REG_STORAGE_DIR="${REG_STORAGE_ROOT}store/" +REG_STORAGE_POOL_LINK="${REG_STORAGE_ROOT}store/pool" +REG_STORAGE_MOUNTPOINT="${REG_STORAGE_DIR}" + +RETRYNUM=30 +RETRYINTERVAL=1 +TIMEOUTSEC=180 +function retry { + local SUCCESS=false + for i in $(seq ${RETRYNUM}) ; do + if eval "timeout ${TIMEOUTSEC} ${@}" ; then + SUCCESS=true + break + fi + echo "Fail(${i}). Retrying..." + sleep ${RETRYINTERVAL} + done + if [ "${SUCCESS}" == "true" ] ; then + return 0 + else + return 1 + fi +} + +function kill_all { + if [ "${1}" != "" ] ; then + ps aux | grep "${1}" \ + | grep -v grep \ + | grep -v "hello.py" \ + | grep -v $(basename ${0}) \ + | sed -E 's/ +/ /g' | cut -f 2 -d ' ' | xargs -I{} kill -9 {} || true + fi +} + +function cleanup { + umount "${REG_STORAGE_MOUNTPOINT}" || true + rm -rf "${REG_STORAGE_DIR}" || true + if [ -d "${REG_STORAGE_ROOT}pool/" ] ; then + for POOL in $(ls "${REG_STORAGE_ROOT}pool/") ; do + umount "${REG_STORAGE_ROOT}pool/${POOL}" || true + for MP in $(ls "${REG_STORAGE_ROOT}pool/${POOL}") ; do + umount "${REG_STORAGE_ROOT}pool/${POOL}/${MP}" || true + done + done + fi + rm -rf "${REG_STORAGE_ROOT}"* + rm "${PODMAN_STORAGE_CONFIG_FILE}" || true + podman system reset -f +} + +echo "cleaning up the environment..." +kill_all "registry-storage" +cleanup + +mkdir -p "${PODMAN_CONFIG_DIR}" + +if [ "${NO_ADDITIONAL_STORAGE:-}" == "true" ] ; then + echo "DO NOT RUN remote snapshotter" + cat < "${PODMAN_STORAGE_CONFIG_FILE}" +[storage] +driver = "overlay" +EOF +else + echo "running remote snaphsotter..." + if [ "${LOG_FILE:-}" == "" ] ; then + LOG_FILE=/dev/null + fi + cat < "${PODMAN_STORAGE_CONFIG_FILE}" +[storage] +driver = "overlay" + +[storage.options] +additionallayerstores = ["${REG_STORAGE_MOUNTPOINT}:ref"] +EOF + mkdir -p "${REG_STORAGE_MOUNTPOINT}" + registry-storage --log-level=debug \ + --config="${REG_STORAGE_CONFIG_FILE}" \ + "${REG_STORAGE_MOUNTPOINT}" \ + 2>&1 | tee -a "${LOG_FILE}" & # Dump all log + retry ls "${REG_STORAGE_POOL_LINK}" > /dev/null +fi diff --git a/script/benchmark2/hello-bench/run.sh b/script/benchmark2/hello-bench/run.sh new file mode 100755 index 000000000..cc5beef10 --- /dev/null +++ b/script/benchmark2/hello-bench/run.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +LEGACY_MODE="legacy" +ESTARGZ_NOOPT_MODE="estargz-noopt" +ESTARGZ_MODE="estargz" +ZSTDCHUNKED_MODE="zstdchunked" + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +REPO="${CONTEXT}../../../" + +# NOTE: The entire contents of containerd/stargz-snapshotter are located in +# the testing container so utils.sh is visible from this script during runtime. +# TODO: Refactor the code dependencies and pack them in the container without +# expecting and relying on volumes. +source "${REPO}/script/util/utils.sh" + +MEASURING_SCRIPT="${REPO}/script/benchmark2/hello-bench/src/hello.py" +REBOOT_STORE_SCRIPT="${REPO}/script/benchmark2/hello-bench/reboot_store.sh" +REPO_CONFIG_DIR="${REPO}/script/benchmark2/hello-bench/config/" +BENCHMARKOUT_MARK_OUTPUT="BENCHMARK_OUTPUT: " +REGISTRY_STORAGE_CONFIG_DIR=/etc/registry-storage/ + +if [ $# -lt 1 ] ; then + echo "Specify benchmark target." + echo "Ex) ${0} --all" + echo "Ex) ${0} alpine busybox" + exit 1 +fi +TARGET_REPOSITORY="${1}" +TARGET_IMAGES=${@:2} +NUM_OF_SAMPLES="${BENCHMARK_SAMPLES_NUM:-1}" + +TMP_LOG_FILE=$(mktemp) +WORKLOADS_LIST=$(mktemp) +function cleanup { + local ORG_EXIT_CODE="${1}" + rm "${TMP_LOG_FILE}" || true + rm "${WORKLOADS_LIST}" + exit "${ORG_EXIT_CODE}" +} +trap 'cleanup "$?"' EXIT SIGHUP SIGINT SIGQUIT SIGTERM + +function output { + echo "${BENCHMARKOUT_MARK_OUTPUT}${1}" +} + +function set_noprefetch { + local NOPREFETCH="${1}" + sed -i 's/noprefetch = .*/noprefetch = '"${NOPREFETCH}"'/g' "${REGISTRY_STORAGE_CONFIG_DIR}config.toml" +} +function measure { + local OPTION="${1}" + local REPOSITORY="${2}" + "${MEASURING_SCRIPT}" ${OPTION} --repository=${REPOSITORY} --op=run --experiments=1 ${@:3} +} + +echo "=========" +echo "SPEC LIST" +echo "=========" +uname -r +cat /etc/os-release +cat /proc/cpuinfo +cat /proc/meminfo +mount +df + +echo "=========" +echo "BENCHMARK" +echo "=========" + +output "[" + +for SAMPLE_NO in $(seq ${NUM_OF_SAMPLES}) ; do + echo -n "" > "${WORKLOADS_LIST}" + # Randomize workloads + for IMAGE in ${TARGET_IMAGES} ; do + for MODE in ${LEGACY_MODE} ${ESTARGZ_NOOPT_MODE} ${ESTARGZ_MODE} ${ZSTDCHUNKED_MODE} ; do + echo "${IMAGE},${MODE}" >> "${WORKLOADS_LIST}" + done + done + sort -R -o "${WORKLOADS_LIST}" "${WORKLOADS_LIST}" + echo "Workloads of iteration [${SAMPLE_NO}]" + cat "${WORKLOADS_LIST}" + + # Run the workloads + for THEWL in $(cat "${WORKLOADS_LIST}") ; do + echo "The workload is ${THEWL}" + + IMAGE=$(echo "${THEWL}" | cut -d ',' -f 1) + MODE=$(echo "${THEWL}" | cut -d ',' -f 2) + + echo "===== Measuring [${SAMPLE_NO}] ${IMAGE} (${MODE}) =====" + + if [ "${MODE}" == "${LEGACY_MODE}" ] ; then + NO_ADDITIONAL_STORAGE="true" "${REBOOT_STORE_SCRIPT}" + measure "--mode=legacy" ${TARGET_REPOSITORY} ${IMAGE} + fi + + if [ "${MODE}" == "${ESTARGZ_NOOPT_MODE}" ] ; then + echo -n "" > "${TMP_LOG_FILE}" + set_noprefetch "true" # disable prefetch + LOG_FILE="${TMP_LOG_FILE}" "${REBOOT_STORE_SCRIPT}" + measure "--mode=estargz-noopt" ${TARGET_REPOSITORY} ${IMAGE} + check_remote_snapshots "${TMP_LOG_FILE}" + fi + + if [ "${MODE}" == "${ESTARGZ_MODE}" ] ; then + echo -n "" > "${TMP_LOG_FILE}" + set_noprefetch "false" # enable prefetch + LOG_FILE="${TMP_LOG_FILE}" "${REBOOT_STORE_SCRIPT}" + measure "--mode=estargz" ${TARGET_REPOSITORY} ${IMAGE} + check_remote_snapshots "${TMP_LOG_FILE}" + fi + + if [ "${MODE}" == "${ZSTDCHUNKED_MODE}" ] ; then + echo -n "" > "${TMP_LOG_FILE}" + set_noprefetch "true" # disable prefetch + LOG_FILE="${TMP_LOG_FILE}" "${REBOOT_STORE_SCRIPT}" + measure "--mode=zstdchunked" ${TARGET_REPOSITORY} ${IMAGE} + check_remote_snapshots "${TMP_LOG_FILE}" # TODO + fi + done +done + +output "]" diff --git a/script/benchmark2/hello-bench/src/gcc/main.c b/script/benchmark2/hello-bench/src/gcc/main.c new file mode 100644 index 000000000..f48e3c8c6 --- /dev/null +++ b/script/benchmark2/hello-bench/src/gcc/main.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, char *argv[]) { + printf("hello\n"); +} diff --git a/script/benchmark2/hello-bench/src/go/main.go b/script/benchmark2/hello-bench/src/go/main.go new file mode 100644 index 000000000..efd442b0e --- /dev/null +++ b/script/benchmark2/hello-bench/src/go/main.go @@ -0,0 +1,23 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package main + +import "fmt" + +func main() { + fmt.Println("hello") +} diff --git a/script/benchmark2/hello-bench/src/hello.py b/script/benchmark2/hello-bench/src/hello.py new file mode 100755 index 000000000..642d1cf68 --- /dev/null +++ b/script/benchmark2/hello-bench/src/hello.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The MIT License (MIT) +# +# Copyright (c) 2015 Tintri +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import os, sys, subprocess, select, random, urllib2, time, json, tempfile, shutil + +TMP_DIR = tempfile.mkdtemp() +LEGACY_MODE = "legacy" +ESTARGZ_NOOPT_MODE = "estargz-noopt" +ESTARGZ_MODE = "estargz" +ZSTDCHUNKED_MODE = "zstdchunked" +DEFAULT_OPTIMIZER = "ctr-remote image convert --oci --zstdchunked" +DEFAULT_PULLER = "nerdctl image pull" +DEFAULT_PUSHER = "nerdctl image push" +DEFAULT_TAGGER = "nerdctl image tag" +BENCHMARKOUT_MARK = "BENCHMARK_OUTPUT: " + +def exit(status): + # cleanup + shutil.rmtree(TMP_DIR) + sys.exit(status) + +def tmp_dir(): + tmp_dir.nxt += 1 + return os.path.join(TMP_DIR, str(tmp_dir.nxt)) +tmp_dir.nxt = 0 + +def tmp_copy(src): + dst = tmp_dir() + shutil.copytree(src, dst) + return dst + +def genargs(arg): + if arg == None or arg == "": + return "" + else: + return '-args \'["%s"]\'' % arg.replace('"', '\\\"').replace('\'', '\'"\'"\'') + +class RunArgs: + def __init__(self, env={}, arg='', stdin='', stdin_sh='sh', waitline='', mount=[]): + self.env = env + self.arg = arg + self.stdin = stdin + self.stdin_sh = stdin_sh + self.waitline = waitline + self.mount = mount + +class Bench: + def __init__(self, name, category='other'): + self.name = name + self.repo = name # TODO: maybe we'll eventually have multiple benches per repo + self.category = category + + def __str__(self): + return json.dumps(self.__dict__) + +class BenchRunner: + ECHO_HELLO = set(['alpine:3.10.2', + 'fedora:30',]) + + CMD_ARG_WAIT = {'rethinkdb:2.3.6': RunArgs(waitline='Server ready'), + 'glassfish:4.1-jdk8': RunArgs(waitline='Running GlassFish'), + 'drupal:8.7.6': RunArgs(waitline='apache2 -D FOREGROUND'), + 'jenkins:2.60.3': RunArgs(waitline='Jenkins is fully up and running'), + 'redis:5.0.5': RunArgs(waitline='Ready to accept connections'), + 'tomcat:10.0.0-jdk15-openjdk-buster': RunArgs(waitline='Server startup'), + 'postgres:13.1': RunArgs(waitline='database system is ready to accept connections', + env={'POSTGRES_PASSWORD': 'abc'}), + } + + CMD_STDIN = {'php:7.3.8': RunArgs(stdin='php -r "echo \\\"hello\\n\\\";"; exit\n'), + 'gcc:10.2.0': RunArgs(stdin='cd /src; gcc main.c; ./a.out; exit\n', + mount=[('gcc', '/src')]), + 'golang:1.12.9': RunArgs(stdin='cd /go/src; go run main.go; exit\n', + mount=[('go', '/go/src')]), + 'jruby:9.2.8.0': RunArgs(stdin='jruby -e "puts \\\"hello\\\""; exit\n'), + 'r-base:3.6.1': RunArgs(stdin='sprintf("hello")\nq()\n', stdin_sh='R --no-save'), + } + + CMD_ARG = {'perl:5.30': RunArgs(arg='perl -e \'print("hello\\n")\''), + 'python:3.9': RunArgs(arg='python -c \'print("hello")\''), + 'pypy:3.5': RunArgs(arg='pypy3 -c \'print("hello")\''), + 'node:13.13.0': RunArgs(arg='node -e \'console.log("hello")\''), + } + + # complete listing + ALL = dict([(b.name, b) for b in + [Bench('alpine:3.10.2', 'distro'), + Bench('fedora:30', 'distro'), + Bench('rethinkdb:2.3.6', 'database'), + Bench('postgres:13.1', 'database'), + Bench('redis:5.0.5', 'database'), + Bench('python:3.9', 'language'), + Bench('golang:1.12.9', 'language'), + Bench('gcc:10.2.0', 'language'), + Bench('jruby:9.2.8.0', 'language'), + Bench('perl:5.30', 'language'), + Bench('php:7.3.8', 'language'), + Bench('pypy:3.5', 'language'), + Bench('r-base:3.6.1', 'language'), + Bench('drupal:8.7.6'), + Bench('jenkins:2.60.3'), + Bench('node:13.13.0'), + Bench('tomcat:10.0.0-jdk15-openjdk-buster', 'web-server'), + ]]) + + def __init__(self, repository='docker.io/library', srcrepository='docker.io/library', mode=LEGACY_MODE, optimizer=DEFAULT_OPTIMIZER, puller=DEFAULT_PULLER, pusher=DEFAULT_PUSHER): + self.docker = 'podman' + self.repository = repository + self.srcrepository = srcrepository + self.mode = mode + self.optimizer = optimizer + self.puller = puller + self.pusher = pusher + + def lazypull(self): + if self.mode == ESTARGZ_NOOPT_MODE or self.mode == ESTARGZ_MODE or self.mode == ZSTDCHUNKED_MODE: + return True + else: + return False + + def cleanup(self, name, image): + print "Cleaning up environment..." + cmd = '%s kill -s 9 %s' % (self.docker, name) + print cmd + rc = os.system(cmd) # sometimes containers already exit. we ignore the failure. + cmd = '%s rm %s' % (self.docker, name) + print cmd + rc = os.system(cmd) + assert(rc == 0) + cmd = '%s image rm %s' % (self.docker, image) + print cmd + rc = os.system(cmd) + assert(rc == 0) + cmd = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../reboot_store.sh') # clear cache + print cmd + rc = os.system(cmd) + assert(rc == 0) + + def add_suffix(self, repo): + if self.mode == ESTARGZ_MODE: + return "%s-esgz" % repo + elif self.mode == ESTARGZ_NOOPT_MODE: + return "%s-esgz-noopt" % repo + elif self.mode == ZSTDCHUNKED_MODE: + return "%s-zstdchunked" % repo + else: + return "%s-org" % repo + + def run_task(self, cid): + cmd = '%s start -a %s' % (self.docker, cid) + print cmd + startrun = time.time() + rc = os.system(cmd) + runtime = time.time() - startrun + assert(rc == 0) + return runtime + + def run_echo_hello(self, repo, cid): + cmd = ('%s create --name %s %s/%s echo hello' % + (self.docker, cid, self.repository, self.add_suffix(repo))) + print cmd + startcreate = time.time() + rc = os.system(cmd) + createtime = time.time() - startcreate + assert(rc == 0) + return createtime, self.run_task(cid) + + def run_cmd_arg(self, repo, cid, runargs): + assert(len(runargs.mount) == 0) + cmd = '%s create --name %s %s/%s ' % (self.docker, cid, self.repository, self.add_suffix(repo)) + cmd += runargs.arg + print cmd + startcreate = time.time() + rc = os.system(cmd) + createtime = time.time() - startcreate + assert(rc == 0) + return createtime, self.run_task(cid) + + def run_cmd_arg_wait(self, repo, cid, runargs): + env = ' '.join(['--env %s=%s' % (k,v) for k,v in runargs.env.iteritems()]) + cmd = ('%s create %s --name %s %s/%s %s ' % + (self.docker, env, cid, self.repository, self.add_suffix(repo), runargs.arg)) + print cmd + startcreate = time.time() + rc = os.system(cmd) + createtime = time.time() - startcreate + assert(rc == 0) + cmd = '%s start -a %s' % (self.docker, cid) + print cmd + runtime = 0 + startrun = time.time() + + # line buffer output + p = subprocess.Popen(cmd, shell=True, bufsize=1, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE) + while True: + l = p.stdout.readline() + if l == '': + continue + print 'out: ' + l.strip() + # are we done? + if l.find(runargs.waitline) >= 0: + runtime = time.time() - startrun + # cleanup + print 'DONE' + cmd = '%s kill -s 9 %s' % (self.docker, cid) + rc = os.system(cmd) + assert(rc == 0) + break + p.wait() + return createtime, runtime + + def run_cmd_stdin(self, repo, cid, runargs): + cmd = '%s create -i ' % self.docker + for a,b in runargs.mount: + a = os.path.join(os.path.dirname(os.path.abspath(__file__)), a) + a = tmp_copy(a) + cmd += '--mount type=bind,src=%s,dst=%s ' % (a,b) + cmd += '--name %s %s/%s ' % (cid, self.repository, self.add_suffix(repo)) + if runargs.stdin_sh: + cmd += runargs.stdin_sh # e.g., sh -c + + print cmd + startcreate = time.time() + rc = os.system(cmd) + createtime = time.time() - startcreate + assert(rc == 0) + cmd = '%s start -a %s' % (self.docker, cid) + print cmd + startrun = time.time() + + p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print runargs.stdin + out, _ = p.communicate(runargs.stdin) + runtime = time.time() - startrun + print out + assert(p.returncode == 0) + return createtime, runtime + + def run(self, bench, cid): + name = bench.name + print "Pulling the image..." + startpull = time.time() + cmd = ('%s pull %s/%s' % + (self.docker, self.repository, self.add_suffix(name))) + print cmd + rc = os.system(cmd) + assert(rc == 0) + pulltime = time.time() - startpull + + runtime = 0 + createtime = 0 + if name in BenchRunner.ECHO_HELLO: + createtime, runtime = self.run_echo_hello(repo=name, cid=cid) + elif name in BenchRunner.CMD_ARG: + createtime, runtime = self.run_cmd_arg(repo=name, cid=cid, runargs=BenchRunner.CMD_ARG[name]) + elif name in BenchRunner.CMD_ARG_WAIT: + createtime, runtime = self.run_cmd_arg_wait(repo=name, cid=cid, runargs=BenchRunner.CMD_ARG_WAIT[name]) + elif name in BenchRunner.CMD_STDIN: + createtime, runtime = self.run_cmd_stdin(repo=name, cid=cid, runargs=BenchRunner.CMD_STDIN[name]) + else: + print 'Unknown bench: '+name + exit(1) + + return pulltime, createtime, runtime + + def copy_img(self, repo): + self.mode = LEGACY_MODE + cmd = 'crane copy %s/%s %s/%s' % (self.srcrepository, repo, self.repository, self.add_suffix(repo)) + print cmd + rc = os.system(cmd) + assert(rc == 0) + + def convert_and_push_img(self, repo): + self.mode = ZSTDCHUNKED_MODE + self.pull_img(repo) + cmd = '%s %s/%s %s/%s' % (self.optimizer, self.srcrepository, repo, self.repository, self.add_suffix(repo)) + print cmd + rc = os.system(cmd) + assert(rc == 0) + self.push_img(repo) + + def push_img(self, repo): + cmd = '%s %s/%s' % (self.pusher, self.repository, self.add_suffix(repo)) + print cmd + rc = os.system(cmd) + assert(rc == 0) + + def pull_img(self, name): + cmd = '%s %s/%s' % (self.puller, self.srcrepository, name) + print cmd + rc = os.system(cmd) + assert(rc == 0) + + def prepare(self, bench): + name = bench.name + self.copy_img(name) + self.convert_and_push_img(name) + + def operation(self, op, bench, cid): + if op == 'run': + return self.run(bench, cid) + elif op == 'prepare': + self.prepare(bench) + return 0, 0, 0 + else: + print 'Unknown operation: '+op + exit(1) + +def main(): + if len(sys.argv) == 1: + print 'Usage: bench.py [OPTIONS] [BENCHMARKS]' + print 'OPTIONS:' + print '--repository=' + print '--srcrepository=' + print '--all' + print '--list' + print '--list-json' + print '--experiments' + print '--op=(prepare|run)' + print '--mode=(%s|%s|%s)' % (LEGACY_MODE, ESTARGZ_NOOPT_MODE, ESTARGZ_MODE, ZSTDCHUNKED_MODE) + exit(1) + + benches = [] + kvargs = {} + # parse args + for arg in sys.argv[1:]: + if arg.startswith('--'): + parts = arg[2:].split('=') + if len(parts) == 2: + kvargs[parts[0]] = parts[1] + elif parts[0] == 'all': + benches.extend(BenchRunner.ALL.values()) + elif parts[0] == 'list': + template = '%-16s\t%-20s' + print template % ('CATEGORY', 'NAME') + for b in sorted(BenchRunner.ALL.values(), key=lambda b:(b.category, b.name)): + print template % (b.category, b.name) + elif parts[0] == 'list-json': + print json.dumps([b.__dict__ for b in BenchRunner.ALL.values()]) + else: + benches.append(BenchRunner.ALL[arg]) + + op = kvargs.pop('op', 'run') + trytimes = int(kvargs.pop('experiments', '1')) + if not op == "run": + trytimes = 1 + + # run benchmarks + runner = BenchRunner(**kvargs) + for bench in benches: + cid = '%s_bench_%d' % (bench.repo.replace(':', '-').replace('/', '-'), random.randint(1,1000000)) + + elapsed_times = [] + pull_times = [] + create_times = [] + run_times = [] + + for i in range(trytimes): + start = time.time() + pulltime, createtime, runtime = runner.operation(op, bench, cid) + elapsed = time.time() - start + if op == "run": + runner.cleanup(cid, '%s/%s' % (runner.repository, runner.add_suffix(bench.repo))) + elapsed_times.append(elapsed) + pull_times.append(pulltime) + create_times.append(createtime) + run_times.append(runtime) + print 'ITERATION %s:' % i + print 'elapsed %s' % elapsed + print 'pull %s' % pulltime + print 'create %s' % createtime + print 'run %s' % runtime + + row = {'mode':'%s' % runner.mode, 'repo':bench.repo, 'bench':bench.name, 'elapsed':sum(elapsed_times) / len(elapsed_times), 'elapsed_pull':sum(pull_times) / len(pull_times), 'elapsed_create':sum(create_times) / len(create_times), 'elapsed_run':sum(run_times) / len(run_times)} + js = json.dumps(row) + print '%s%s,' % (BENCHMARKOUT_MARK, js) + sys.stdout.flush() + +if __name__ == '__main__': + main() + exit(0) diff --git a/script/benchmark2/test.sh b/script/benchmark2/test.sh new file mode 100755 index 000000000..7c4884068 --- /dev/null +++ b/script/benchmark2/test.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +REPO="${CONTEXT}../../" + +BENCHMARKING_BASE_IMAGE_NAME="benchmark-image-base" +BENCHMARKING_NODE_IMAGE_NAME="benchmark-image-test" +BENCHMARKING_NODE=hello-bench +BENCHMARKING_CONTAINER=hello-bench-container + +if [ "${BENCHMARKING_NO_RECREATE:-}" != "true" ] ; then + echo "Preparing node image..." + docker build ${DOCKER_BUILD_ARGS:-} -t "${BENCHMARKING_BASE_IMAGE_NAME}" \ + --target podman-base "${REPO}" +fi + +DOCKER_COMPOSE_YAML=$(mktemp) +TMP_CONTEXT=$(mktemp -d) +function cleanup { + local ORG_EXIT_CODE="${1}" + rm "${DOCKER_COMPOSE_YAML}" || true + rm -rf "${TMP_CONTEXT}" || true + exit "${ORG_EXIT_CODE}" +} +trap 'cleanup "$?"' EXIT SIGHUP SIGINT SIGQUIT SIGTERM + +cp -R "${CONTEXT}/config" "${TMP_CONTEXT}" + +cat < "${TMP_CONTEXT}/Dockerfile" +FROM ${BENCHMARKING_BASE_IMAGE_NAME} + +RUN apt-get update -y && \ + apt-get --no-install-recommends install -y python jq && \ + git clone https://github.com/google/go-containerregistry \ + \${GOPATH}/src/github.com/google/go-containerregistry && \ + cd \${GOPATH}/src/github.com/google/go-containerregistry && \ + git checkout 4b1985e5ea2104672636879e1694808f735fd214 && \ + GO111MODULE=on go get github.com/google/go-containerregistry/cmd/crane + +COPY ./config/config.stargz.toml /etc/registry-storage/config.toml + +ENV CONTAINERD_SNAPSHOTTER="" + +ENTRYPOINT [ "sleep", "infinity" ] +EOF +docker build -t "${BENCHMARKING_NODE_IMAGE_NAME}" ${DOCKER_BUILD_ARGS:-} "${TMP_CONTEXT}" + +echo "Preparing docker-compose.yml..." +cat < "${DOCKER_COMPOSE_YAML}" +version: "3.7" +services: + ${BENCHMARKING_NODE}: + image: ${BENCHMARKING_NODE_IMAGE_NAME} + container_name: ${BENCHMARKING_CONTAINER} + privileged: true + init: true + working_dir: /go/src/github.com/containerd/stargz-snapshotter + environment: + - NO_PROXY=127.0.0.1,localhost + - HTTP_PROXY=${HTTP_PROXY:-} + - HTTPS_PROXY=${HTTPS_PROXY:-} + - http_proxy=${http_proxy:-} + - https_proxy=${https_proxy:-} + tmpfs: + - /tmp:exec,mode=777 + volumes: + - "${REPO}:/go/src/github.com/containerd/stargz-snapshotter:ro" + - "/dev/fuse:/dev/fuse" + - "containers-data:/var/lib/containers:delegated" + - "additional-store-data:/var/lib/registry-storage:delegated" +volumes: + containers-data: + additional-store-data: +EOF + +echo "Preparing for benchmark..." +OUTPUTDIR="${BENCHMARK_RESULT_DIR:-}" +if [ "${OUTPUTDIR}" == "" ] ; then + OUTPUTDIR=$(mktemp -d) +fi +echo "See output for >>> ${OUTPUTDIR}" +LOG_DIR="${BENCHMARK_LOG_DIR:-}" +if [ "${LOG_DIR}" == "" ] ; then + LOG_DIR=$(mktemp -d) +fi +LOG_FILE="${LOG_DIR}/registry-storage-benchmark-$(date '+%Y%m%d%H%M%S')" +touch "${LOG_FILE}" +echo "Logging to >>> ${LOG_FILE} (will finally be stored under ${OUTPUTDIR})" + +echo "Benchmarking..." +FAIL= +if ! ( cd "${CONTEXT}" && \ + docker-compose -f "${DOCKER_COMPOSE_YAML}" build ${DOCKER_BUILD_ARGS:-} \ + "${BENCHMARKING_NODE}" && \ + docker-compose -f "${DOCKER_COMPOSE_YAML}" up -d --force-recreate && \ + docker exec -e BENCHMARK_SAMPLES_NUM -i "${BENCHMARKING_CONTAINER}" \ + script/benchmark2/hello-bench/run.sh \ + "${BENCHMARK_REGISTRY:-docker.io}/${BENCHMARK_USER}" \ + ${BENCHMARK_TARGETS} &> "${LOG_FILE}" ) ; then + echo "Failed to run benchmark." + FAIL=true +fi + +echo "Harvesting log ${LOG_FILE} -> ${OUTPUTDIR} ..." +tar zcvf "${OUTPUTDIR}/result.log.tar.gz" "${LOG_FILE}" +if [ "${FAIL}" != "true" ] ; then + echo "Formatting output..." + if ! ( tar zOxf "${OUTPUTDIR}/result.log.tar.gz" | "${CONTEXT}/tools/format.sh" > "${OUTPUTDIR}/result.json" && \ + cat "${OUTPUTDIR}/result.json" | "${CONTEXT}/tools/plot.sh" "${OUTPUTDIR}" && \ + cat "${OUTPUTDIR}/result.json" | "${CONTEXT}/tools/percentiles.sh" "${OUTPUTDIR}" && \ + cat "${OUTPUTDIR}/result.json" | "${CONTEXT}/tools/table.sh" > "${OUTPUTDIR}/result.md" && \ + cat "${OUTPUTDIR}/result.json" | "${CONTEXT}/tools/csv.sh" > "${OUTPUTDIR}/result.csv" ) ; then + echo "Failed to formatting output (but you can try it manually from ${OUTPUTDIR})" + FAIL=true + fi +fi + +echo "Cleaning up environment..." +docker-compose -f "${DOCKER_COMPOSE_YAML}" down -v +if [ "${FAIL}" == "true" ] ; then + exit 1 +fi + +exit 0 diff --git a/script/benchmark2/tools/csv.sh b/script/benchmark2/tools/csv.sh new file mode 100755 index 000000000..da7e5919d --- /dev/null +++ b/script/benchmark2/tools/csv.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +JSON="$(mktemp)" +cat > "${JSON}" + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +source "${CONTEXT}/util.sh" + +MODES=( ${TARGET_MODES:-} ) +if [ ${#MODES[@]} -eq 0 ] ; then + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") +fi + +IMAGES=( ${TARGET_IMAGES:-} ) +if [ ${#IMAGES[@]} -eq 0 ] ; then + IMAGES=( $(cat "${JSON}" | jq -r '[ .[] | select(.mode=="'${MODES[0]}'").repo ] | unique[]') ) +fi + +# Ensure we use the exact same number of samples among benchmarks +MINSAMPLES= +for IMGNAME in "${IMAGES[@]}" ; do + for MODE in "${MODES[@]}"; do + THEMIN=$(min_samples "${JSON}" "${IMGNAME}" "${MODE}") + if [ "${MINSAMPLES}" == "" ] ; then + MINSAMPLES="${THEMIN}" + fi + MINSAMPLES=$(echo "${MINSAMPLES} ${THEMIN}" | tr ' ' '\n' | sort -n | head -1) + done +done + +INDEX="image,operation" +for MODE in "${MODES[@]}"; do + INDEX="${INDEX},${MODE}" +done +echo "${INDEX}" +for IMGNAME in "${IMAGES[@]}" ; do + PULLLINE="${IMGNAME},pull" + for MODE in "${MODES[@]}"; do + PULLTIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_pull") + PULLLINE="${PULLLINE},${PULLTIME}" + done + echo "${PULLLINE}" + + CREATELINE="${IMGNAME},create" + for MODE in "${MODES[@]}"; do + CREATETIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_create") + CREATELINE="${CREATELINE},${CREATETIME}" + done + echo "${CREATELINE}" + + RUNLINE="${IMGNAME},run" + for MODE in "${MODES[@]}"; do + RUNTIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_run") + RUNLINE="${RUNLINE},${RUNTIME}" + done + echo "${RUNLINE}" +done diff --git a/script/benchmark2/tools/format.sh b/script/benchmark2/tools/format.sh new file mode 100755 index 000000000..266ca09d7 --- /dev/null +++ b/script/benchmark2/tools/format.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +OUTPUT_MARK="BENCHMARK_OUTPUT: " + +grep "${OUTPUT_MARK}" | sed -e 's/^'"${OUTPUT_MARK}"'//g' | sed -e ':begin;$!N;s/,\n\(\(}\|]\),*\)/\n\1/g;tbegin;P;D' diff --git a/script/benchmark2/tools/percentiles.sh b/script/benchmark2/tools/percentiles.sh new file mode 100755 index 000000000..54dc91214 --- /dev/null +++ b/script/benchmark2/tools/percentiles.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +JSON="$(mktemp)" +cat > "${JSON}" + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +source "${CONTEXT}/util.sh" + +if [ "${1}" == "" ] ; then + echo "Specify directory for output" + exit 1 +fi + +DATADIR="${1}/" +echo "output into: ${DATADIR}" + +MODES=( ${TARGET_MODES:-} ) +if [ ${#MODES[@]} -eq 0 ] ; then + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") +fi + +IMAGES=( ${TARGET_IMAGES:-} ) +if [ ${#IMAGES[@]} -eq 0 ] ; then + IMAGES=( $(cat "${JSON}" | jq -r '[ .[] | select(.mode=="'${MODES[0]}'").repo ] | unique[]') ) +fi + +GRANULARITY="${BENCHMARK_PERCENTILES_GRANULARITY}" +if [ "${GRANULARITY}" == "" ] ; then + GRANULARITY="0.1" +fi + +# Ensure we use the exact same number of samples among benchmarks +MINSAMPLES= +for IMGNAME in "${IMAGES[@]}" ; do + for MODE in "${MODES[@]}"; do + THEMIN=$(min_samples "${JSON}" "${IMGNAME}" "${MODE}") + if [ "${MINSAMPLES}" == "" ] ; then + MINSAMPLES="${THEMIN}" + fi + MINSAMPLES=$(echo "${MINSAMPLES} ${THEMIN}" | tr ' ' '\n' | sort -n | head -1) + done +done + +function template { + local GRAPHFILE="${1}" + local IMGNAME="${2}" + local MODE="${3}" + local OPERATION="${4}" + local SAMPLES="${5}" + + cat < "${CSVFILE}" + for MODE in "${MODES[@]}"; do + for OPERATION in "pull" "create" "run" ; do + DATAFILE="${RAWDATADIR}${IMAGE}-${MODE}-${OPERATION}.dat" + PLTFILE="${PLTDATADIR}result-${IMAGE}-${MODE}-${OPERATION}.plt" + template "${IMGDATADIR}result-${IMAGE}-${MODE}-${OPERATION}.png" \ + "${IMGNAME}" "${MODE}" "${OPERATION}" "${MINSAMPLES}" > "${PLTFILE}" + for PCTL in $(seq 0 "${GRANULARITY}" 100) ; do + TIME=$(PERCENTILE=${PCTL} percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_${OPERATION}") + echo "${PCTL} ${TIME}" >> "${DATAFILE}" + echo "${IMGNAME},${MODE},${OPERATION},${PCTL},${TIME}" >> "${CSVFILE}" + done + echo 'plot "'"${DATAFILE}"'" using 0:2:xtic(1) with boxes lw 1 lc rgb "black" notitle' >> "${PLTFILE}" + gnuplot "${PLTFILE}" + done + done +done diff --git a/script/benchmark2/tools/plot.sh b/script/benchmark2/tools/plot.sh new file mode 100755 index 000000000..66471ab7f --- /dev/null +++ b/script/benchmark2/tools/plot.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +JSON="$(mktemp)" +cat > "${JSON}" + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +source "${CONTEXT}/util.sh" + +if [ "${1}" == "" ] ; then + echo "Specify directory for output" + exit 1 +fi + +DATADIR="${1}/" +echo "output into: ${DATADIR}" + +MODES=( ${TARGET_MODES:-} ) +if [ ${#MODES[@]} -eq 0 ] ; then + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") +fi + +IMAGES=( ${TARGET_IMAGES:-} ) +if [ ${#IMAGES[@]} -eq 0 ] ; then + IMAGES=( $(cat "${JSON}" | jq -r '[ .[] | select(.mode=="'${MODES[0]}'").repo ] | unique[]') ) +fi + +# Ensure we use the exact same number of samples among benchmarks +MINSAMPLES= +for IMGNAME in "${IMAGES[@]}" ; do + for MODE in "${MODES[@]}"; do + THEMIN=$(min_samples "${JSON}" "${IMGNAME}" "${MODE}") + if [ "${MINSAMPLES}" == "" ] ; then + MINSAMPLES="${THEMIN}" + fi + MINSAMPLES=$(echo "${MINSAMPLES} ${THEMIN}" | tr ' ' '\n' | sort -n | head -1) + done +done + +PLTFILE_ALL="${DATADIR}result.plt" +GRAPHFILE_ALL="${DATADIR}result.png" + +cat < "${PLTFILE_ALL}" +set output '${GRAPHFILE_ALL}' +set title "Time to take for starting up containers(${PERCENTILE} pctl., ${MINSAMPLES} samples)" +set terminal png size 1000, 750 +set style data histogram +set style histogram rowstack gap 1 +set style fill solid 1.0 border -1 +set key autotitle columnheader +set xtics rotate by -45 +set ylabel 'time[sec]' +set lmargin 10 +set rmargin 5 +set tmargin 5 +set bmargin 7 +plot \\ +EOF + +NOTITLE= +INDEX=1 +for IMGNAME in "${IMAGES[@]}" ; do + echo "[${INDEX}]Processing: ${IMGNAME}" + IMAGE=$(echo "${IMGNAME}" | sed 's|[/:]|-|g') + DATAFILE="${DATADIR}${IMAGE}.dat" + SUFFIX=', \' + if [ ${INDEX} -eq ${#IMAGES[@]} ] ; then + SUFFIX='' + fi + echo "mode pull create run" > "${DATAFILE}" + for MODE in "${MODES[@]}"; do + PULLTIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_pull") + CREATETIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_create") + RUNTIME=$(percentile "${JSON}" "${MINSAMPLES}" "${IMGNAME}" "${MODE}" "elapsed_run") + + echo "${MODE} ${PULLTIME} ${CREATETIME} ${RUNTIME}" >> "${DATAFILE}" + done + + echo 'newhistogram "'"${IMAGE}"'", "'"${DATAFILE}"'" u 2:xtic(1) fs pattern 1 lt -1 '"${NOTITLE}"', "" u 3 fs pattern 2 lt -1 '"${NOTITLE}"', "" u 4 fs pattern 3 lt -1 '"${NOTITLE}""${SUFFIX}" \ + >> "${PLTFILE_ALL}" + NOTITLE=notitle + ((INDEX+=1)) +done + +gnuplot "${PLTFILE_ALL}" diff --git a/script/benchmark2/tools/table.sh b/script/benchmark2/tools/table.sh new file mode 100755 index 000000000..110cb96c3 --- /dev/null +++ b/script/benchmark2/tools/table.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Copyright The containerd Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +JSON="$(mktemp)" +cat > "${JSON}" + +CONTEXT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/" +source "${CONTEXT}/util.sh" + +MODES=( ${TARGET_MODES:-} ) +if [ ${#MODES[@]} -eq 0 ] ; then + MODES=("legacy" "estargz-noopt" "estargz" "zstdchunked") +fi + +IMAGES=( ${TARGET_IMAGES:-} ) +if [ ${#IMAGES[@]} -eq 0 ] ; then + IMAGES=( $(cat "${JSON}" | jq -r '[ .[] | select(.mode=="'${MODES[0]}'").repo ] | unique[]') ) +fi + +# Ensure we use the exact same number of samples among benchmarks +MINSAMPLES= +for IMGNAME in "${IMAGES[@]}" ; do + for MODE in "${MODES[@]}"; do + THEMIN=$(min_samples "${JSON}" "${IMGNAME}" "${MODE}") + if [ "${MINSAMPLES}" == "" ] ; then + MINSAMPLES="${THEMIN}" + fi + MINSAMPLES=$(echo "${MINSAMPLES} ${THEMIN}" | tr ' ' '\n' | sort -n | head -1) + done +done + +cat < "${CALCTEMP}" + local PYTHON_BIN= + if which python &> /dev/null ; then + PYTHON_BIN=python + elif which python3 &> /dev/null ; then + # Try also with python3 + PYTHON_BIN=python3 + else + echo "Python not found" + exit 1 + fi + cat <