diff --git a/commands/imagetools/inspect.go b/commands/imagetools/inspect.go
index 7a6892f3..70867431 100644
--- a/commands/imagetools/inspect.go
+++ b/commands/imagetools/inspect.go
@@ -1,28 +1,29 @@
package commands
import (
- "fmt"
- "os"
-
- "github.com/containerd/containerd/images"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
type inspectOptions struct {
- raw bool
builder string
+ format string
+ raw bool
}
func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
ctx := appcontext.Context()
+ if in.format != "" && in.raw {
+ return errors.Errorf("format and raw cannot be used together")
+ }
+
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
@@ -47,28 +48,13 @@ func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
if err != nil {
return err
}
- r := imagetools.New(imageopt)
- dt, desc, err := r.Get(ctx, name)
+ p, err := imagetools.NewPrinter(ctx, imageopt, name, in.format)
if err != nil {
return err
}
- if in.raw {
- fmt.Printf("%s", dt) // avoid newline to keep digest
- return nil
- }
-
- switch desc.MediaType {
- // case images.MediaTypeDockerSchema2Manifest, specs.MediaTypeImageManifest:
- // TODO: handle distribution manifest and schema1
- case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
- return imagetools.PrintManifestList(dt, desc, name, os.Stdout)
- default:
- fmt.Printf("%s\n", dt)
- }
-
- return nil
+ return p.Print(in.raw, dockerCli.Out())
}
func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
@@ -76,7 +62,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "inspect [OPTIONS] NAME",
- Short: "Show details of image in the registry",
+ Short: "Show details of an image in the registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.Builder
@@ -85,7 +71,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
}
flags := cmd.Flags()
- flags.BoolVar(&options.raw, "raw", false, "Show original JSON manifest")
+ flags.StringVar(&options.format, "format", "{{.Manifest}}", "Format the output using the given Go template")
+ flags.BoolVar(&options.raw, "raw", false, "Show original, unformatted JSON manifest")
return cmd
}
diff --git a/commands/imagetools/root.go b/commands/imagetools/root.go
index df4a87e2..9be5b013 100644
--- a/commands/imagetools/root.go
+++ b/commands/imagetools/root.go
@@ -16,8 +16,8 @@ func RootCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
}
cmd.AddCommand(
- inspectCmd(dockerCli, opts),
createCmd(dockerCli, opts),
+ inspectCmd(dockerCli, opts),
)
return cmd
diff --git a/docs/reference/buildx_imagetools.md b/docs/reference/buildx_imagetools.md
index 4dab8b8c..db7feffa 100644
--- a/docs/reference/buildx_imagetools.md
+++ b/docs/reference/buildx_imagetools.md
@@ -12,7 +12,7 @@ Commands to work on images in registry
| Name | Description |
| --- | --- |
| [`create`](buildx_imagetools_create.md) | Create a new image based on source images |
-| [`inspect`](buildx_imagetools_inspect.md) | Show details of image in the registry |
+| [`inspect`](buildx_imagetools_inspect.md) | Show details of an image in the registry |
### Options
diff --git a/docs/reference/buildx_imagetools_create.md b/docs/reference/buildx_imagetools_create.md
index 8ede3c6a..0ecb4eeb 100644
--- a/docs/reference/buildx_imagetools_create.md
+++ b/docs/reference/buildx_imagetools_create.md
@@ -22,9 +22,6 @@ Create a new image based on source images
## Description
-Imagetools contains commands for working with manifest lists in the registry.
-These commands are useful for inspecting multi-platform build results.
-
Create a new manifest list based on source manifests. The source manifests can
be manifest lists or single platform distribution manifests and must already
exist in the registry where the new manifest is created. If only one source is
diff --git a/docs/reference/buildx_imagetools_inspect.md b/docs/reference/buildx_imagetools_inspect.md
index 0d68b196..da48341e 100644
--- a/docs/reference/buildx_imagetools_inspect.md
+++ b/docs/reference/buildx_imagetools_inspect.md
@@ -5,7 +5,7 @@ docker buildx imagetools inspect [OPTIONS] NAME
```
-Show details of image in the registry
+Show details of an image in the registry
### Options
@@ -19,33 +19,500 @@ Show details of image in the registry
## Description
-Show details of image in the registry.
+Show details of an image in the registry.
+
+## Examples
+
+### Override the configured builder instance (--builder)
+
+Same as [`buildx --builder`](buildx.md#builder).
+
+### Format the output (--format)
+
+Format the output using the given Go template. At the moment the following
+fields are available:
+
+* `.Name`: provides the reference of the image
+* `.Manifest`: provides manifest or manifest list
+* `.Config`: provides the image config as `application/vnd.oci.image.config.v1+json` mediatype
+* `.BuildInfo`: provides [build info from image config](https://github.com/moby/buildkit/blob/master/docs/build-repro.md#image-config)
+
+#### `.Name`
```console
-$ docker buildx imagetools inspect alpine
+$ docker buildx imagetools inspect alpine --format "{{.Name}}"
+Name: docker.io/library/alpine:latest
+```
+
+#### `.Manifest`
+
+```console
+$ docker buildx imagetools inspect crazymax/loop --format "{{.Manifest}}"
+Name: docker.io/crazymax/loop:latest
+MediaType: application/vnd.docker.distribution.manifest.v2+json
+Digest: sha256:08602e7340970e92bde5e0a2e887c1fde4d9ae753d1e05efb4c8ef3b609f97f1
+```
-Name: docker.io/library/alpine:latest
+```console
+$ docker buildx imagetools inspect moby/buildkit:master --format "{{.Manifest}}"
+Name: docker.io/moby/buildkit:master
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
-Digest: sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913
+Digest: sha256:4e078bb87be98cc2a0fcdac4a05ac934055d563bd3d23e4d714eb03a3f62b49e
Manifests:
- Name: docker.io/library/alpine:latest@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907
+ Name: docker.io/moby/buildkit:master@sha256:bd1e78f06de26610fadf4eb9d04b1a45a545799d6342701726e952cc0c11c912
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
- Name: docker.io/library/alpine:latest@sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204
+ Name: docker.io/moby/buildkit:master@sha256:d37dcced63ec0965824fca644f0ac9efad8569434ec15b4c83adfcb3dcfc743b
+ MediaType: application/vnd.docker.distribution.manifest.v2+json
+ Platform: linux/arm/v7
+
+ Name: docker.io/moby/buildkit:master@sha256:ce142eb2255e6af46f2809e159fd03081697c7605a3de03b9cbe9a52ddb244bf
+ MediaType: application/vnd.docker.distribution.manifest.v2+json
+ Platform: linux/arm64
+
+ Name: docker.io/moby/buildkit:master@sha256:f59bfb5062fff76ce464bfa4e25ebaaaac887d6818238e119d68613c456d360c
+ MediaType: application/vnd.docker.distribution.manifest.v2+json
+ Platform: linux/s390x
+
+ Name: docker.io/moby/buildkit:master@sha256:cc96426e0c50a78105d5637d31356db5dd6ec594f21b24276e534a32da09645c
MediaType: application/vnd.docker.distribution.manifest.v2+json
- Platform: linux/arm/v6
- ...
+ Platform: linux/ppc64le
+
+ Name: docker.io/moby/buildkit:master@sha256:39f9c1e2878e6c333acb23187d6b205ce82ed934c60da326cb2c698192631478
+ MediaType: application/vnd.docker.distribution.manifest.v2+json
+ Platform: linux/riscv64
```
-## Examples
+#### `.BuildInfo`
-### Override the configured builder instance (--builder)
+```console
+$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{.BuildInfo}}"
+Name: docker.io/crazymax/buildx:buildinfo
+Frontend: dockerfile.v0
+Attrs:
+ build-arg:bar: foo
+ build-arg:foo: bar
+ filename: Dockerfile
+ source: crazymax/dockerfile:buildattrs
+Sources:
+ Type: docker-image
+ Ref: docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0
+ Pin: sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0
-Same as [`buildx --builder`](buildx.md#builder).
+ Type: docker-image
+ Ref: docker.io/library/alpine:3.13@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c
+ Pin: sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c
+
+ Type: docker-image
+ Ref: docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab
+ Pin: sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab
+
+ Type: docker-image
+ Ref: docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04
+ Pin: sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04
+
+ Type: http
+ Ref: https://raw.githubusercontent.com/moby/moby/master/README.md
+ Pin: sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c
+```
+
+#### JSON output
+
+A `json` go template func is also available if you want to render fields as
+JSON bytes:
+
+```console
+$ docker buildx imagetools inspect crazymax/loop --format "{{json .Manifest}}"
+```
+```json
+{
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:08602e7340970e92bde5e0a2e887c1fde4d9ae753d1e05efb4c8ef3b609f97f1",
+ "size": 949
+}
+```
+
+```console
+$ docker buildx imagetools inspect moby/buildkit:master --format "{{json .Manifest}}"
+```
+```json
+{
+ "schemaVersion": 2,
+ "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
+ "manifests": [
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:bd1e78f06de26610fadf4eb9d04b1a45a545799d6342701726e952cc0c11c912",
+ "size": 1158,
+ "platform": {
+ "architecture": "amd64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:d37dcced63ec0965824fca644f0ac9efad8569434ec15b4c83adfcb3dcfc743b",
+ "size": 1158,
+ "platform": {
+ "architecture": "arm",
+ "os": "linux",
+ "variant": "v7"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:ce142eb2255e6af46f2809e159fd03081697c7605a3de03b9cbe9a52ddb244bf",
+ "size": 1158,
+ "platform": {
+ "architecture": "arm64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:f59bfb5062fff76ce464bfa4e25ebaaaac887d6818238e119d68613c456d360c",
+ "size": 1158,
+ "platform": {
+ "architecture": "s390x",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:cc96426e0c50a78105d5637d31356db5dd6ec594f21b24276e534a32da09645c",
+ "size": 1159,
+ "platform": {
+ "architecture": "ppc64le",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:39f9c1e2878e6c333acb23187d6b205ce82ed934c60da326cb2c698192631478",
+ "size": 1158,
+ "platform": {
+ "architecture": "riscv64",
+ "os": "linux"
+ }
+ }
+ ]
+}
+```
+
+```console
+$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{json .BuildInfo}}"
+```
+```json
+{
+ "frontend": "dockerfile.v0",
+ "attrs": {
+ "build-arg:bar": "foo",
+ "build-arg:foo": "bar",
+ "filename": "Dockerfile",
+ "source": "crazymax/dockerfile:buildattrs"
+ },
+ "sources": [
+ {
+ "type": "docker-image",
+ "ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0",
+ "pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/library/alpine:3.13@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c",
+ "pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab",
+ "pin": "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04",
+ "pin": "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04"
+ },
+ {
+ "type": "http",
+ "ref": "https://raw.githubusercontent.com/moby/moby/master/README.md",
+ "pin": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c"
+ }
+ ]
+}
+```
+
+```console
+$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{json .}}"
+```
+```json
+{
+ "name": "crazymax/buildx:buildinfo",
+ "manifest": {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:ac98300a6580fa30d9350a868de2a09f5a0190f4ba94e6c0ead8cc892150977c",
+ "size": 2628
+ },
+ "config": {
+ "created": "2022-02-24T12:27:43.627154558Z",
+ "architecture": "amd64",
+ "os": "linux",
+ "config": {
+ "Env": [
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "DOCKER_TLS_CERTDIR=/certs",
+ "DOCKER_CLI_EXPERIMENTAL=enabled"
+ ],
+ "Entrypoint": [
+ "docker-entrypoint.sh"
+ ],
+ "Cmd": [
+ "sh"
+ ]
+ },
+ "rootfs": {
+ "type": "layers",
+ "diff_ids": [
+ "sha256:7fcb75871b2101082203959c83514ac8a9f4ecfee77a0fe9aa73bbe56afdf1b4",
+ "sha256:d3c0b963ff5684160641f936d6a4aa14efc8ff27b6edac255c07f2d03ff92e82",
+ "sha256:3f8d78f13fa9b1f35d3bc3f1351d03a027c38018c37baca73f93eecdea17f244",
+ "sha256:8e6eb1137b182ae0c3f5d40ca46341fda2eaeeeb5fa516a9a2bf96171238e2e0",
+ "sha256:fde4c869a56b54dd76d7352ddaa813fd96202bda30b9dceb2c2f2ad22fa2e6ce",
+ "sha256:52025823edb284321af7846419899234b3c66219bf06061692b709875ed0760f",
+ "sha256:50adb5982dbf6126c7cf279ac3181d1e39fc9116b610b947a3dadae6f7e7c5bc",
+ "sha256:9801c319e1c66c5d295e78b2d3e80547e73c7e3c63a4b71e97c8ca357224af24",
+ "sha256:dfbfac44d5d228c49b42194c8a2f470abd6916d072f612a6fb14318e94fde8ae",
+ "sha256:3dfb74e19dedf61568b917c19b0fd3ee4580870027ca0b6054baf239855d1322",
+ "sha256:b182e707c23e4f19be73f9022a99d2d1ca7bf1ca8f280d40e4d1c10a6f51550e"
+ ]
+ },
+ "history": [
+ {
+ "created": "2021-11-12T17:19:58.698676655Z",
+ "created_by": "/bin/sh -c #(nop) ADD file:5a707b9d6cb5fff532e4c2141bc35707593f21da5528c9e71ae2ddb6ba4a4eb6 in / "
+ },
+ {
+ "created": "2021-11-12T17:19:58.948920855Z",
+ "created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
+ "empty_layer": true
+ },
+ {
+ "created": "2022-02-24T12:27:38.285594601Z",
+ "created_by": "RUN /bin/sh -c apk --update --no-cache add bash ca-certificates openssh-client \u0026\u0026 rm -rf /tmp/* /var/cache/apk/* # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:41.061874167Z",
+ "created_by": "COPY /opt/docker/ /usr/local/bin/ # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:41.174098947Z",
+ "created_by": "COPY /usr/bin/buildctl /usr/local/bin/buildctl # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:41.320343683Z",
+ "created_by": "COPY /usr/bin/buildkit* /usr/local/bin/ # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:41.447149933Z",
+ "created_by": "COPY /buildx /usr/libexec/docker/cli-plugins/docker-buildx # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.057722191Z",
+ "created_by": "COPY /opt/docker-compose /usr/libexec/docker/cli-plugins/docker-compose # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.145224134Z",
+ "created_by": "ADD https://raw.githubusercontent.com/moby/moby/master/README.md / # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.422212427Z",
+ "created_by": "ENV DOCKER_TLS_CERTDIR=/certs",
+ "comment": "buildkit.dockerfile.v0",
+ "empty_layer": true
+ },
+ {
+ "created": "2022-02-24T12:27:43.422212427Z",
+ "created_by": "ENV DOCKER_CLI_EXPERIMENTAL=enabled",
+ "comment": "buildkit.dockerfile.v0",
+ "empty_layer": true
+ },
+ {
+ "created": "2022-02-24T12:27:43.422212427Z",
+ "created_by": "RUN /bin/sh -c docker --version \u0026\u0026 buildkitd --version \u0026\u0026 buildctl --version \u0026\u0026 docker buildx version \u0026\u0026 docker compose version \u0026\u0026 mkdir /certs /certs/client \u0026\u0026 chmod 1777 /certs /certs/client # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.514320155Z",
+ "created_by": "COPY rootfs/modprobe.sh /usr/local/bin/modprobe # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.627154558Z",
+ "created_by": "COPY rootfs/docker-entrypoint.sh /usr/local/bin/ # buildkit",
+ "comment": "buildkit.dockerfile.v0"
+ },
+ {
+ "created": "2022-02-24T12:27:43.627154558Z",
+ "created_by": "ENTRYPOINT [\"docker-entrypoint.sh\"]",
+ "comment": "buildkit.dockerfile.v0",
+ "empty_layer": true
+ },
+ {
+ "created": "2022-02-24T12:27:43.627154558Z",
+ "created_by": "CMD [\"sh\"]",
+ "comment": "buildkit.dockerfile.v0",
+ "empty_layer": true
+ }
+ ]
+ },
+ "buildinfo": {
+ "frontend": "dockerfile.v0",
+ "attrs": {
+ "build-arg:bar": "foo",
+ "build-arg:foo": "bar",
+ "filename": "Dockerfile",
+ "source": "crazymax/dockerfile:buildattrs"
+ },
+ "sources": [
+ {
+ "type": "docker-image",
+ "ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0",
+ "pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/library/alpine:3.13@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c",
+ "pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab",
+ "pin": "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab"
+ },
+ {
+ "type": "docker-image",
+ "ref": "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04",
+ "pin": "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04"
+ },
+ {
+ "type": "http",
+ "ref": "https://raw.githubusercontent.com/moby/moby/master/README.md",
+ "pin": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c"
+ }
+ ]
+ }
+}
+```
### Show original, unformatted JSON manifest (--raw)
-Use the `--raw` option to print the original JSON bytes instead of the formatted
-output.
+Use the `--raw` option to print the unformatted JSON manifest bytes.
+
+> `jq` is used here to get a better rendering of the output result.
+
+```console
+$ docker buildx imagetools inspect --raw crazymax/loop | jq
+```
+```json
+{
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "schemaVersion": 2,
+ "config": {
+ "mediaType": "application/vnd.docker.container.image.v1+json",
+ "digest": "sha256:7ace7d324e79b360b2db8b820d83081863d96d22e734cdf297a8e7fd83f6ceb3",
+ "size": 2298
+ },
+ "layers": [
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "digest": "sha256:5843afab387455b37944e709ee8c78d7520df80f8d01cf7f861aae63beeddb6b",
+ "size": 2811478
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "digest": "sha256:726d3732a87e1c430d67e8969de6b222a889d45e045ebae1a008a37ba38f3b1f",
+ "size": 1776812
+ },
+ {
+ "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
+ "digest": "sha256:5d7cf9b33148a8f220c84f27dd2cfae46aca019a3ea3fbf7274f6d6dbfae8f3b",
+ "size": 382855
+ }
+ ]
+}
+```
+
+```console
+$ docker buildx imagetools inspect --raw moby/buildkit:master | jq
+```
+```json
+{
+ "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
+ "schemaVersion": 2,
+ "manifests": [
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:905307ef07e366e5977163218b9f01e9553fe2c15fe5e4a529328f91b510351d",
+ "size": 1158,
+ "platform": {
+ "architecture": "amd64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:250a6dd96c377a1084cf8ea766916ae643f2fdf0f2e68728b3fda0a4d4669a2e",
+ "size": 1158,
+ "platform": {
+ "architecture": "arm",
+ "os": "linux",
+ "variant": "v7"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:7ee37cac4b2b8b54d28127a10aa783260430a145bafab169cec88b8a20462678",
+ "size": 1158,
+ "platform": {
+ "architecture": "arm64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:0ba8d091f6b346a5c45b06dcb534b6b6945a681fcdf40c195e1d6619115dffd2",
+ "size": 1158,
+ "platform": {
+ "architecture": "s390x",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:964d4b79d1be97ce22e6fe0c29be1d6cc63da10e5b8a21505851014c9846268a",
+ "size": 1159,
+ "platform": {
+ "architecture": "ppc64le",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "digest": "sha256:44d8de2c5f813b48d649a3a6cc348b57387cc22355897a14c7447cbfa03d079c",
+ "size": 1158,
+ "platform": {
+ "architecture": "riscv64",
+ "os": "linux"
+ }
+ }
+ ]
+}
+```
diff --git a/go.mod b/go.mod
index 3793a385..925953ac 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
github.com/jinzhu/gorm v1.9.2 // indirect
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
- github.com/moby/buildkit v0.10.0-rc1.0.20220223192213-b124b0c3f19c
+ github.com/moby/buildkit v0.10.0-rc1.0.20220223201314-b0e56cd5633a
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5
diff --git a/go.sum b/go.sum
index b9ce7628..3c98e380 100644
--- a/go.sum
+++ b/go.sum
@@ -973,8 +973,8 @@ github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7p
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
-github.com/moby/buildkit v0.10.0-rc1.0.20220223192213-b124b0c3f19c h1:87w+w9emZUjhsTFw6JrSi9Cg9TwXjn4JMol/HSj6PrA=
-github.com/moby/buildkit v0.10.0-rc1.0.20220223192213-b124b0c3f19c/go.mod h1:WJRmOULNzLGkGcQaLLVYJiARKvm37cFx23r6oQAPhvQ=
+github.com/moby/buildkit v0.10.0-rc1.0.20220223201314-b0e56cd5633a h1:DKCpVlYCXbP1iJZ7rNSJTzH4N+El12Z6IdVRaNMbQ54=
+github.com/moby/buildkit v0.10.0-rc1.0.20220223201314-b0e56cd5633a/go.mod h1:WJRmOULNzLGkGcQaLLVYJiARKvm37cFx23r6oQAPhvQ=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
diff --git a/util/imagetools/inspect.go b/util/imagetools/inspect.go
index 51577b65..059f25e7 100644
--- a/util/imagetools/inspect.go
+++ b/util/imagetools/inspect.go
@@ -14,7 +14,10 @@ import (
"github.com/docker/buildx/util/resolver"
clitypes "github.com/docker/cli/cli/config/types"
"github.com/docker/distribution/reference"
+ "github.com/moby/buildkit/util/contentutil"
+ "github.com/moby/buildkit/util/imageutil"
"github.com/moby/buildkit/util/tracing"
+ "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)
@@ -29,14 +32,16 @@ type Opt struct {
}
type Resolver struct {
- auth docker.Authorizer
- hosts docker.RegistryHosts
+ auth docker.Authorizer
+ hosts docker.RegistryHosts
+ buffer contentutil.Buffer
}
func New(opt Opt) *Resolver {
return &Resolver{
- auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)),
- hosts: resolver.NewRegistryConfig(opt.RegistryConfig),
+ auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)),
+ hosts: resolver.NewRegistryConfig(opt.RegistryConfig),
+ buffer: contentutil.NewBuffer(),
}
}
@@ -157,3 +162,11 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) {
}
return base64.URLEncoding.EncodeToString(buf), nil
}
+
+func (r *Resolver) ImageConfig(ctx context.Context, in string) (digest.Digest, []byte, error) {
+ in, _, err := r.Resolve(ctx, in)
+ if err != nil {
+ return "", nil, err
+ }
+ return imageutil.Config(ctx, in, r.resolver(), r.buffer, nil, nil)
+}
diff --git a/util/imagetools/printers.go b/util/imagetools/printers.go
index 633b017d..900027b7 100644
--- a/util/imagetools/printers.go
+++ b/util/imagetools/printers.go
@@ -1,72 +1,240 @@
package imagetools
import (
+ "context"
+ "encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
+ "text/template"
+ "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/docker/distribution/reference"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ binfotypes "github.com/moby/buildkit/util/buildinfo/types"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
)
-func PrintManifestList(dt []byte, desc ocispec.Descriptor, refstr string, out io.Writer) error {
- ref, err := parseRef(refstr)
+const defaultPfx = " "
+
+type Printer struct {
+ ctx context.Context
+ resolver *Resolver
+
+ name string
+ format string
+
+ raw []byte
+ ref reference.Named
+ manifest ocispecs.Descriptor
+ index ocispecs.Index
+ image ocispecs.Image
+ binfo binfotypes.BuildInfo
+}
+
+func NewPrinter(ctx context.Context, opt Opt, name string, format string) (*Printer, error) {
+ resolver := New(opt)
+
+ ref, err := parseRef(name)
+ if err != nil {
+ return nil, err
+ }
+
+ dt, manifest, err := resolver.Get(ctx, name)
+ if err != nil {
+ return nil, err
+ }
+
+ var index ocispecs.Index
+ if err = json.Unmarshal(dt, &index); err != nil {
+ return nil, err
+ }
+
+ _, dtcic, err := resolver.ImageConfig(ctx, name)
if err != nil {
+ return nil, err
+ }
+
+ var image ocispecs.Image
+ if err = json.Unmarshal(dtcic, &image); err != nil {
+ return nil, err
+ }
+
+ var binfo binfotypes.BuildInfo
+ if len(dtcic) > 0 {
+ var biconfig binfotypes.ImageConfig
+ if err := json.Unmarshal(dtcic, &biconfig); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal image config")
+ }
+ if len(biconfig.BuildInfo) > 0 {
+ bidec, err := base64.StdEncoding.DecodeString(biconfig.BuildInfo)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to decode build info")
+ }
+ if err = json.Unmarshal(bidec, &binfo); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal build info")
+ }
+ }
+ }
+
+ return &Printer{
+ ctx: ctx,
+ resolver: resolver,
+ name: name,
+ format: format,
+ raw: dt,
+ ref: ref,
+ manifest: manifest,
+ index: index,
+ image: image,
+ binfo: binfo,
+ }, nil
+}
+
+func (p *Printer) Print(raw bool, out io.Writer) error {
+ if raw {
+ _, err := fmt.Fprintf(out, "%s", p.raw) // avoid newline to keep digest
return err
}
- var mfst ocispec.Index
- if err := json.Unmarshal(dt, &mfst); err != nil {
+ tpl, err := template.New("").Funcs(template.FuncMap{
+ "json": func(v interface{}) string {
+ b, _ := json.MarshalIndent(v, "", " ")
+ return string(b)
+ },
+ }).Parse(p.format)
+ if err != nil {
return err
}
- w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
+ format := tpl.Root.String()
- fmt.Fprintf(w, "Name:\t%s\n", ref.String())
- fmt.Fprintf(w, "MediaType:\t%s\n", desc.MediaType)
- fmt.Fprintf(w, "Digest:\t%s\n", desc.Digest)
- fmt.Fprintf(w, "\t\n")
+ var manifest interface{}
+ switch p.manifest.MediaType {
+ case images.MediaTypeDockerSchema2Manifest, ocispecs.MediaTypeImageManifest:
+ manifest = p.manifest
+ case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
+ manifest = p.index
+ }
+ v := struct {
+ Name string `json:"name,omitempty"`
+ Manifest interface{} `json:"manifest,omitempty"`
+ Config ocispecs.Image `json:"config,omitempty"`
+ BuildInfo binfotypes.BuildInfo `json:"buildinfo,omitempty"`
+ }{
+ Name: p.name,
+ Manifest: manifest,
+ Config: p.image,
+ BuildInfo: p.binfo,
+ }
- fmt.Fprintf(w, "Manifests:\t\n")
- w.Flush()
+ switch {
+ case strings.HasPrefix(format, "{{.Manifest"), strings.HasPrefix(format, "{{.Config"), strings.HasPrefix(format, "{{.BuildInfo"):
+ w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
+ _, _ = fmt.Fprintf(w, "Name:\t%s\n", p.ref.String())
+ _ = w.Flush()
+ if strings.HasPrefix(format, "{{.Manifest") {
+ _, _ = fmt.Fprintf(w, "MediaType:\t%s\n", p.manifest.MediaType)
+ _, _ = fmt.Fprintf(w, "Digest:\t%s\n", p.manifest.Digest)
+ _ = w.Flush()
+ switch p.manifest.MediaType {
+ case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
+ _ = p.printManifestList(out)
+ }
+ } else if strings.HasPrefix(format, "{{.Config") {
+ // TODO: print formatted config
+ } else if strings.HasPrefix(format, "{{.BuildInfo") {
+ _ = p.printBuildInfo(p.binfo, "", out)
+ }
+ default:
+ return tpl.Execute(out, v)
+ }
- pfx := " "
+ return nil
+}
+
+func (p *Printer) printManifestList(out io.Writer) error {
+ w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
+ _, _ = fmt.Fprintf(w, "\t\n")
+ _, _ = fmt.Fprintf(w, "Manifests:\t\n")
+ _ = w.Flush()
w = tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
- for i, m := range mfst.Manifests {
+ for i, m := range p.index.Manifests {
if i != 0 {
- fmt.Fprintf(w, "\t\n")
+ _, _ = fmt.Fprintf(w, "\t\n")
}
- cr, err := reference.WithDigest(ref, m.Digest)
+ cr, err := reference.WithDigest(p.ref, m.Digest)
if err != nil {
return err
}
- fmt.Fprintf(w, "%sName:\t%s\n", pfx, cr.String())
- fmt.Fprintf(w, "%sMediaType:\t%s\n", pfx, m.MediaType)
+ _, _ = fmt.Fprintf(w, "%sName:\t%s\n", defaultPfx, cr.String())
+ _, _ = fmt.Fprintf(w, "%sMediaType:\t%s\n", defaultPfx, m.MediaType)
if p := m.Platform; p != nil {
- fmt.Fprintf(w, "%sPlatform:\t%s\n", pfx, platforms.Format(*p))
+ _, _ = fmt.Fprintf(w, "%sPlatform:\t%s\n", defaultPfx, platforms.Format(*p))
if p.OSVersion != "" {
- fmt.Fprintf(w, "%sOSVersion:\t%s\n", pfx, p.OSVersion)
+ _, _ = fmt.Fprintf(w, "%sOSVersion:\t%s\n", defaultPfx, p.OSVersion)
}
if len(p.OSFeatures) > 0 {
- fmt.Fprintf(w, "%sOSFeatures:\t%s\n", pfx, strings.Join(p.OSFeatures, ", "))
+ _, _ = fmt.Fprintf(w, "%sOSFeatures:\t%s\n", defaultPfx, strings.Join(p.OSFeatures, ", "))
}
if len(m.URLs) > 0 {
- fmt.Fprintf(w, "%sURLs:\t%s\n", pfx, strings.Join(m.URLs, ", "))
+ _, _ = fmt.Fprintf(w, "%sURLs:\t%s\n", defaultPfx, strings.Join(m.URLs, ", "))
}
if len(m.Annotations) > 0 {
- w.Flush()
+ _ = w.Flush()
w2 := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
- pfx2 := pfx + " "
for k, v := range m.Annotations {
- fmt.Fprintf(w2, "%s%s:\t%s\n", pfx2, k, v)
+ _, _ = fmt.Fprintf(w2, "%s%s:\t%s\n", defaultPfx+defaultPfx, k, v)
}
- w2.Flush()
+ _ = w2.Flush()
+ }
+ }
+ }
+ return w.Flush()
+}
+
+func (p *Printer) printBuildInfo(bi binfotypes.BuildInfo, pfx string, out io.Writer) error {
+ w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
+ _, _ = fmt.Fprintf(w, "%sFrontend:\t%s\n", pfx, bi.Frontend)
+
+ if len(bi.Attrs) > 0 {
+ _, _ = fmt.Fprintf(w, "%sAttrs:\t\n", pfx)
+ _ = w.Flush()
+ for k, v := range bi.Attrs {
+ _, _ = fmt.Fprintf(w, "%s%s:\t%s\n", pfx+defaultPfx, k, *v)
+ }
+ }
+
+ if len(bi.Sources) > 0 {
+ _, _ = fmt.Fprintf(w, "%sSources:\t\n", pfx)
+ _ = w.Flush()
+ for i, v := range bi.Sources {
+ if i != 0 {
+ _, _ = fmt.Fprintf(w, "\t\n")
+ }
+ _, _ = fmt.Fprintf(w, "%sType:\t%s\n", pfx+defaultPfx, v.Type)
+ _, _ = fmt.Fprintf(w, "%sRef:\t%s\n", pfx+defaultPfx, v.Ref)
+ _, _ = fmt.Fprintf(w, "%sPin:\t%s\n", pfx+defaultPfx, v.Pin)
+ }
+ }
+
+ if len(bi.Deps) > 0 {
+ _, _ = fmt.Fprintf(w, "%sDeps:\t\n", pfx)
+ _ = w.Flush()
+ firstPass := true
+ for k, v := range bi.Deps {
+ if !firstPass {
+ _, _ = fmt.Fprintf(w, "\t\n")
}
+ _, _ = fmt.Fprintf(w, "%sName:\t%s\n", pfx+defaultPfx, k)
+ _ = w.Flush()
+ _ = p.printBuildInfo(v, pfx+defaultPfx, out)
+ firstPass = false
}
}
diff --git a/vendor/github.com/containerd/containerd/identifiers/validate.go b/vendor/github.com/containerd/containerd/identifiers/validate.go
new file mode 100644
index 00000000..cbd3a52b
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/identifiers/validate.go
@@ -0,0 +1,73 @@
+/*
+ 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 identifiers provides common validation for identifiers and keys
+// across containerd.
+//
+// Identifiers in containerd must be a alphanumeric, allowing limited
+// underscores, dashes and dots.
+//
+// While the character set may be expanded in the future, identifiers
+// are guaranteed to be safely used as filesystem path components.
+package identifiers
+
+import (
+ "fmt"
+ "regexp"
+
+ "github.com/containerd/containerd/errdefs"
+)
+
+const (
+ maxLength = 76
+ alphanum = `[A-Za-z0-9]+`
+ separators = `[._-]`
+)
+
+var (
+ // identifierRe defines the pattern for valid identifiers.
+ identifierRe = regexp.MustCompile(reAnchor(alphanum + reGroup(separators+reGroup(alphanum)) + "*"))
+)
+
+// Validate returns nil if the string s is a valid identifier.
+//
+// identifiers are similar to the domain name rules according to RFC 1035, section 2.3.1. However
+// rules in this package are relaxed to allow numerals to follow period (".") and mixed case is
+// allowed.
+//
+// In general identifiers that pass this validation should be safe for use as filesystem path components.
+func Validate(s string) error {
+ if len(s) == 0 {
+ return fmt.Errorf("identifier must not be empty: %w", errdefs.ErrInvalidArgument)
+ }
+
+ if len(s) > maxLength {
+ return fmt.Errorf("identifier %q greater than maximum length (%d characters): %w", s, maxLength, errdefs.ErrInvalidArgument)
+ }
+
+ if !identifierRe.MatchString(s) {
+ return fmt.Errorf("identifier %q must match %v: %w", s, identifierRe, errdefs.ErrInvalidArgument)
+ }
+ return nil
+}
+
+func reGroup(s string) string {
+ return `(?:` + s + `)`
+}
+
+func reAnchor(s string) string {
+ return `^` + s + `$`
+}
diff --git a/vendor/github.com/containerd/containerd/leases/context.go b/vendor/github.com/containerd/containerd/leases/context.go
new file mode 100644
index 00000000..599c549d
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/leases/context.go
@@ -0,0 +1,40 @@
+/*
+ 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 leases
+
+import "context"
+
+type leaseKey struct{}
+
+// WithLease sets a given lease on the context
+func WithLease(ctx context.Context, lid string) context.Context {
+ ctx = context.WithValue(ctx, leaseKey{}, lid)
+
+ // also store on the grpc headers so it gets picked up by any clients that
+ // are using this.
+ return withGRPCLeaseHeader(ctx, lid)
+}
+
+// FromContext returns the lease from the context.
+func FromContext(ctx context.Context) (string, bool) {
+ lid, ok := ctx.Value(leaseKey{}).(string)
+ if !ok {
+ return fromGRPCHeader(ctx)
+ }
+
+ return lid, ok
+}
diff --git a/vendor/github.com/containerd/containerd/leases/grpc.go b/vendor/github.com/containerd/containerd/leases/grpc.go
new file mode 100644
index 00000000..22f287a8
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/leases/grpc.go
@@ -0,0 +1,58 @@
+/*
+ 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 leases
+
+import (
+ "context"
+
+ "google.golang.org/grpc/metadata"
+)
+
+const (
+ // GRPCHeader defines the header name for specifying a containerd lease.
+ GRPCHeader = "containerd-lease"
+)
+
+func withGRPCLeaseHeader(ctx context.Context, lid string) context.Context {
+ // also store on the grpc headers so it gets picked up by any clients
+ // that are using this.
+ txheader := metadata.Pairs(GRPCHeader, lid)
+ md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context.
+ if !ok {
+ md = txheader
+ } else {
+ // order ensures the latest is first in this list.
+ md = metadata.Join(txheader, md)
+ }
+
+ return metadata.NewOutgoingContext(ctx, md)
+}
+
+func fromGRPCHeader(ctx context.Context) (string, bool) {
+ // try to extract for use in grpc servers.
+ md, ok := metadata.FromIncomingContext(ctx)
+ if !ok {
+ return "", false
+ }
+
+ values := md[GRPCHeader]
+ if len(values) == 0 {
+ return "", false
+ }
+
+ return values[0], true
+}
diff --git a/vendor/github.com/containerd/containerd/leases/id.go b/vendor/github.com/containerd/containerd/leases/id.go
new file mode 100644
index 00000000..8781a1d7
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/leases/id.go
@@ -0,0 +1,43 @@
+/*
+ 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 leases
+
+import (
+ "encoding/base64"
+ "fmt"
+ "math/rand"
+ "time"
+)
+
+// WithRandomID sets the lease ID to a random unique value
+func WithRandomID() Opt {
+ return func(l *Lease) error {
+ t := time.Now()
+ var b [3]byte
+ rand.Read(b[:])
+ l.ID = fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
+ return nil
+ }
+}
+
+// WithID sets the ID for the lease
+func WithID(id string) Opt {
+ return func(l *Lease) error {
+ l.ID = id
+ return nil
+ }
+}
diff --git a/vendor/github.com/containerd/containerd/leases/lease.go b/vendor/github.com/containerd/containerd/leases/lease.go
new file mode 100644
index 00000000..058d0655
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/leases/lease.go
@@ -0,0 +1,86 @@
+/*
+ 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 leases
+
+import (
+ "context"
+ "time"
+)
+
+// Opt is used to set options on a lease
+type Opt func(*Lease) error
+
+// DeleteOpt allows configuring a delete operation
+type DeleteOpt func(context.Context, *DeleteOptions) error
+
+// Manager is used to create, list, and remove leases
+type Manager interface {
+ Create(context.Context, ...Opt) (Lease, error)
+ Delete(context.Context, Lease, ...DeleteOpt) error
+ List(context.Context, ...string) ([]Lease, error)
+ AddResource(context.Context, Lease, Resource) error
+ DeleteResource(context.Context, Lease, Resource) error
+ ListResources(context.Context, Lease) ([]Resource, error)
+}
+
+// Lease retains resources to prevent cleanup before
+// the resources can be fully referenced.
+type Lease struct {
+ ID string
+ CreatedAt time.Time
+ Labels map[string]string
+}
+
+// Resource represents low level resource of image, like content, ingest and
+// snapshotter.
+type Resource struct {
+ ID string
+ Type string
+}
+
+// DeleteOptions provide options on image delete
+type DeleteOptions struct {
+ Synchronous bool
+}
+
+// SynchronousDelete is used to indicate that a lease deletion and removal of
+// any unreferenced resources should occur synchronously before returning the
+// result.
+func SynchronousDelete(ctx context.Context, o *DeleteOptions) error {
+ o.Synchronous = true
+ return nil
+}
+
+// WithLabels sets labels on a lease
+func WithLabels(labels map[string]string) Opt {
+ return func(l *Lease) error {
+ l.Labels = labels
+ return nil
+ }
+}
+
+// WithExpiration sets an expiration on the lease
+func WithExpiration(d time.Duration) Opt {
+ return func(l *Lease) error {
+ if l.Labels == nil {
+ l.Labels = map[string]string{}
+ }
+ l.Labels["containerd.io/gc.expire"] = time.Now().Add(d).Format(time.RFC3339)
+
+ return nil
+ }
+}
diff --git a/vendor/github.com/containerd/containerd/namespaces/context.go b/vendor/github.com/containerd/containerd/namespaces/context.go
new file mode 100644
index 00000000..e5e23fe4
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/namespaces/context.go
@@ -0,0 +1,78 @@
+/*
+ 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 namespaces
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/containerd/containerd/errdefs"
+ "github.com/containerd/containerd/identifiers"
+)
+
+const (
+ // NamespaceEnvVar is the environment variable key name
+ NamespaceEnvVar = "CONTAINERD_NAMESPACE"
+ // Default is the name of the default namespace
+ Default = "default"
+)
+
+type namespaceKey struct{}
+
+// WithNamespace sets a given namespace on the context
+func WithNamespace(ctx context.Context, namespace string) context.Context {
+ ctx = context.WithValue(ctx, namespaceKey{}, namespace) // set our key for namespace
+ // also store on the grpc and ttrpc headers so it gets picked up by any clients that
+ // are using this.
+ return withTTRPCNamespaceHeader(withGRPCNamespaceHeader(ctx, namespace), namespace)
+}
+
+// NamespaceFromEnv uses the namespace defined in CONTAINERD_NAMESPACE or
+// default
+func NamespaceFromEnv(ctx context.Context) context.Context {
+ namespace := os.Getenv(NamespaceEnvVar)
+ if namespace == "" {
+ namespace = Default
+ }
+ return WithNamespace(ctx, namespace)
+}
+
+// Namespace returns the namespace from the context.
+//
+// The namespace is not guaranteed to be valid.
+func Namespace(ctx context.Context) (string, bool) {
+ namespace, ok := ctx.Value(namespaceKey{}).(string)
+ if !ok {
+ if namespace, ok = fromGRPCHeader(ctx); !ok {
+ return fromTTRPCHeader(ctx)
+ }
+ }
+ return namespace, ok
+}
+
+// NamespaceRequired returns the valid namespace from the context or an error.
+func NamespaceRequired(ctx context.Context) (string, error) {
+ namespace, ok := Namespace(ctx)
+ if !ok || namespace == "" {
+ return "", fmt.Errorf("namespace is required: %w", errdefs.ErrFailedPrecondition)
+ }
+ if err := identifiers.Validate(namespace); err != nil {
+ return "", fmt.Errorf("namespace validation: %w", err)
+ }
+ return namespace, nil
+}
diff --git a/vendor/github.com/containerd/containerd/namespaces/grpc.go b/vendor/github.com/containerd/containerd/namespaces/grpc.go
new file mode 100644
index 00000000..6991460d
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/namespaces/grpc.go
@@ -0,0 +1,61 @@
+/*
+ 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 namespaces
+
+import (
+ "context"
+
+ "google.golang.org/grpc/metadata"
+)
+
+const (
+ // GRPCHeader defines the header name for specifying a containerd namespace.
+ GRPCHeader = "containerd-namespace"
+)
+
+// NOTE(stevvooe): We can stub this file out if we don't want a grpc dependency here.
+
+func withGRPCNamespaceHeader(ctx context.Context, namespace string) context.Context {
+ // also store on the grpc headers so it gets picked up by any clients that
+ // are using this.
+ nsheader := metadata.Pairs(GRPCHeader, namespace)
+ md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context.
+ if !ok {
+ md = nsheader
+ } else {
+ // order ensures the latest is first in this list.
+ md = metadata.Join(nsheader, md)
+ }
+
+ return metadata.NewOutgoingContext(ctx, md)
+}
+
+func fromGRPCHeader(ctx context.Context) (string, bool) {
+ // try to extract for use in grpc servers.
+ md, ok := metadata.FromIncomingContext(ctx)
+ if !ok {
+ // TODO(stevvooe): Check outgoing context?
+ return "", false
+ }
+
+ values := md[GRPCHeader]
+ if len(values) == 0 {
+ return "", false
+ }
+
+ return values[0], true
+}
diff --git a/vendor/github.com/containerd/containerd/namespaces/store.go b/vendor/github.com/containerd/containerd/namespaces/store.go
new file mode 100644
index 00000000..5936772c
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/namespaces/store.go
@@ -0,0 +1,46 @@
+/*
+ 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 namespaces
+
+import "context"
+
+// Store provides introspection about namespaces.
+//
+// Note that these are slightly different than other objects, which are record
+// oriented. A namespace is really just a name and a set of labels. Objects
+// that belong to a namespace are returned when the namespace is assigned to a
+// given context.
+//
+//
+type Store interface {
+ Create(ctx context.Context, namespace string, labels map[string]string) error
+ Labels(ctx context.Context, namespace string) (map[string]string, error)
+ SetLabel(ctx context.Context, namespace, key, value string) error
+ List(ctx context.Context) ([]string, error)
+
+ // Delete removes the namespace. The namespace must be empty to be deleted.
+ Delete(ctx context.Context, namespace string, opts ...DeleteOpts) error
+}
+
+// DeleteInfo specifies information for the deletion of a namespace
+type DeleteInfo struct {
+ // Name of the namespace
+ Name string
+}
+
+// DeleteOpts allows the caller to set options for namespace deletion
+type DeleteOpts func(context.Context, *DeleteInfo) error
diff --git a/vendor/github.com/containerd/containerd/namespaces/ttrpc.go b/vendor/github.com/containerd/containerd/namespaces/ttrpc.go
new file mode 100644
index 00000000..bcd2643c
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/namespaces/ttrpc.go
@@ -0,0 +1,51 @@
+/*
+ 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 namespaces
+
+import (
+ "context"
+
+ "github.com/containerd/ttrpc"
+)
+
+const (
+ // TTRPCHeader defines the header name for specifying a containerd namespace
+ TTRPCHeader = "containerd-namespace-ttrpc"
+)
+
+func copyMetadata(src ttrpc.MD) ttrpc.MD {
+ md := ttrpc.MD{}
+ for k, v := range src {
+ md[k] = append(md[k], v...)
+ }
+ return md
+}
+
+func withTTRPCNamespaceHeader(ctx context.Context, namespace string) context.Context {
+ md, ok := ttrpc.GetMetadata(ctx)
+ if !ok {
+ md = ttrpc.MD{}
+ } else {
+ md = copyMetadata(md)
+ }
+ md.Set(TTRPCHeader, namespace)
+ return ttrpc.WithMetadata(ctx, md)
+}
+
+func fromTTRPCHeader(ctx context.Context) (string, bool) {
+ return ttrpc.GetMetadataValue(ctx, TTRPCHeader)
+}
diff --git a/vendor/github.com/containerd/ttrpc/.gitignore b/vendor/github.com/containerd/ttrpc/.gitignore
new file mode 100644
index 00000000..ea58090b
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/.gitignore
@@ -0,0 +1,11 @@
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/containerd/ttrpc/LICENSE b/vendor/github.com/containerd/ttrpc/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/vendor/github.com/containerd/ttrpc/README.md b/vendor/github.com/containerd/ttrpc/README.md
new file mode 100644
index 00000000..547a1297
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/README.md
@@ -0,0 +1,58 @@
+# ttrpc
+
+[![Build Status](https://github.com/containerd/ttrpc/workflows/CI/badge.svg)](https://github.com/containerd/ttrpc/actions?query=workflow%3ACI)
+[![codecov](https://codecov.io/gh/containerd/ttrpc/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/ttrpc)
+
+GRPC for low-memory environments.
+
+The existing grpc-go project requires a lot of memory overhead for importing
+packages and at runtime. While this is great for many services with low density
+requirements, this can be a problem when running a large number of services on
+a single machine or on a machine with a small amount of memory.
+
+Using the same GRPC definitions, this project reduces the binary size and
+protocol overhead required. We do this by eliding the `net/http`, `net/http2`
+and `grpc` package used by grpc replacing it with a lightweight framing
+protocol. The result are smaller binaries that use less resident memory with
+the same ease of use as GRPC.
+
+Please note that while this project supports generating either end of the
+protocol, the generated service definitions will be incompatible with regular
+GRPC services, as they do not speak the same protocol.
+
+# Usage
+
+Create a gogo vanity binary (see
+[`cmd/protoc-gen-gogottrpc/main.go`](cmd/protoc-gen-gogottrpc/main.go) for an
+example with the ttrpc plugin enabled.
+
+It's recommended to use [`protobuild`](https://github.com//stevvooe/protobuild)
+to build the protobufs for this project, but this will work with protoc
+directly, if required.
+
+# Differences from GRPC
+
+- The protocol stack has been replaced with a lighter protocol that doesn't
+ require http, http2 and tls.
+- The client and server interface are identical whereas in GRPC there is a
+ client and server interface that are different.
+- The Go stdlib context package is used instead.
+- No support for streams yet.
+
+# Status
+
+TODO:
+
+- [ ] Document protocol layout
+- [ ] Add testing under concurrent load to ensure
+- [ ] Verify connection error handling
+
+# Project details
+
+ttrpc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+ * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.
diff --git a/vendor/github.com/containerd/ttrpc/channel.go b/vendor/github.com/containerd/ttrpc/channel.go
new file mode 100644
index 00000000..81116a5e
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/channel.go
@@ -0,0 +1,153 @@
+/*
+ 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 ttrpc
+
+import (
+ "bufio"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "sync"
+
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+const (
+ messageHeaderLength = 10
+ messageLengthMax = 4 << 20
+)
+
+type messageType uint8
+
+const (
+ messageTypeRequest messageType = 0x1
+ messageTypeResponse messageType = 0x2
+)
+
+// messageHeader represents the fixed-length message header of 10 bytes sent
+// with every request.
+type messageHeader struct {
+ Length uint32 // length excluding this header. b[:4]
+ StreamID uint32 // identifies which request stream message is a part of. b[4:8]
+ Type messageType // message type b[8]
+ Flags uint8 // reserved b[9]
+}
+
+func readMessageHeader(p []byte, r io.Reader) (messageHeader, error) {
+ _, err := io.ReadFull(r, p[:messageHeaderLength])
+ if err != nil {
+ return messageHeader{}, err
+ }
+
+ return messageHeader{
+ Length: binary.BigEndian.Uint32(p[:4]),
+ StreamID: binary.BigEndian.Uint32(p[4:8]),
+ Type: messageType(p[8]),
+ Flags: p[9],
+ }, nil
+}
+
+func writeMessageHeader(w io.Writer, p []byte, mh messageHeader) error {
+ binary.BigEndian.PutUint32(p[:4], mh.Length)
+ binary.BigEndian.PutUint32(p[4:8], mh.StreamID)
+ p[8] = byte(mh.Type)
+ p[9] = mh.Flags
+
+ _, err := w.Write(p[:])
+ return err
+}
+
+var buffers sync.Pool
+
+type channel struct {
+ conn net.Conn
+ bw *bufio.Writer
+ br *bufio.Reader
+ hrbuf [messageHeaderLength]byte // avoid alloc when reading header
+ hwbuf [messageHeaderLength]byte
+}
+
+func newChannel(conn net.Conn) *channel {
+ return &channel{
+ conn: conn,
+ bw: bufio.NewWriter(conn),
+ br: bufio.NewReader(conn),
+ }
+}
+
+// recv a message from the channel. The returned buffer contains the message.
+//
+// If a valid grpc status is returned, the message header
+// returned will be valid and caller should send that along to
+// the correct consumer. The bytes on the underlying channel
+// will be discarded.
+func (ch *channel) recv() (messageHeader, []byte, error) {
+ mh, err := readMessageHeader(ch.hrbuf[:], ch.br)
+ if err != nil {
+ return messageHeader{}, nil, err
+ }
+
+ if mh.Length > uint32(messageLengthMax) {
+ if _, err := ch.br.Discard(int(mh.Length)); err != nil {
+ return mh, nil, fmt.Errorf("failed to discard after receiving oversized message: %w", err)
+ }
+
+ return mh, nil, status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", mh.Length, messageLengthMax)
+ }
+
+ p := ch.getmbuf(int(mh.Length))
+ if _, err := io.ReadFull(ch.br, p); err != nil {
+ return messageHeader{}, nil, fmt.Errorf("failed reading message: %w", err)
+ }
+
+ return mh, p, nil
+}
+
+func (ch *channel) send(streamID uint32, t messageType, p []byte) error {
+ if err := writeMessageHeader(ch.bw, ch.hwbuf[:], messageHeader{Length: uint32(len(p)), StreamID: streamID, Type: t}); err != nil {
+ return err
+ }
+
+ _, err := ch.bw.Write(p)
+ if err != nil {
+ return err
+ }
+
+ return ch.bw.Flush()
+}
+
+func (ch *channel) getmbuf(size int) []byte {
+ // we can't use the standard New method on pool because we want to allocate
+ // based on size.
+ b, ok := buffers.Get().(*[]byte)
+ if !ok || cap(*b) < size {
+ // TODO(stevvooe): It may be better to allocate these in fixed length
+ // buckets to reduce fragmentation but its not clear that would help
+ // with performance. An ilogb approach or similar would work well.
+ bb := make([]byte, size)
+ b = &bb
+ } else {
+ *b = (*b)[:size]
+ }
+ return *b
+}
+
+func (ch *channel) putmbuf(p []byte) {
+ buffers.Put(&p)
+}
diff --git a/vendor/github.com/containerd/ttrpc/client.go b/vendor/github.com/containerd/ttrpc/client.go
new file mode 100644
index 00000000..26c3dd2a
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/client.go
@@ -0,0 +1,409 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net"
+ "os"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/gogo/protobuf/proto"
+ "github.com/sirupsen/logrus"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// ErrClosed is returned by client methods when the underlying connection is
+// closed.
+var ErrClosed = errors.New("ttrpc: closed")
+
+// Client for a ttrpc server
+type Client struct {
+ codec codec
+ conn net.Conn
+ channel *channel
+ calls chan *callRequest
+
+ ctx context.Context
+ closed func()
+
+ closeOnce sync.Once
+ userCloseFunc func()
+ userCloseWaitCh chan struct{}
+
+ errOnce sync.Once
+ err error
+ interceptor UnaryClientInterceptor
+}
+
+// ClientOpts configures a client
+type ClientOpts func(c *Client)
+
+// WithOnClose sets the close func whenever the client's Close() method is called
+func WithOnClose(onClose func()) ClientOpts {
+ return func(c *Client) {
+ c.userCloseFunc = onClose
+ }
+}
+
+// WithUnaryClientInterceptor sets the provided client interceptor
+func WithUnaryClientInterceptor(i UnaryClientInterceptor) ClientOpts {
+ return func(c *Client) {
+ c.interceptor = i
+ }
+}
+
+func NewClient(conn net.Conn, opts ...ClientOpts) *Client {
+ ctx, cancel := context.WithCancel(context.Background())
+ c := &Client{
+ codec: codec{},
+ conn: conn,
+ channel: newChannel(conn),
+ calls: make(chan *callRequest),
+ closed: cancel,
+ ctx: ctx,
+ userCloseFunc: func() {},
+ userCloseWaitCh: make(chan struct{}),
+ interceptor: defaultClientInterceptor,
+ }
+
+ for _, o := range opts {
+ o(c)
+ }
+
+ go c.run()
+ return c
+}
+
+type callRequest struct {
+ ctx context.Context
+ req *Request
+ resp *Response // response will be written back here
+ errs chan error // error written here on completion
+}
+
+func (c *Client) Call(ctx context.Context, service, method string, req, resp interface{}) error {
+ payload, err := c.codec.Marshal(req)
+ if err != nil {
+ return err
+ }
+
+ var (
+ creq = &Request{
+ Service: service,
+ Method: method,
+ Payload: payload,
+ }
+
+ cresp = &Response{}
+ )
+
+ if metadata, ok := GetMetadata(ctx); ok {
+ metadata.setRequest(creq)
+ }
+
+ if dl, ok := ctx.Deadline(); ok {
+ creq.TimeoutNano = dl.Sub(time.Now()).Nanoseconds()
+ }
+
+ info := &UnaryClientInfo{
+ FullMethod: fullPath(service, method),
+ }
+ if err := c.interceptor(ctx, creq, cresp, info, c.dispatch); err != nil {
+ return err
+ }
+
+ if err := c.codec.Unmarshal(cresp.Payload, resp); err != nil {
+ return err
+ }
+
+ if cresp.Status != nil && cresp.Status.Code != int32(codes.OK) {
+ return status.ErrorProto(cresp.Status)
+ }
+ return nil
+}
+
+func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) error {
+ errs := make(chan error, 1)
+ call := &callRequest{
+ ctx: ctx,
+ req: req,
+ resp: resp,
+ errs: errs,
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case c.calls <- call:
+ case <-c.ctx.Done():
+ return c.error()
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case err := <-errs:
+ return filterCloseErr(err)
+ case <-c.ctx.Done():
+ return c.error()
+ }
+}
+
+func (c *Client) Close() error {
+ c.closeOnce.Do(func() {
+ c.closed()
+ })
+ return nil
+}
+
+// UserOnCloseWait is used to blocks untils the user's on-close callback
+// finishes.
+func (c *Client) UserOnCloseWait(ctx context.Context) error {
+ select {
+ case <-c.userCloseWaitCh:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
+
+type message struct {
+ messageHeader
+ p []byte
+ err error
+}
+
+// callMap provides access to a map of active calls, guarded by a mutex.
+type callMap struct {
+ m sync.Mutex
+ activeCalls map[uint32]*callRequest
+ closeErr error
+}
+
+// newCallMap returns a new callMap with an empty set of active calls.
+func newCallMap() *callMap {
+ return &callMap{
+ activeCalls: make(map[uint32]*callRequest),
+ }
+}
+
+// set adds a call entry to the map with the given streamID key.
+func (cm *callMap) set(streamID uint32, cr *callRequest) error {
+ cm.m.Lock()
+ defer cm.m.Unlock()
+ if cm.closeErr != nil {
+ return cm.closeErr
+ }
+ cm.activeCalls[streamID] = cr
+ return nil
+}
+
+// get looks up the call entry for the given streamID key, then removes it
+// from the map and returns it.
+func (cm *callMap) get(streamID uint32) (cr *callRequest, ok bool, err error) {
+ cm.m.Lock()
+ defer cm.m.Unlock()
+ if cm.closeErr != nil {
+ return nil, false, cm.closeErr
+ }
+ cr, ok = cm.activeCalls[streamID]
+ if ok {
+ delete(cm.activeCalls, streamID)
+ }
+ return
+}
+
+// abort sends the given error to each active call, and clears the map.
+// Once abort has been called, any subsequent calls to the callMap will return the error passed to abort.
+func (cm *callMap) abort(err error) error {
+ cm.m.Lock()
+ defer cm.m.Unlock()
+ if cm.closeErr != nil {
+ return cm.closeErr
+ }
+ for streamID, call := range cm.activeCalls {
+ call.errs <- err
+ delete(cm.activeCalls, streamID)
+ }
+ cm.closeErr = err
+ return nil
+}
+
+func (c *Client) run() {
+ var (
+ waiters = newCallMap()
+ receiverDone = make(chan struct{})
+ )
+
+ // Sender goroutine
+ // Receives calls from dispatch, adds them to the set of active calls, and sends them
+ // to the server.
+ go func() {
+ var streamID uint32 = 1
+ for {
+ select {
+ case <-c.ctx.Done():
+ return
+ case call := <-c.calls:
+ id := streamID
+ streamID += 2 // enforce odd client initiated request ids
+ if err := waiters.set(id, call); err != nil {
+ call.errs <- err // errs is buffered so should not block.
+ continue
+ }
+ if err := c.send(id, messageTypeRequest, call.req); err != nil {
+ call.errs <- err // errs is buffered so should not block.
+ waiters.get(id) // remove from waiters set
+ }
+ }
+ }
+ }()
+
+ // Receiver goroutine
+ // Receives responses from the server, looks up the call info in the set of active calls,
+ // and notifies the caller of the response.
+ go func() {
+ defer close(receiverDone)
+ for {
+ select {
+ case <-c.ctx.Done():
+ c.setError(c.ctx.Err())
+ return
+ default:
+ mh, p, err := c.channel.recv()
+ if err != nil {
+ _, ok := status.FromError(err)
+ if !ok {
+ // treat all errors that are not an rpc status as terminal.
+ // all others poison the connection.
+ c.setError(filterCloseErr(err))
+ return
+ }
+ }
+ msg := &message{
+ messageHeader: mh,
+ p: p[:mh.Length],
+ err: err,
+ }
+ call, ok, err := waiters.get(mh.StreamID)
+ if err != nil {
+ logrus.Errorf("ttrpc: failed to look up active call: %s", err)
+ continue
+ }
+ if !ok {
+ logrus.Errorf("ttrpc: received message for unknown channel %v", mh.StreamID)
+ continue
+ }
+ call.errs <- c.recv(call.resp, msg)
+ }
+ }
+ }()
+
+ defer func() {
+ c.conn.Close()
+ c.userCloseFunc()
+ close(c.userCloseWaitCh)
+ }()
+
+ for {
+ select {
+ case <-receiverDone:
+ // The receiver has exited.
+ // don't return out, let the close of the context trigger the abort of waiters
+ c.Close()
+ case <-c.ctx.Done():
+ // Abort all active calls. This will also prevent any new calls from being added
+ // to waiters.
+ waiters.abort(c.error())
+ return
+ }
+ }
+}
+
+func (c *Client) error() error {
+ c.errOnce.Do(func() {
+ if c.err == nil {
+ c.err = ErrClosed
+ }
+ })
+ return c.err
+}
+
+func (c *Client) setError(err error) {
+ c.errOnce.Do(func() {
+ c.err = err
+ })
+}
+
+func (c *Client) send(streamID uint32, mtype messageType, msg interface{}) error {
+ p, err := c.codec.Marshal(msg)
+ if err != nil {
+ return err
+ }
+
+ return c.channel.send(streamID, mtype, p)
+}
+
+func (c *Client) recv(resp *Response, msg *message) error {
+ if msg.err != nil {
+ return msg.err
+ }
+
+ if msg.Type != messageTypeResponse {
+ return errors.New("unknown message type received")
+ }
+
+ defer c.channel.putmbuf(msg.p)
+ return proto.Unmarshal(msg.p, resp)
+}
+
+// filterCloseErr rewrites EOF and EPIPE errors to ErrClosed. Use when
+// returning from call or handling errors from main read loop.
+//
+// This purposely ignores errors with a wrapped cause.
+func filterCloseErr(err error) error {
+ switch {
+ case err == nil:
+ return nil
+ case err == io.EOF:
+ return ErrClosed
+ case errors.Is(err, io.EOF):
+ return ErrClosed
+ case strings.Contains(err.Error(), "use of closed network connection"):
+ return ErrClosed
+ default:
+ // if we have an epipe on a write or econnreset on a read , we cast to errclosed
+ var oerr *net.OpError
+ if errors.As(err, &oerr) && (oerr.Op == "write" || oerr.Op == "read") {
+ serr, sok := oerr.Err.(*os.SyscallError)
+ if sok && ((serr.Err == syscall.EPIPE && oerr.Op == "write") ||
+ (serr.Err == syscall.ECONNRESET && oerr.Op == "read")) {
+
+ return ErrClosed
+ }
+ }
+ }
+
+ return err
+}
diff --git a/vendor/github.com/containerd/ttrpc/codec.go b/vendor/github.com/containerd/ttrpc/codec.go
new file mode 100644
index 00000000..880634c2
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/codec.go
@@ -0,0 +1,43 @@
+/*
+ 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 ttrpc
+
+import (
+ "fmt"
+
+ "github.com/gogo/protobuf/proto"
+)
+
+type codec struct{}
+
+func (c codec) Marshal(msg interface{}) ([]byte, error) {
+ switch v := msg.(type) {
+ case proto.Message:
+ return proto.Marshal(v)
+ default:
+ return nil, fmt.Errorf("ttrpc: cannot marshal unknown type: %T", msg)
+ }
+}
+
+func (c codec) Unmarshal(p []byte, msg interface{}) error {
+ switch v := msg.(type) {
+ case proto.Message:
+ return proto.Unmarshal(p, v)
+ default:
+ return fmt.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg)
+ }
+}
diff --git a/vendor/github.com/containerd/ttrpc/config.go b/vendor/github.com/containerd/ttrpc/config.go
new file mode 100644
index 00000000..09741963
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/config.go
@@ -0,0 +1,52 @@
+/*
+ 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 ttrpc
+
+import "errors"
+
+type serverConfig struct {
+ handshaker Handshaker
+ interceptor UnaryServerInterceptor
+}
+
+// ServerOpt for configuring a ttrpc server
+type ServerOpt func(*serverConfig) error
+
+// WithServerHandshaker can be passed to NewServer to ensure that the
+// handshaker is called before every connection attempt.
+//
+// Only one handshaker is allowed per server.
+func WithServerHandshaker(handshaker Handshaker) ServerOpt {
+ return func(c *serverConfig) error {
+ if c.handshaker != nil {
+ return errors.New("only one handshaker allowed per server")
+ }
+ c.handshaker = handshaker
+ return nil
+ }
+}
+
+// WithUnaryServerInterceptor sets the provided interceptor on the server
+func WithUnaryServerInterceptor(i UnaryServerInterceptor) ServerOpt {
+ return func(c *serverConfig) error {
+ if c.interceptor != nil {
+ return errors.New("only one interceptor allowed per server")
+ }
+ c.interceptor = i
+ return nil
+ }
+}
diff --git a/vendor/github.com/containerd/ttrpc/go.mod b/vendor/github.com/containerd/ttrpc/go.mod
new file mode 100644
index 00000000..efc00860
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/go.mod
@@ -0,0 +1,13 @@
+module github.com/containerd/ttrpc
+
+go 1.13
+
+require (
+ github.com/gogo/protobuf v1.3.2
+ github.com/prometheus/procfs v0.6.0
+ github.com/sirupsen/logrus v1.8.1
+ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
+ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
+ google.golang.org/grpc v1.27.1
+ google.golang.org/protobuf v1.27.1
+)
diff --git a/vendor/github.com/containerd/ttrpc/go.sum b/vendor/github.com/containerd/ttrpc/go.sum
new file mode 100644
index 00000000..cb91432e
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/go.sum
@@ -0,0 +1,99 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+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=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/vendor/github.com/containerd/ttrpc/handshake.go b/vendor/github.com/containerd/ttrpc/handshake.go
new file mode 100644
index 00000000..a424b67a
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/handshake.go
@@ -0,0 +1,50 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "net"
+)
+
+// Handshaker defines the interface for connection handshakes performed on the
+// server or client when first connecting.
+type Handshaker interface {
+ // Handshake should confirm or decorate a connection that may be incoming
+ // to a server or outgoing from a client.
+ //
+ // If this returns without an error, the caller should use the connection
+ // in place of the original connection.
+ //
+ // The second return value can contain credential specific data, such as
+ // unix socket credentials or TLS information.
+ //
+ // While we currently only have implementations on the server-side, this
+ // interface should be sufficient to implement similar handshakes on the
+ // client-side.
+ Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error)
+}
+
+type handshakerFunc func(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error)
+
+func (fn handshakerFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
+ return fn(ctx, conn)
+}
+
+func noopHandshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
+ return conn, nil, nil
+}
diff --git a/vendor/github.com/containerd/ttrpc/interceptor.go b/vendor/github.com/containerd/ttrpc/interceptor.go
new file mode 100644
index 00000000..c1219dac
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/interceptor.go
@@ -0,0 +1,50 @@
+/*
+ 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 ttrpc
+
+import "context"
+
+// UnaryServerInfo provides information about the server request
+type UnaryServerInfo struct {
+ FullMethod string
+}
+
+// UnaryClientInfo provides information about the client request
+type UnaryClientInfo struct {
+ FullMethod string
+}
+
+// Unmarshaler contains the server request data and allows it to be unmarshaled
+// into a concrete type
+type Unmarshaler func(interface{}) error
+
+// Invoker invokes the client's request and response from the ttrpc server
+type Invoker func(context.Context, *Request, *Response) error
+
+// UnaryServerInterceptor specifies the interceptor function for server request/response
+type UnaryServerInterceptor func(context.Context, Unmarshaler, *UnaryServerInfo, Method) (interface{}, error)
+
+// UnaryClientInterceptor specifies the interceptor function for client request/response
+type UnaryClientInterceptor func(context.Context, *Request, *Response, *UnaryClientInfo, Invoker) error
+
+func defaultServerInterceptor(ctx context.Context, unmarshal Unmarshaler, info *UnaryServerInfo, method Method) (interface{}, error) {
+ return method(ctx, unmarshal)
+}
+
+func defaultClientInterceptor(ctx context.Context, req *Request, resp *Response, _ *UnaryClientInfo, invoker Invoker) error {
+ return invoker(ctx, req, resp)
+}
diff --git a/vendor/github.com/containerd/ttrpc/metadata.go b/vendor/github.com/containerd/ttrpc/metadata.go
new file mode 100644
index 00000000..ce8c0d13
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/metadata.go
@@ -0,0 +1,107 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "strings"
+)
+
+// MD is the user type for ttrpc metadata
+type MD map[string][]string
+
+// Get returns the metadata for a given key when they exist.
+// If there is no metadata, a nil slice and false are returned.
+func (m MD) Get(key string) ([]string, bool) {
+ key = strings.ToLower(key)
+ list, ok := m[key]
+ if !ok || len(list) == 0 {
+ return nil, false
+ }
+
+ return list, true
+}
+
+// Set sets the provided values for a given key.
+// The values will overwrite any existing values.
+// If no values provided, a key will be deleted.
+func (m MD) Set(key string, values ...string) {
+ key = strings.ToLower(key)
+ if len(values) == 0 {
+ delete(m, key)
+ return
+ }
+ m[key] = values
+}
+
+// Append appends additional values to the given key.
+func (m MD) Append(key string, values ...string) {
+ key = strings.ToLower(key)
+ if len(values) == 0 {
+ return
+ }
+ current, ok := m[key]
+ if ok {
+ m.Set(key, append(current, values...)...)
+ } else {
+ m.Set(key, values...)
+ }
+}
+
+func (m MD) setRequest(r *Request) {
+ for k, values := range m {
+ for _, v := range values {
+ r.Metadata = append(r.Metadata, &KeyValue{
+ Key: k,
+ Value: v,
+ })
+ }
+ }
+}
+
+func (m MD) fromRequest(r *Request) {
+ for _, kv := range r.Metadata {
+ m[kv.Key] = append(m[kv.Key], kv.Value)
+ }
+}
+
+type metadataKey struct{}
+
+// GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata)
+func GetMetadata(ctx context.Context) (MD, bool) {
+ metadata, ok := ctx.Value(metadataKey{}).(MD)
+ return metadata, ok
+}
+
+// GetMetadataValue gets a specific metadata value by name from context.Context
+func GetMetadataValue(ctx context.Context, name string) (string, bool) {
+ metadata, ok := GetMetadata(ctx)
+ if !ok {
+ return "", false
+ }
+
+ if list, ok := metadata.Get(name); ok {
+ return list[0], true
+ }
+
+ return "", false
+}
+
+// WithMetadata attaches metadata map to a context.Context
+func WithMetadata(ctx context.Context, md MD) context.Context {
+ return context.WithValue(ctx, metadataKey{}, md)
+}
diff --git a/vendor/github.com/containerd/ttrpc/server.go b/vendor/github.com/containerd/ttrpc/server.go
new file mode 100644
index 00000000..b0e48073
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/server.go
@@ -0,0 +1,500 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "errors"
+ "io"
+ "math/rand"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+var (
+ ErrServerClosed = errors.New("ttrpc: server closed")
+)
+
+type Server struct {
+ config *serverConfig
+ services *serviceSet
+ codec codec
+
+ mu sync.Mutex
+ listeners map[net.Listener]struct{}
+ connections map[*serverConn]struct{} // all connections to current state
+ done chan struct{} // marks point at which we stop serving requests
+}
+
+func NewServer(opts ...ServerOpt) (*Server, error) {
+ config := &serverConfig{}
+ for _, opt := range opts {
+ if err := opt(config); err != nil {
+ return nil, err
+ }
+ }
+ if config.interceptor == nil {
+ config.interceptor = defaultServerInterceptor
+ }
+
+ return &Server{
+ config: config,
+ services: newServiceSet(config.interceptor),
+ done: make(chan struct{}),
+ listeners: make(map[net.Listener]struct{}),
+ connections: make(map[*serverConn]struct{}),
+ }, nil
+}
+
+func (s *Server) Register(name string, methods map[string]Method) {
+ s.services.register(name, methods)
+}
+
+func (s *Server) Serve(ctx context.Context, l net.Listener) error {
+ s.addListener(l)
+ defer s.closeListener(l)
+
+ var (
+ backoff time.Duration
+ handshaker = s.config.handshaker
+ )
+
+ if handshaker == nil {
+ handshaker = handshakerFunc(noopHandshake)
+ }
+
+ for {
+ conn, err := l.Accept()
+ if err != nil {
+ select {
+ case <-s.done:
+ return ErrServerClosed
+ default:
+ }
+
+ if terr, ok := err.(interface {
+ Temporary() bool
+ }); ok && terr.Temporary() {
+ if backoff == 0 {
+ backoff = time.Millisecond
+ } else {
+ backoff *= 2
+ }
+
+ if max := time.Second; backoff > max {
+ backoff = max
+ }
+
+ sleep := time.Duration(rand.Int63n(int64(backoff)))
+ logrus.WithError(err).Errorf("ttrpc: failed accept; backoff %v", sleep)
+ time.Sleep(sleep)
+ continue
+ }
+
+ return err
+ }
+
+ backoff = 0
+
+ approved, handshake, err := handshaker.Handshake(ctx, conn)
+ if err != nil {
+ logrus.WithError(err).Errorf("ttrpc: refusing connection after handshake")
+ conn.Close()
+ continue
+ }
+
+ sc := s.newConn(approved, handshake)
+ go sc.run(ctx)
+ }
+}
+
+func (s *Server) Shutdown(ctx context.Context) error {
+ s.mu.Lock()
+ select {
+ case <-s.done:
+ default:
+ // protected by mutex
+ close(s.done)
+ }
+ lnerr := s.closeListeners()
+ s.mu.Unlock()
+
+ ticker := time.NewTicker(200 * time.Millisecond)
+ defer ticker.Stop()
+ for {
+ if s.closeIdleConns() {
+ return lnerr
+ }
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-ticker.C:
+ }
+ }
+}
+
+// Close the server without waiting for active connections.
+func (s *Server) Close() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ select {
+ case <-s.done:
+ default:
+ // protected by mutex
+ close(s.done)
+ }
+
+ err := s.closeListeners()
+ for c := range s.connections {
+ c.close()
+ delete(s.connections, c)
+ }
+
+ return err
+}
+
+func (s *Server) addListener(l net.Listener) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.listeners[l] = struct{}{}
+}
+
+func (s *Server) closeListener(l net.Listener) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ return s.closeListenerLocked(l)
+}
+
+func (s *Server) closeListenerLocked(l net.Listener) error {
+ defer delete(s.listeners, l)
+ return l.Close()
+}
+
+func (s *Server) closeListeners() error {
+ var err error
+ for l := range s.listeners {
+ if cerr := s.closeListenerLocked(l); cerr != nil && err == nil {
+ err = cerr
+ }
+ }
+ return err
+}
+
+func (s *Server) addConnection(c *serverConn) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.connections[c] = struct{}{}
+}
+
+func (s *Server) delConnection(c *serverConn) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ delete(s.connections, c)
+}
+
+func (s *Server) countConnection() int {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ return len(s.connections)
+}
+
+func (s *Server) closeIdleConns() bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ quiescent := true
+ for c := range s.connections {
+ st, ok := c.getState()
+ if !ok || st != connStateIdle {
+ quiescent = false
+ continue
+ }
+ c.close()
+ delete(s.connections, c)
+ }
+ return quiescent
+}
+
+type connState int
+
+const (
+ connStateActive = iota + 1 // outstanding requests
+ connStateIdle // no requests
+ connStateClosed // closed connection
+)
+
+func (cs connState) String() string {
+ switch cs {
+ case connStateActive:
+ return "active"
+ case connStateIdle:
+ return "idle"
+ case connStateClosed:
+ return "closed"
+ default:
+ return "unknown"
+ }
+}
+
+func (s *Server) newConn(conn net.Conn, handshake interface{}) *serverConn {
+ c := &serverConn{
+ server: s,
+ conn: conn,
+ handshake: handshake,
+ shutdown: make(chan struct{}),
+ }
+ c.setState(connStateIdle)
+ s.addConnection(c)
+ return c
+}
+
+type serverConn struct {
+ server *Server
+ conn net.Conn
+ handshake interface{} // data from handshake, not used for now
+ state atomic.Value
+
+ shutdownOnce sync.Once
+ shutdown chan struct{} // forced shutdown, used by close
+}
+
+func (c *serverConn) getState() (connState, bool) {
+ cs, ok := c.state.Load().(connState)
+ return cs, ok
+}
+
+func (c *serverConn) setState(newstate connState) {
+ c.state.Store(newstate)
+}
+
+func (c *serverConn) close() error {
+ c.shutdownOnce.Do(func() {
+ close(c.shutdown)
+ })
+
+ return nil
+}
+
+func (c *serverConn) run(sctx context.Context) {
+ type (
+ request struct {
+ id uint32
+ req *Request
+ }
+
+ response struct {
+ id uint32
+ resp *Response
+ }
+ )
+
+ var (
+ ch = newChannel(c.conn)
+ ctx, cancel = context.WithCancel(sctx)
+ active int
+ state connState = connStateIdle
+ responses = make(chan response)
+ requests = make(chan request)
+ recvErr = make(chan error, 1)
+ shutdown = c.shutdown
+ done = make(chan struct{})
+ )
+
+ defer c.conn.Close()
+ defer cancel()
+ defer close(done)
+ defer c.server.delConnection(c)
+
+ go func(recvErr chan error) {
+ defer close(recvErr)
+ sendImmediate := func(id uint32, st *status.Status) bool {
+ select {
+ case responses <- response{
+ // even though we've had an invalid stream id, we send it
+ // back on the same stream id so the client knows which
+ // stream id was bad.
+ id: id,
+ resp: &Response{
+ Status: st.Proto(),
+ },
+ }:
+ return true
+ case <-c.shutdown:
+ return false
+ case <-done:
+ return false
+ }
+ }
+
+ for {
+ select {
+ case <-c.shutdown:
+ return
+ case <-done:
+ return
+ default: // proceed
+ }
+
+ mh, p, err := ch.recv()
+ if err != nil {
+ status, ok := status.FromError(err)
+ if !ok {
+ recvErr <- err
+ return
+ }
+
+ // in this case, we send an error for that particular message
+ // when the status is defined.
+ if !sendImmediate(mh.StreamID, status) {
+ return
+ }
+
+ continue
+ }
+
+ if mh.Type != messageTypeRequest {
+ // we must ignore this for future compat.
+ continue
+ }
+
+ var req Request
+ if err := c.server.codec.Unmarshal(p, &req); err != nil {
+ ch.putmbuf(p)
+ if !sendImmediate(mh.StreamID, status.Newf(codes.InvalidArgument, "unmarshal request error: %v", err)) {
+ return
+ }
+ continue
+ }
+ ch.putmbuf(p)
+
+ if mh.StreamID%2 != 1 {
+ // enforce odd client initiated identifiers.
+ if !sendImmediate(mh.StreamID, status.Newf(codes.InvalidArgument, "StreamID must be odd for client initiated streams")) {
+ return
+ }
+ continue
+ }
+
+ // Forward the request to the main loop. We don't wait on s.done
+ // because we have already accepted the client request.
+ select {
+ case requests <- request{
+ id: mh.StreamID,
+ req: &req,
+ }:
+ case <-done:
+ return
+ }
+ }
+ }(recvErr)
+
+ for {
+ newstate := state
+ switch {
+ case active > 0:
+ newstate = connStateActive
+ shutdown = nil
+ case active == 0:
+ newstate = connStateIdle
+ shutdown = c.shutdown // only enable this branch in idle mode
+ }
+
+ if newstate != state {
+ c.setState(newstate)
+ state = newstate
+ }
+
+ select {
+ case request := <-requests:
+ active++
+ go func(id uint32) {
+ ctx, cancel := getRequestContext(ctx, request.req)
+ defer cancel()
+
+ p, status := c.server.services.call(ctx, request.req.Service, request.req.Method, request.req.Payload)
+ resp := &Response{
+ Status: status.Proto(),
+ Payload: p,
+ }
+
+ select {
+ case responses <- response{
+ id: id,
+ resp: resp,
+ }:
+ case <-done:
+ }
+ }(request.id)
+ case response := <-responses:
+ p, err := c.server.codec.Marshal(response.resp)
+ if err != nil {
+ logrus.WithError(err).Error("failed marshaling response")
+ return
+ }
+
+ if err := ch.send(response.id, messageTypeResponse, p); err != nil {
+ logrus.WithError(err).Error("failed sending message on channel")
+ return
+ }
+
+ active--
+ case err := <-recvErr:
+ // TODO(stevvooe): Not wildly clear what we should do in this
+ // branch. Basically, it means that we are no longer receiving
+ // requests due to a terminal error.
+ recvErr = nil // connection is now "closing"
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ // The client went away and we should stop processing
+ // requests, so that the client connection is closed
+ return
+ }
+ if err != nil {
+ logrus.WithError(err).Error("error receiving message")
+ }
+ case <-shutdown:
+ return
+ }
+ }
+}
+
+var noopFunc = func() {}
+
+func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) {
+ if len(req.Metadata) > 0 {
+ md := MD{}
+ md.fromRequest(req)
+ ctx = WithMetadata(ctx, md)
+ }
+
+ cancel = noopFunc
+ if req.TimeoutNano == 0 {
+ return ctx, cancel
+ }
+
+ ctx, cancel = context.WithTimeout(ctx, time.Duration(req.TimeoutNano))
+ return ctx, cancel
+}
diff --git a/vendor/github.com/containerd/ttrpc/services.go b/vendor/github.com/containerd/ttrpc/services.go
new file mode 100644
index 00000000..f359e961
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/services.go
@@ -0,0 +1,166 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "unsafe"
+
+ "github.com/gogo/protobuf/proto"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+type Method func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error)
+
+type ServiceDesc struct {
+ Methods map[string]Method
+
+ // TODO(stevvooe): Add stream support.
+}
+
+type serviceSet struct {
+ services map[string]ServiceDesc
+ interceptor UnaryServerInterceptor
+}
+
+func newServiceSet(interceptor UnaryServerInterceptor) *serviceSet {
+ return &serviceSet{
+ services: make(map[string]ServiceDesc),
+ interceptor: interceptor,
+ }
+}
+
+func (s *serviceSet) register(name string, methods map[string]Method) {
+ if _, ok := s.services[name]; ok {
+ panic(fmt.Errorf("duplicate service %v registered", name))
+ }
+
+ s.services[name] = ServiceDesc{
+ Methods: methods,
+ }
+}
+
+func (s *serviceSet) call(ctx context.Context, serviceName, methodName string, p []byte) ([]byte, *status.Status) {
+ p, err := s.dispatch(ctx, serviceName, methodName, p)
+ st, ok := status.FromError(err)
+ if !ok {
+ st = status.New(convertCode(err), err.Error())
+ }
+
+ return p, st
+}
+
+func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName string, p []byte) ([]byte, error) {
+ method, err := s.resolve(serviceName, methodName)
+ if err != nil {
+ return nil, err
+ }
+
+ unmarshal := func(obj interface{}) error {
+ switch v := obj.(type) {
+ case proto.Message:
+ if err := proto.Unmarshal(p, v); err != nil {
+ return status.Errorf(codes.Internal, "ttrpc: error unmarshalling payload: %v", err.Error())
+ }
+ default:
+ return status.Errorf(codes.Internal, "ttrpc: error unsupported request type: %T", v)
+ }
+ return nil
+ }
+
+ info := &UnaryServerInfo{
+ FullMethod: fullPath(serviceName, methodName),
+ }
+
+ resp, err := s.interceptor(ctx, unmarshal, info, method)
+ if err != nil {
+ return nil, err
+ }
+
+ if isNil(resp) {
+ return nil, errors.New("ttrpc: marshal called with nil")
+ }
+
+ switch v := resp.(type) {
+ case proto.Message:
+ r, err := proto.Marshal(v)
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "ttrpc: error marshaling payload: %v", err.Error())
+ }
+
+ return r, nil
+ default:
+ return nil, status.Errorf(codes.Internal, "ttrpc: error unsupported response type: %T", v)
+ }
+}
+
+func (s *serviceSet) resolve(service, method string) (Method, error) {
+ srv, ok := s.services[service]
+ if !ok {
+ return nil, status.Errorf(codes.Unimplemented, "service %v", service)
+ }
+
+ mthd, ok := srv.Methods[method]
+ if !ok {
+ return nil, status.Errorf(codes.Unimplemented, "method %v", method)
+ }
+
+ return mthd, nil
+}
+
+// convertCode maps stdlib go errors into grpc space.
+//
+// This is ripped from the grpc-go code base.
+func convertCode(err error) codes.Code {
+ switch err {
+ case nil:
+ return codes.OK
+ case io.EOF:
+ return codes.OutOfRange
+ case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
+ return codes.FailedPrecondition
+ case os.ErrInvalid:
+ return codes.InvalidArgument
+ case context.Canceled:
+ return codes.Canceled
+ case context.DeadlineExceeded:
+ return codes.DeadlineExceeded
+ }
+ switch {
+ case os.IsExist(err):
+ return codes.AlreadyExists
+ case os.IsNotExist(err):
+ return codes.NotFound
+ case os.IsPermission(err):
+ return codes.PermissionDenied
+ }
+ return codes.Unknown
+}
+
+func fullPath(service, method string) string {
+ return "/" + path.Join(service, method)
+}
+
+func isNil(resp interface{}) bool {
+ return (*[2]uintptr)(unsafe.Pointer(&resp))[1] == 0
+}
diff --git a/vendor/github.com/containerd/ttrpc/types.go b/vendor/github.com/containerd/ttrpc/types.go
new file mode 100644
index 00000000..9a1c19a7
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/types.go
@@ -0,0 +1,63 @@
+/*
+ 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 ttrpc
+
+import (
+ "fmt"
+
+ spb "google.golang.org/genproto/googleapis/rpc/status"
+)
+
+type Request struct {
+ Service string `protobuf:"bytes,1,opt,name=service,proto3"`
+ Method string `protobuf:"bytes,2,opt,name=method,proto3"`
+ Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"`
+ TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,proto3"`
+ Metadata []*KeyValue `protobuf:"bytes,5,rep,name=metadata,proto3"`
+}
+
+func (r *Request) Reset() { *r = Request{} }
+func (r *Request) String() string { return fmt.Sprintf("%+#v", r) }
+func (r *Request) ProtoMessage() {}
+
+type Response struct {
+ Status *spb.Status `protobuf:"bytes,1,opt,name=status,proto3"`
+ Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3"`
+}
+
+func (r *Response) Reset() { *r = Response{} }
+func (r *Response) String() string { return fmt.Sprintf("%+#v", r) }
+func (r *Response) ProtoMessage() {}
+
+type StringList struct {
+ List []string `protobuf:"bytes,1,rep,name=list,proto3"`
+}
+
+func (r *StringList) Reset() { *r = StringList{} }
+func (r *StringList) String() string { return fmt.Sprintf("%+#v", r) }
+func (r *StringList) ProtoMessage() {}
+
+func makeStringList(item ...string) StringList { return StringList{List: item} }
+
+type KeyValue struct {
+ Key string `protobuf:"bytes,1,opt,name=key,proto3"`
+ Value string `protobuf:"bytes,2,opt,name=value,proto3"`
+}
+
+func (m *KeyValue) Reset() { *m = KeyValue{} }
+func (*KeyValue) ProtoMessage() {}
+func (m *KeyValue) String() string { return fmt.Sprintf("%+#v", m) }
diff --git a/vendor/github.com/containerd/ttrpc/unixcreds_linux.go b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go
new file mode 100644
index 00000000..a59dad60
--- /dev/null
+++ b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go
@@ -0,0 +1,109 @@
+/*
+ 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 ttrpc
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+type UnixCredentialsFunc func(*unix.Ucred) error
+
+func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) {
+ uc, err := requireUnixSocket(conn)
+ if err != nil {
+ return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: require unix socket: %w", err)
+ }
+
+ rs, err := uc.SyscallConn()
+ if err != nil {
+ return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed: %w", err)
+ }
+ var (
+ ucred *unix.Ucred
+ ucredErr error
+ )
+ if err := rs.Control(func(fd uintptr) {
+ ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED)
+ }); err != nil {
+ return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed: %w", err)
+ }
+
+ if ucredErr != nil {
+ return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials: %w", err)
+ }
+
+ if err := fn(ucred); err != nil {
+ return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: credential check failed: %w", err)
+ }
+
+ return uc, ucred, nil
+}
+
+// UnixSocketRequireUidGid requires specific *effective* UID/GID, rather than the real UID/GID.
+//
+// For example, if a daemon binary is owned by the root (UID 0) with SUID bit but running as an
+// unprivileged user (UID 1001), the effective UID becomes 0, and the real UID becomes 1001.
+// So calling this function with uid=0 allows a connection from effective UID 0 but rejects
+// a connection from effective UID 1001.
+//
+// See socket(7), SO_PEERCRED: "The returned credentials are those that were in effect at the time of the call to connect(2) or socketpair(2)."
+func UnixSocketRequireUidGid(uid, gid int) UnixCredentialsFunc {
+ return func(ucred *unix.Ucred) error {
+ return requireUidGid(ucred, uid, gid)
+ }
+}
+
+func UnixSocketRequireRoot() UnixCredentialsFunc {
+ return UnixSocketRequireUidGid(0, 0)
+}
+
+// UnixSocketRequireSameUser resolves the current effective unix user and returns a
+// UnixCredentialsFunc that will validate incoming unix connections against the
+// current credentials.
+//
+// This is useful when using abstract sockets that are accessible by all users.
+func UnixSocketRequireSameUser() UnixCredentialsFunc {
+ euid, egid := os.Geteuid(), os.Getegid()
+ return UnixSocketRequireUidGid(euid, egid)
+}
+
+func requireRoot(ucred *unix.Ucred) error {
+ return requireUidGid(ucred, 0, 0)
+}
+
+func requireUidGid(ucred *unix.Ucred, uid, gid int) error {
+ if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) {
+ return fmt.Errorf("ttrpc: invalid credentials: %v", syscall.EPERM)
+ }
+ return nil
+}
+
+func requireUnixSocket(conn net.Conn) (*net.UnixConn, error) {
+ uc, ok := conn.(*net.UnixConn)
+ if !ok {
+ return nil, errors.New("a unix socket connection is required")
+ }
+
+ return uc, nil
+}
diff --git a/vendor/github.com/moby/buildkit/source/types/types.go b/vendor/github.com/moby/buildkit/source/types/types.go
new file mode 100644
index 00000000..b96eac23
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/source/types/types.go
@@ -0,0 +1,9 @@
+package srctypes
+
+const (
+ DockerImageScheme = "docker-image"
+ GitScheme = "git"
+ LocalScheme = "local"
+ HTTPScheme = "http"
+ HTTPSScheme = "https"
+)
diff --git a/vendor/github.com/moby/buildkit/util/buildinfo/types/types.go b/vendor/github.com/moby/buildkit/util/buildinfo/types/types.go
new file mode 100644
index 00000000..93abcd1b
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/buildinfo/types/types.go
@@ -0,0 +1,52 @@
+package binfotypes
+
+import (
+ srctypes "github.com/moby/buildkit/source/types"
+)
+
+// ImageConfigField defines the key of build dependencies.
+const ImageConfigField = "moby.buildkit.buildinfo.v1"
+
+// ImageConfig defines the structure of build dependencies
+// inside image config.
+type ImageConfig struct {
+ BuildInfo string `json:"moby.buildkit.buildinfo.v1,omitempty"`
+}
+
+// BuildInfo defines the main structure added to image config as
+// ImageConfigField key and returned in solver ExporterResponse as
+// exptypes.ExporterBuildInfo key.
+type BuildInfo struct {
+ // Frontend defines the frontend used to build.
+ Frontend string `json:"frontend,omitempty"`
+ // Attrs defines build request attributes.
+ Attrs map[string]*string `json:"attrs,omitempty"`
+ // Sources defines build dependencies.
+ Sources []Source `json:"sources,omitempty"`
+ // Deps defines context dependencies.
+ Deps map[string]BuildInfo `json:"deps,omitempty"`
+}
+
+// Source defines a build dependency.
+type Source struct {
+ // Type defines the SourceType source type (docker-image, git, http).
+ Type SourceType `json:"type,omitempty"`
+ // Ref is the reference of the source.
+ Ref string `json:"ref,omitempty"`
+ // Alias is a special field used to match with the actual source ref
+ // because frontend might have already transformed a string user typed
+ // before generating LLB.
+ Alias string `json:"alias,omitempty"`
+ // Pin is the source digest.
+ Pin string `json:"pin,omitempty"`
+}
+
+// SourceType contains source type.
+type SourceType string
+
+// List of source types.
+const (
+ SourceTypeDockerImage SourceType = srctypes.DockerImageScheme
+ SourceTypeGit SourceType = srctypes.GitScheme
+ SourceTypeHTTP SourceType = srctypes.HTTPScheme
+)
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/buffer.go b/vendor/github.com/moby/buildkit/util/contentutil/buffer.go
new file mode 100644
index 00000000..31d2be68
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/buffer.go
@@ -0,0 +1,156 @@
+package contentutil
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "sync"
+ "time"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/errdefs"
+ digest "github.com/opencontainers/go-digest"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+// Buffer is a content provider and ingester that keeps data in memory
+type Buffer interface {
+ content.Provider
+ content.Ingester
+}
+
+// NewBuffer returns a new buffer
+func NewBuffer() Buffer {
+ return &buffer{
+ buffers: map[digest.Digest][]byte{},
+ refs: map[string]struct{}{},
+ }
+}
+
+type buffer struct {
+ mu sync.Mutex
+ buffers map[digest.Digest][]byte
+ refs map[string]struct{}
+}
+
+func (b *buffer) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
+ var wOpts content.WriterOpts
+ for _, opt := range opts {
+ if err := opt(&wOpts); err != nil {
+ return nil, err
+ }
+ }
+ b.mu.Lock()
+ if _, ok := b.refs[wOpts.Ref]; ok {
+ return nil, errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", wOpts.Ref)
+ }
+ b.mu.Unlock()
+ return &bufferedWriter{
+ main: b,
+ digester: digest.Canonical.Digester(),
+ buffer: bytes.NewBuffer(nil),
+ expected: wOpts.Desc.Digest,
+ releaseRef: func() {
+ b.mu.Lock()
+ delete(b.refs, wOpts.Ref)
+ b.mu.Unlock()
+ },
+ }, nil
+}
+
+func (b *buffer) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
+ r, err := b.getBytesReader(ctx, desc.Digest)
+ if err != nil {
+ return nil, err
+ }
+ return &readerAt{Reader: r, Closer: ioutil.NopCloser(r), size: int64(r.Len())}, nil
+}
+
+func (b *buffer) getBytesReader(ctx context.Context, dgst digest.Digest) (*bytes.Reader, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if dt, ok := b.buffers[dgst]; ok {
+ return bytes.NewReader(dt), nil
+ }
+
+ return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
+}
+
+func (b *buffer) addValue(k digest.Digest, dt []byte) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ b.buffers[k] = dt
+}
+
+type bufferedWriter struct {
+ main *buffer
+ ref string
+ offset int64
+ total int64
+ startedAt time.Time
+ updatedAt time.Time
+ buffer *bytes.Buffer
+ expected digest.Digest
+ digester digest.Digester
+ releaseRef func()
+}
+
+func (w *bufferedWriter) Write(p []byte) (n int, err error) {
+ n, err = w.buffer.Write(p)
+ w.digester.Hash().Write(p[:n])
+ w.offset += int64(len(p))
+ w.updatedAt = time.Now()
+ return n, err
+}
+
+func (w *bufferedWriter) Close() error {
+ if w.buffer != nil {
+ w.releaseRef()
+ w.buffer = nil
+ }
+ return nil
+}
+
+func (w *bufferedWriter) Status() (content.Status, error) {
+ return content.Status{
+ Ref: w.ref,
+ Offset: w.offset,
+ Total: w.total,
+ StartedAt: w.startedAt,
+ UpdatedAt: w.updatedAt,
+ }, nil
+}
+
+func (w *bufferedWriter) Digest() digest.Digest {
+ return w.digester.Digest()
+}
+
+func (w *bufferedWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opt ...content.Opt) error {
+ if w.buffer == nil {
+ return errors.Errorf("can't commit already committed or closed")
+ }
+ if s := int64(w.buffer.Len()); size > 0 && size != s {
+ return errors.Errorf("unexpected commit size %d, expected %d", s, size)
+ }
+ dgst := w.digester.Digest()
+ if expected != "" && expected != dgst {
+ return errors.Errorf("unexpected digest: %v != %v", dgst, expected)
+ }
+ if w.expected != "" && w.expected != dgst {
+ return errors.Errorf("unexpected digest: %v != %v", dgst, w.expected)
+ }
+ w.main.addValue(dgst, w.buffer.Bytes())
+ return w.Close()
+}
+
+func (w *bufferedWriter) Truncate(size int64) error {
+ if size != 0 {
+ return errors.New("Truncate: unsupported size")
+ }
+ w.offset = 0
+ w.digester.Hash().Reset()
+ w.buffer.Reset()
+ return nil
+}
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/copy.go b/vendor/github.com/moby/buildkit/util/contentutil/copy.go
new file mode 100644
index 00000000..2509ce1a
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/copy.go
@@ -0,0 +1,94 @@
+package contentutil
+
+import (
+ "context"
+ "io"
+ "sync"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/images"
+ "github.com/moby/buildkit/util/resolver/limited"
+ "github.com/moby/buildkit/util/resolver/retryhandler"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func Copy(ctx context.Context, ingester content.Ingester, provider content.Provider, desc ocispecs.Descriptor, ref string, logger func([]byte)) error {
+ if _, err := retryhandler.New(limited.FetchHandler(ingester, &localFetcher{provider}, ref), logger)(ctx, desc); err != nil {
+ return err
+ }
+ return nil
+}
+
+type localFetcher struct {
+ content.Provider
+}
+
+func (f *localFetcher) Fetch(ctx context.Context, desc ocispecs.Descriptor) (io.ReadCloser, error) {
+ r, err := f.Provider.ReaderAt(ctx, desc)
+ if err != nil {
+ return nil, err
+ }
+ return &rc{ReaderAt: r}, nil
+}
+
+type rc struct {
+ content.ReaderAt
+ offset int64
+}
+
+func (r *rc) Read(b []byte) (int, error) {
+ n, err := r.ReadAt(b, r.offset)
+ r.offset += int64(n)
+ if n > 0 && err == io.EOF {
+ err = nil
+ }
+ return n, err
+}
+
+func (r *rc) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ case io.SeekStart:
+ r.offset = offset
+ case io.SeekCurrent:
+ r.offset += offset
+ case io.SeekEnd:
+ r.offset = r.Size() - offset
+ }
+ return r.offset, nil
+}
+
+func CopyChain(ctx context.Context, ingester content.Ingester, provider content.Provider, desc ocispecs.Descriptor) error {
+ var m sync.Mutex
+ manifestStack := []ocispecs.Descriptor{}
+
+ filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispecs.Descriptor) ([]ocispecs.Descriptor, error) {
+ switch desc.MediaType {
+ case images.MediaTypeDockerSchema2Manifest, ocispecs.MediaTypeImageManifest,
+ images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
+ m.Lock()
+ manifestStack = append(manifestStack, desc)
+ m.Unlock()
+ return nil, images.ErrStopHandler
+ default:
+ return nil, nil
+ }
+ })
+ handlers := []images.Handler{
+ images.ChildrenHandler(provider),
+ filterHandler,
+ retryhandler.New(limited.FetchHandler(ingester, &localFetcher{provider}, ""), func(_ []byte) {}),
+ }
+
+ if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
+ return errors.WithStack(err)
+ }
+
+ for i := len(manifestStack) - 1; i >= 0; i-- {
+ if err := Copy(ctx, ingester, provider, manifestStack[i], "", nil); err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go b/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go
new file mode 100644
index 00000000..6414b12f
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go
@@ -0,0 +1,73 @@
+package contentutil
+
+import (
+ "context"
+ "io"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/remotes"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func FromFetcher(f remotes.Fetcher) content.Provider {
+ return &fetchedProvider{
+ f: f,
+ }
+}
+
+type fetchedProvider struct {
+ f remotes.Fetcher
+}
+
+func (p *fetchedProvider) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
+ rc, err := p.f.Fetch(ctx, desc)
+ if err != nil {
+ return nil, err
+ }
+
+ return &readerAt{Reader: rc, Closer: rc, size: desc.Size}, nil
+}
+
+type readerAt struct {
+ io.Reader
+ io.Closer
+ size int64
+ offset int64
+}
+
+func (r *readerAt) ReadAt(b []byte, off int64) (int, error) {
+ if ra, ok := r.Reader.(io.ReaderAt); ok {
+ return ra.ReadAt(b, off)
+ }
+
+ if r.offset != off {
+ if seeker, ok := r.Reader.(io.Seeker); ok {
+ if _, err := seeker.Seek(off, io.SeekStart); err != nil {
+ return 0, err
+ }
+ r.offset = off
+ } else {
+ return 0, errors.Errorf("unsupported offset")
+ }
+ }
+
+ var totalN int
+ for len(b) > 0 {
+ n, err := r.Reader.Read(b)
+ if err == io.EOF && n == len(b) {
+ err = nil
+ }
+ r.offset += int64(n)
+ totalN += n
+ b = b[n:]
+ if err != nil {
+ return totalN, err
+ }
+ }
+ return totalN, nil
+}
+
+func (r *readerAt) Size() int64 {
+ return r.size
+}
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go b/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go
new file mode 100644
index 00000000..469096d3
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go
@@ -0,0 +1,92 @@
+package contentutil
+
+import (
+ "context"
+ "sync"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/errdefs"
+ digest "github.com/opencontainers/go-digest"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+// NewMultiProvider creates a new mutable provider with a base provider
+func NewMultiProvider(base content.Provider) *MultiProvider {
+ return &MultiProvider{
+ base: base,
+ sub: map[digest.Digest]content.Provider{},
+ }
+}
+
+// MultiProvider is a provider backed by a mutable map of providers
+type MultiProvider struct {
+ mu sync.RWMutex
+ base content.Provider
+ sub map[digest.Digest]content.Provider
+}
+
+func (mp *MultiProvider) SnapshotLabels(descs []ocispecs.Descriptor, index int) map[string]string {
+ if len(descs) < index {
+ return nil
+ }
+ desc := descs[index]
+ type snapshotLabels interface {
+ SnapshotLabels([]ocispecs.Descriptor, int) map[string]string
+ }
+
+ mp.mu.RLock()
+ if p, ok := mp.sub[desc.Digest]; ok {
+ mp.mu.RUnlock()
+ if cd, ok := p.(snapshotLabels); ok {
+ return cd.SnapshotLabels(descs, index)
+ }
+ } else {
+ mp.mu.RUnlock()
+ }
+ if cd, ok := mp.base.(snapshotLabels); ok {
+ return cd.SnapshotLabels(descs, index)
+ }
+ return nil
+}
+
+func (mp *MultiProvider) CheckDescriptor(ctx context.Context, desc ocispecs.Descriptor) error {
+ type checkDescriptor interface {
+ CheckDescriptor(context.Context, ocispecs.Descriptor) error
+ }
+
+ mp.mu.RLock()
+ if p, ok := mp.sub[desc.Digest]; ok {
+ mp.mu.RUnlock()
+ if cd, ok := p.(checkDescriptor); ok {
+ return cd.CheckDescriptor(ctx, desc)
+ }
+ } else {
+ mp.mu.RUnlock()
+ }
+ if cd, ok := mp.base.(checkDescriptor); ok {
+ return cd.CheckDescriptor(ctx, desc)
+ }
+ return nil
+}
+
+// ReaderAt returns a content.ReaderAt
+func (mp *MultiProvider) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
+ mp.mu.RLock()
+ if p, ok := mp.sub[desc.Digest]; ok {
+ mp.mu.RUnlock()
+ return p.ReaderAt(ctx, desc)
+ }
+ mp.mu.RUnlock()
+ if mp.base == nil {
+ return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", desc.Digest)
+ }
+ return mp.base.ReaderAt(ctx, desc)
+}
+
+// Add adds a new child provider for a specific digest
+func (mp *MultiProvider) Add(dgst digest.Digest, p content.Provider) {
+ mp.mu.Lock()
+ defer mp.mu.Unlock()
+ mp.sub[dgst] = p
+}
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/pusher.go b/vendor/github.com/moby/buildkit/util/contentutil/pusher.go
new file mode 100644
index 00000000..693dcfea
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/pusher.go
@@ -0,0 +1,122 @@
+package contentutil
+
+import (
+ "context"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/errdefs"
+ "github.com/containerd/containerd/remotes"
+ digest "github.com/opencontainers/go-digest"
+ "github.com/pkg/errors"
+)
+
+func FromPusher(p remotes.Pusher) content.Ingester {
+ var mu sync.Mutex
+ c := sync.NewCond(&mu)
+ return &pushingIngester{
+ mu: &mu,
+ c: c,
+ p: p,
+ active: map[digest.Digest]struct{}{},
+ }
+}
+
+type pushingIngester struct {
+ p remotes.Pusher
+
+ mu *sync.Mutex
+ c *sync.Cond
+ active map[digest.Digest]struct{}
+}
+
+// Writer implements content.Ingester. desc.MediaType must be set for manifest blobs.
+func (i *pushingIngester) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
+ var wOpts content.WriterOpts
+ for _, opt := range opts {
+ if err := opt(&wOpts); err != nil {
+ return nil, err
+ }
+ }
+ if wOpts.Ref == "" {
+ return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty")
+ }
+
+ st := time.Now()
+
+ i.mu.Lock()
+ for {
+ if time.Since(st) > time.Hour {
+ i.mu.Unlock()
+ return nil, errors.Wrapf(errdefs.ErrUnavailable, "ref %v locked", wOpts.Desc.Digest)
+ }
+ if _, ok := i.active[wOpts.Desc.Digest]; ok {
+ i.c.Wait()
+ } else {
+ break
+ }
+ }
+
+ i.active[wOpts.Desc.Digest] = struct{}{}
+ i.mu.Unlock()
+
+ var once sync.Once
+ release := func() {
+ once.Do(func() {
+ i.mu.Lock()
+ delete(i.active, wOpts.Desc.Digest)
+ i.c.Broadcast()
+ i.mu.Unlock()
+ })
+ }
+
+ // pusher requires desc.MediaType to determine the PUT URL, especially for manifest blobs.
+ contentWriter, err := i.p.Push(ctx, wOpts.Desc)
+ if err != nil {
+ release()
+ return nil, err
+ }
+ runtime.SetFinalizer(contentWriter, func(_ content.Writer) {
+ release()
+ })
+ return &writer{
+ Writer: contentWriter,
+ contentWriterRef: wOpts.Ref,
+ release: release,
+ }, nil
+}
+
+type writer struct {
+ content.Writer // returned from pusher.Push
+ contentWriterRef string // ref passed for Writer()
+ release func()
+}
+
+func (w *writer) Status() (content.Status, error) {
+ st, err := w.Writer.Status()
+ if err != nil {
+ return st, err
+ }
+ if w.contentWriterRef != "" {
+ st.Ref = w.contentWriterRef
+ }
+ return st, nil
+}
+
+func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
+ err := w.Writer.Commit(ctx, size, expected, opts...)
+ if w.release != nil {
+ w.release()
+ }
+ return err
+}
+
+func (w *writer) Close() error {
+ err := w.Writer.Close()
+ if w.release != nil {
+ w.release()
+ }
+ return err
+}
diff --git a/vendor/github.com/moby/buildkit/util/contentutil/refs.go b/vendor/github.com/moby/buildkit/util/contentutil/refs.go
new file mode 100644
index 00000000..16fb9aaf
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/contentutil/refs.go
@@ -0,0 +1,109 @@
+package contentutil
+
+import (
+ "context"
+ "net/http"
+ "sync"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/errdefs"
+ "github.com/containerd/containerd/remotes"
+ "github.com/containerd/containerd/remotes/docker"
+ "github.com/moby/buildkit/version"
+ "github.com/moby/locker"
+ digest "github.com/opencontainers/go-digest"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func ProviderFromRef(ref string) (ocispecs.Descriptor, content.Provider, error) {
+ headers := http.Header{}
+ headers.Set("User-Agent", version.UserAgent())
+ remote := docker.NewResolver(docker.ResolverOptions{
+ Client: http.DefaultClient,
+ Headers: headers,
+ })
+
+ name, desc, err := remote.Resolve(context.TODO(), ref)
+ if err != nil {
+ return ocispecs.Descriptor{}, nil, err
+ }
+
+ fetcher, err := remote.Fetcher(context.TODO(), name)
+ if err != nil {
+ return ocispecs.Descriptor{}, nil, err
+ }
+ return desc, FromFetcher(fetcher), nil
+}
+
+func IngesterFromRef(ref string) (content.Ingester, error) {
+ headers := http.Header{}
+ headers.Set("User-Agent", version.UserAgent())
+ remote := docker.NewResolver(docker.ResolverOptions{
+ Client: http.DefaultClient,
+ Headers: headers,
+ })
+
+ p, err := remote.Pusher(context.TODO(), ref)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ingester{
+ locker: locker.New(),
+ pusher: &pusher{p},
+ }, nil
+}
+
+type pusher struct {
+ remotes.Pusher
+}
+
+type ingester struct {
+ locker *locker.Locker
+ pusher remotes.Pusher
+}
+
+func (w *ingester) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
+ var wo content.WriterOpts
+ for _, o := range opts {
+ if err := o(&wo); err != nil {
+ return nil, err
+ }
+ }
+ if wo.Ref == "" {
+ return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty")
+ }
+ w.locker.Lock(wo.Ref)
+ var once sync.Once
+ unlock := func() {
+ once.Do(func() {
+ w.locker.Unlock(wo.Ref)
+ })
+ }
+ writer, err := w.pusher.Push(ctx, wo.Desc)
+ if err != nil {
+ unlock()
+ return nil, err
+ }
+ return &lockedWriter{unlock: unlock, Writer: writer}, nil
+}
+
+type lockedWriter struct {
+ unlock func()
+ content.Writer
+}
+
+func (w *lockedWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
+ err := w.Writer.Commit(ctx, size, expected, opts...)
+ if err == nil {
+ w.unlock()
+ }
+ return err
+}
+
+func (w *lockedWriter) Close() error {
+ err := w.Writer.Close()
+ w.unlock()
+ return err
+}
diff --git a/vendor/github.com/moby/buildkit/util/imageutil/config.go b/vendor/github.com/moby/buildkit/util/imageutil/config.go
new file mode 100644
index 00000000..cfb9d417
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/imageutil/config.go
@@ -0,0 +1,222 @@
+package imageutil
+
+import (
+ "context"
+ "encoding/json"
+ "sync"
+ "time"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/images"
+ "github.com/containerd/containerd/leases"
+ "github.com/containerd/containerd/platforms"
+ "github.com/containerd/containerd/reference"
+ "github.com/containerd/containerd/remotes"
+ "github.com/containerd/containerd/remotes/docker"
+ "github.com/moby/buildkit/util/leaseutil"
+ "github.com/moby/buildkit/util/resolver/limited"
+ "github.com/moby/buildkit/util/resolver/retryhandler"
+ digest "github.com/opencontainers/go-digest"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+type ContentCache interface {
+ content.Ingester
+ content.Provider
+}
+
+var leasesMu sync.Mutex
+var leasesF []func(context.Context) error
+
+func CancelCacheLeases() {
+ leasesMu.Lock()
+ for _, f := range leasesF {
+ f(context.TODO())
+ }
+ leasesF = nil
+ leasesMu.Unlock()
+}
+
+func AddLease(f func(context.Context) error) {
+ leasesMu.Lock()
+ leasesF = append(leasesF, f)
+ leasesMu.Unlock()
+}
+
+func Config(ctx context.Context, str string, resolver remotes.Resolver, cache ContentCache, leaseManager leases.Manager, p *ocispecs.Platform) (digest.Digest, []byte, error) {
+ // TODO: fix buildkit to take interface instead of struct
+ var platform platforms.MatchComparer
+ if p != nil {
+ platform = platforms.Only(*p)
+ } else {
+ platform = platforms.Default()
+ }
+ ref, err := reference.Parse(str)
+ if err != nil {
+ return "", nil, errors.WithStack(err)
+ }
+
+ if leaseManager != nil {
+ ctx2, done, err := leaseutil.WithLease(ctx, leaseManager, leases.WithExpiration(5*time.Minute), leaseutil.MakeTemporary)
+ if err != nil {
+ return "", nil, errors.WithStack(err)
+ }
+ ctx = ctx2
+ defer func() {
+ // this lease is not deleted to allow other components to access manifest/config from cache. It will be deleted after 5 min deadline or on pruning inactive builder
+ AddLease(done)
+ }()
+ }
+
+ desc := ocispecs.Descriptor{
+ Digest: ref.Digest(),
+ }
+ if desc.Digest != "" {
+ ra, err := cache.ReaderAt(ctx, desc)
+ if err == nil {
+ desc.Size = ra.Size()
+ mt, err := DetectManifestMediaType(ra)
+ if err == nil {
+ desc.MediaType = mt
+ }
+ }
+ }
+ // use resolver if desc is incomplete
+ if desc.MediaType == "" {
+ _, desc, err = resolver.Resolve(ctx, ref.String())
+ if err != nil {
+ return "", nil, err
+ }
+ }
+
+ fetcher, err := resolver.Fetcher(ctx, ref.String())
+ if err != nil {
+ return "", nil, err
+ }
+
+ if desc.MediaType == images.MediaTypeDockerSchema1Manifest {
+ return readSchema1Config(ctx, ref.String(), desc, fetcher, cache)
+ }
+
+ children := childrenConfigHandler(cache, platform)
+
+ handlers := []images.Handler{
+ retryhandler.New(limited.FetchHandler(cache, fetcher, str), func(_ []byte) {}),
+ children,
+ }
+ if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
+ return "", nil, err
+ }
+ config, err := images.Config(ctx, cache, desc, platform)
+ if err != nil {
+ return "", nil, err
+ }
+
+ dt, err := content.ReadBlob(ctx, cache, config)
+ if err != nil {
+ return "", nil, err
+ }
+
+ return desc.Digest, dt, nil
+}
+
+func childrenConfigHandler(provider content.Provider, platform platforms.MatchComparer) images.HandlerFunc {
+ return func(ctx context.Context, desc ocispecs.Descriptor) ([]ocispecs.Descriptor, error) {
+ var descs []ocispecs.Descriptor
+ switch desc.MediaType {
+ case images.MediaTypeDockerSchema2Manifest, ocispecs.MediaTypeImageManifest:
+ p, err := content.ReadBlob(ctx, provider, desc)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO(stevvooe): We just assume oci manifest, for now. There may be
+ // subtle differences from the docker version.
+ var manifest ocispecs.Manifest
+ if err := json.Unmarshal(p, &manifest); err != nil {
+ return nil, err
+ }
+
+ descs = append(descs, manifest.Config)
+ case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
+ p, err := content.ReadBlob(ctx, provider, desc)
+ if err != nil {
+ return nil, err
+ }
+
+ var index ocispecs.Index
+ if err := json.Unmarshal(p, &index); err != nil {
+ return nil, err
+ }
+
+ if platform != nil {
+ for _, d := range index.Manifests {
+ if d.Platform == nil || platform.Match(*d.Platform) {
+ descs = append(descs, d)
+ }
+ }
+ } else {
+ descs = append(descs, index.Manifests...)
+ }
+ case images.MediaTypeDockerSchema2Config, ocispecs.MediaTypeImageConfig, docker.LegacyConfigMediaType:
+ // childless data types.
+ return nil, nil
+ default:
+ return nil, errors.Errorf("encountered unknown type %v; children may not be fetched", desc.MediaType)
+ }
+
+ return descs, nil
+ }
+}
+
+// specs.MediaTypeImageManifest, // TODO: detect schema1/manifest-list
+func DetectManifestMediaType(ra content.ReaderAt) (string, error) {
+ // TODO: schema1
+
+ dt := make([]byte, ra.Size())
+ if _, err := ra.ReadAt(dt, 0); err != nil {
+ return "", err
+ }
+
+ return DetectManifestBlobMediaType(dt)
+}
+
+func DetectManifestBlobMediaType(dt []byte) (string, error) {
+ var mfst struct {
+ MediaType *string `json:"mediaType"`
+ Config json.RawMessage `json:"config"`
+ Manifests json.RawMessage `json:"manifests"`
+ Layers json.RawMessage `json:"layers"`
+ }
+
+ if err := json.Unmarshal(dt, &mfst); err != nil {
+ return "", err
+ }
+
+ mt := images.MediaTypeDockerSchema2ManifestList
+
+ if mfst.Config != nil || mfst.Layers != nil {
+ mt = images.MediaTypeDockerSchema2Manifest
+
+ if mfst.Manifests != nil {
+ return "", errors.Errorf("invalid ambiguous manifest and manifest list")
+ }
+ }
+
+ if mfst.MediaType != nil {
+ switch *mfst.MediaType {
+ case images.MediaTypeDockerSchema2ManifestList, ocispecs.MediaTypeImageIndex:
+ if mt != images.MediaTypeDockerSchema2ManifestList {
+ return "", errors.Errorf("mediaType in manifest does not match manifest contents")
+ }
+ mt = *mfst.MediaType
+ case images.MediaTypeDockerSchema2Manifest, ocispecs.MediaTypeImageManifest:
+ if mt != images.MediaTypeDockerSchema2Manifest {
+ return "", errors.Errorf("mediaType in manifest does not match manifest contents")
+ }
+ mt = *mfst.MediaType
+ }
+ }
+ return mt, nil
+}
diff --git a/vendor/github.com/moby/buildkit/util/imageutil/schema1.go b/vendor/github.com/moby/buildkit/util/imageutil/schema1.go
new file mode 100644
index 00000000..10838bf5
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/imageutil/schema1.go
@@ -0,0 +1,87 @@
+package imageutil
+
+import (
+ "context"
+ "encoding/json"
+ "io/ioutil"
+ "strings"
+ "time"
+
+ "github.com/containerd/containerd/remotes"
+ digest "github.com/opencontainers/go-digest"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func readSchema1Config(ctx context.Context, ref string, desc ocispecs.Descriptor, fetcher remotes.Fetcher, cache ContentCache) (digest.Digest, []byte, error) {
+ rc, err := fetcher.Fetch(ctx, desc)
+ if err != nil {
+ return "", nil, err
+ }
+ defer rc.Close()
+ dt, err := ioutil.ReadAll(rc)
+ if err != nil {
+ return "", nil, errors.Wrap(err, "failed to fetch schema1 manifest")
+ }
+ dt, err = convertSchema1ConfigMeta(dt)
+ if err != nil {
+ return "", nil, err
+ }
+ return desc.Digest, dt, nil
+}
+
+func convertSchema1ConfigMeta(in []byte) ([]byte, error) {
+ type history struct {
+ V1Compatibility string `json:"v1Compatibility"`
+ }
+ var m struct {
+ History []history `json:"history"`
+ }
+ if err := json.Unmarshal(in, &m); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal schema1 manifest")
+ }
+ if len(m.History) == 0 {
+ return nil, errors.Errorf("invalid schema1 manifest")
+ }
+
+ var img ocispecs.Image
+ if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), &img); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal image from schema 1 history")
+ }
+
+ img.RootFS = ocispecs.RootFS{
+ Type: "layers", // filled in by exporter
+ }
+ img.History = make([]ocispecs.History, len(m.History))
+
+ for i := range m.History {
+ var h v1History
+ if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil {
+ return nil, errors.Wrap(err, "failed to unmarshal history")
+ }
+ img.History[len(m.History)-i-1] = ocispecs.History{
+ Author: h.Author,
+ Comment: h.Comment,
+ Created: &h.Created,
+ CreatedBy: strings.Join(h.ContainerConfig.Cmd, " "),
+ EmptyLayer: (h.ThrowAway != nil && *h.ThrowAway) || (h.Size != nil && *h.Size == 0),
+ }
+ }
+
+ dt, err := json.MarshalIndent(img, "", " ")
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to marshal schema1 config")
+ }
+ return dt, nil
+}
+
+type v1History struct {
+ Author string `json:"author,omitempty"`
+ Created time.Time `json:"created"`
+ Comment string `json:"comment,omitempty"`
+ ThrowAway *bool `json:"throwaway,omitempty"`
+ Size *int `json:"Size,omitempty"` // used before ThrowAway field
+ ContainerConfig struct {
+ Cmd []string `json:"Cmd,omitempty"`
+ } `json:"container_config,omitempty"`
+}
diff --git a/vendor/github.com/moby/buildkit/util/leaseutil/manager.go b/vendor/github.com/moby/buildkit/util/leaseutil/manager.go
new file mode 100644
index 00000000..45a35273
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/leaseutil/manager.go
@@ -0,0 +1,75 @@
+package leaseutil
+
+import (
+ "context"
+ "time"
+
+ "github.com/containerd/containerd/leases"
+ "github.com/containerd/containerd/namespaces"
+)
+
+func WithLease(ctx context.Context, ls leases.Manager, opts ...leases.Opt) (context.Context, func(context.Context) error, error) {
+ _, ok := leases.FromContext(ctx)
+ if ok {
+ return ctx, func(context.Context) error {
+ return nil
+ }, nil
+ }
+
+ l, err := ls.Create(ctx, append([]leases.Opt{leases.WithRandomID(), leases.WithExpiration(time.Hour)}, opts...)...)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ ctx = leases.WithLease(ctx, l.ID)
+ return ctx, func(ctx context.Context) error {
+ return ls.Delete(ctx, l)
+ }, nil
+}
+
+func MakeTemporary(l *leases.Lease) error {
+ if l.Labels == nil {
+ l.Labels = map[string]string{}
+ }
+ l.Labels["buildkit/lease.temporary"] = time.Now().UTC().Format(time.RFC3339Nano)
+ return nil
+}
+
+func WithNamespace(lm leases.Manager, ns string) leases.Manager {
+ return &nsLM{manager: lm, ns: ns}
+}
+
+type nsLM struct {
+ manager leases.Manager
+ ns string
+}
+
+func (l *nsLM) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.Create(ctx, opts...)
+}
+
+func (l *nsLM) Delete(ctx context.Context, lease leases.Lease, opts ...leases.DeleteOpt) error {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.Delete(ctx, lease, opts...)
+}
+
+func (l *nsLM) List(ctx context.Context, filters ...string) ([]leases.Lease, error) {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.List(ctx, filters...)
+}
+
+func (l *nsLM) AddResource(ctx context.Context, lease leases.Lease, resource leases.Resource) error {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.AddResource(ctx, lease, resource)
+}
+
+func (l *nsLM) DeleteResource(ctx context.Context, lease leases.Lease, resource leases.Resource) error {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.DeleteResource(ctx, lease, resource)
+}
+
+func (l *nsLM) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) {
+ ctx = namespaces.WithNamespace(ctx, l.ns)
+ return l.manager.ListResources(ctx, lease)
+}
diff --git a/vendor/github.com/moby/buildkit/util/resolver/limited/group.go b/vendor/github.com/moby/buildkit/util/resolver/limited/group.go
new file mode 100644
index 00000000..7fdd947a
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/resolver/limited/group.go
@@ -0,0 +1,175 @@
+package limited
+
+import (
+ "context"
+ "io"
+ "runtime"
+ "strings"
+ "sync"
+
+ "github.com/containerd/containerd/content"
+ "github.com/containerd/containerd/images"
+ "github.com/containerd/containerd/remotes"
+ "github.com/docker/distribution/reference"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sync/semaphore"
+)
+
+type contextKeyT string
+
+var contextKey = contextKeyT("buildkit/util/resolver/limited")
+
+var Default = New(4)
+
+type Group struct {
+ mu sync.Mutex
+ size int
+ sem map[string][2]*semaphore.Weighted
+}
+
+type req struct {
+ g *Group
+ ref string
+}
+
+func (r *req) acquire(ctx context.Context, desc ocispecs.Descriptor) (context.Context, func(), error) {
+ if v := ctx.Value(contextKey); v != nil {
+ return ctx, func() {}, nil
+ }
+
+ ctx = context.WithValue(ctx, contextKey, struct{}{})
+
+ // json request get one additional connection
+ highPriority := strings.HasSuffix(desc.MediaType, "+json")
+
+ r.g.mu.Lock()
+ s, ok := r.g.sem[r.ref]
+ if !ok {
+ s = [2]*semaphore.Weighted{
+ semaphore.NewWeighted(int64(r.g.size)),
+ semaphore.NewWeighted(int64(r.g.size + 1)),
+ }
+ r.g.sem[r.ref] = s
+ }
+ r.g.mu.Unlock()
+ if !highPriority {
+ if err := s[0].Acquire(ctx, 1); err != nil {
+ return ctx, nil, err
+ }
+ }
+ if err := s[1].Acquire(ctx, 1); err != nil {
+ if !highPriority {
+ s[0].Release(1)
+ }
+ return ctx, nil, err
+ }
+ return ctx, func() {
+ s[1].Release(1)
+ if !highPriority {
+ s[0].Release(1)
+ }
+ }, nil
+}
+
+func New(size int) *Group {
+ return &Group{
+ size: size,
+ sem: make(map[string][2]*semaphore.Weighted),
+ }
+}
+
+func (g *Group) req(ref string) *req {
+ return &req{g: g, ref: domain(ref)}
+}
+
+func (g *Group) WrapFetcher(f remotes.Fetcher, ref string) remotes.Fetcher {
+ return &fetcher{Fetcher: f, req: g.req(ref)}
+}
+
+func (g *Group) PushHandler(pusher remotes.Pusher, provider content.Provider, ref string) images.HandlerFunc {
+ ph := remotes.PushHandler(pusher, provider)
+ req := g.req(ref)
+ return func(ctx context.Context, desc ocispecs.Descriptor) ([]ocispecs.Descriptor, error) {
+ ctx, release, err := req.acquire(ctx, desc)
+ if err != nil {
+ return nil, err
+ }
+ defer release()
+ return ph(ctx, desc)
+ }
+}
+
+type fetcher struct {
+ remotes.Fetcher
+ req *req
+}
+
+func (f *fetcher) Fetch(ctx context.Context, desc ocispecs.Descriptor) (io.ReadCloser, error) {
+ ctx, release, err := f.req.acquire(ctx, desc)
+ if err != nil {
+ return nil, err
+ }
+ rc, err := f.Fetcher.Fetch(ctx, desc)
+ if err != nil {
+ release()
+ return nil, err
+ }
+
+ rcw := &readCloser{ReadCloser: rc}
+ closer := func() {
+ if !rcw.closed {
+ logrus.Warnf("fetcher not closed cleanly: %s", desc.Digest)
+ }
+ release()
+ }
+ rcw.release = closer
+ runtime.SetFinalizer(rcw, func(rc *readCloser) {
+ rc.close()
+ })
+
+ if s, ok := rc.(io.Seeker); ok {
+ return &readCloserSeeker{rcw, s}, nil
+ }
+
+ return rcw, nil
+}
+
+type readCloserSeeker struct {
+ *readCloser
+ io.Seeker
+}
+
+type readCloser struct {
+ io.ReadCloser
+ once sync.Once
+ closed bool
+ release func()
+}
+
+func (r *readCloser) Close() error {
+ r.closed = true
+ r.close()
+ return r.ReadCloser.Close()
+}
+
+func (r *readCloser) close() {
+ r.once.Do(r.release)
+}
+
+func FetchHandler(ingester content.Ingester, fetcher remotes.Fetcher, ref string) images.HandlerFunc {
+ return remotes.FetchHandler(ingester, Default.WrapFetcher(fetcher, ref))
+}
+
+func PushHandler(pusher remotes.Pusher, provider content.Provider, ref string) images.HandlerFunc {
+ return Default.PushHandler(pusher, provider, ref)
+}
+
+func domain(ref string) string {
+ if ref != "" {
+ if named, err := reference.ParseNormalizedNamed(ref); err == nil {
+ return reference.Domain(named)
+ }
+ }
+ return ref
+}
diff --git a/vendor/github.com/moby/buildkit/util/resolver/retryhandler/retry.go b/vendor/github.com/moby/buildkit/util/resolver/retryhandler/retry.go
new file mode 100644
index 00000000..554076b0
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/util/resolver/retryhandler/retry.go
@@ -0,0 +1,72 @@
+package retryhandler
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net"
+ "syscall"
+ "time"
+
+ "github.com/containerd/containerd/images"
+ remoteserrors "github.com/containerd/containerd/remotes/errors"
+ ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+func New(f images.HandlerFunc, logger func([]byte)) images.HandlerFunc {
+ return func(ctx context.Context, desc ocispecs.Descriptor) ([]ocispecs.Descriptor, error) {
+ backoff := time.Second
+ for {
+ descs, err := f(ctx, desc)
+ if err != nil {
+ select {
+ case <-ctx.Done():
+ return nil, err
+ default:
+ if !retryError(err) {
+ return nil, err
+ }
+ }
+ if logger != nil {
+ logger([]byte(fmt.Sprintf("error: %v\n", err.Error())))
+ }
+ } else {
+ return descs, nil
+ }
+ // backoff logic
+ if backoff >= 8*time.Second {
+ return nil, err
+ }
+ if logger != nil {
+ logger([]byte(fmt.Sprintf("retrying in %v\n", backoff)))
+ }
+ time.Sleep(backoff)
+ backoff *= 2
+ }
+ }
+}
+
+func retryError(err error) bool {
+ // Retry on 5xx errors
+ var errUnexpectedStatus remoteserrors.ErrUnexpectedStatus
+ if errors.As(err, &errUnexpectedStatus) &&
+ errUnexpectedStatus.StatusCode >= 500 &&
+ errUnexpectedStatus.StatusCode <= 599 {
+ return true
+ }
+
+ if errors.Is(err, io.EOF) || errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.EPIPE) || errors.Is(err, net.ErrClosed) {
+ return true
+ }
+ // catches TLS timeout or other network-related temporary errors
+ if ne, ok := errors.Cause(err).(net.Error); ok && ne.Temporary() {
+ return true
+ }
+ // https://github.com/containerd/containerd/pull/4724
+ if errors.Cause(err).Error() == "no response" {
+ return true
+ }
+
+ return false
+}
diff --git a/vendor/github.com/moby/buildkit/version/version.go b/vendor/github.com/moby/buildkit/version/version.go
new file mode 100644
index 00000000..137c4a13
--- /dev/null
+++ b/vendor/github.com/moby/buildkit/version/version.go
@@ -0,0 +1,62 @@
+/*
+ Copyright The BuildKit Authors.
+ 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 version
+
+import (
+ "regexp"
+ "sync"
+)
+
+const (
+ defaultVersion = "0.0.0+unknown"
+)
+
+var (
+ // Package is filled at linking time
+ Package = "github.com/moby/buildkit"
+
+ // Version holds the complete version number. Filled in at linking time.
+ Version = defaultVersion
+
+ // Revision is filled with the VCS (e.g. git) revision being used to build
+ // the program at linking time.
+ Revision = ""
+)
+
+var (
+ reRelease *regexp.Regexp
+ reDev *regexp.Regexp
+ reOnce sync.Once
+)
+
+func UserAgent() string {
+ version := defaultVersion
+
+ reOnce.Do(func() {
+ reRelease = regexp.MustCompile(`^(v[0-9]+\.[0-9]+)\.[0-9]+$`)
+ reDev = regexp.MustCompile(`^(v[0-9]+\.[0-9]+)\.[0-9]+`)
+ })
+
+ if matches := reRelease.FindAllStringSubmatch(version, 1); len(matches) > 0 {
+ version = matches[0][1]
+ } else if matches := reDev.FindAllStringSubmatch(version, 1); len(matches) > 0 {
+ version = matches[0][1] + "-dev"
+ }
+
+ return "buildkit/" + version
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 635d28f4..601341fb 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -55,9 +55,12 @@ github.com/containerd/containerd/content/proxy
github.com/containerd/containerd/defaults
github.com/containerd/containerd/errdefs
github.com/containerd/containerd/filters
+github.com/containerd/containerd/identifiers
github.com/containerd/containerd/images
github.com/containerd/containerd/labels
+github.com/containerd/containerd/leases
github.com/containerd/containerd/log
+github.com/containerd/containerd/namespaces
github.com/containerd/containerd/pkg/seed
github.com/containerd/containerd/pkg/userns
github.com/containerd/containerd/platforms
@@ -71,6 +74,8 @@ github.com/containerd/containerd/services/content/contentserver
github.com/containerd/containerd/version
# github.com/containerd/continuity v0.2.2
github.com/containerd/continuity/sysx
+# github.com/containerd/ttrpc v1.1.0
+github.com/containerd/ttrpc
# github.com/containerd/typeurl v1.0.2
github.com/containerd/typeurl
# github.com/davecgh/go-spew v1.1.1
@@ -293,7 +298,7 @@ github.com/miekg/pkcs11
github.com/mitchellh/go-wordwrap
# github.com/mitchellh/mapstructure v1.4.2
github.com/mitchellh/mapstructure
-# github.com/moby/buildkit v0.10.0-rc1.0.20220223192213-b124b0c3f19c
+# github.com/moby/buildkit v0.10.0-rc1.0.20220223201314-b0e56cd5633a
## explicit
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types
@@ -322,19 +327,26 @@ github.com/moby/buildkit/session/upload
github.com/moby/buildkit/session/upload/uploadprovider
github.com/moby/buildkit/solver/errdefs
github.com/moby/buildkit/solver/pb
+github.com/moby/buildkit/source/types
github.com/moby/buildkit/util/apicaps
github.com/moby/buildkit/util/apicaps/pb
github.com/moby/buildkit/util/appcontext
github.com/moby/buildkit/util/appdefaults
github.com/moby/buildkit/util/bklog
+github.com/moby/buildkit/util/buildinfo/types
+github.com/moby/buildkit/util/contentutil
github.com/moby/buildkit/util/entitlements
github.com/moby/buildkit/util/flightcontrol
github.com/moby/buildkit/util/gitutil
github.com/moby/buildkit/util/grpcerrors
+github.com/moby/buildkit/util/imageutil
+github.com/moby/buildkit/util/leaseutil
github.com/moby/buildkit/util/progress
github.com/moby/buildkit/util/progress/progressui
github.com/moby/buildkit/util/progress/progresswriter
github.com/moby/buildkit/util/resolver/config
+github.com/moby/buildkit/util/resolver/limited
+github.com/moby/buildkit/util/resolver/retryhandler
github.com/moby/buildkit/util/sshutil
github.com/moby/buildkit/util/stack
github.com/moby/buildkit/util/system
@@ -343,6 +355,7 @@ github.com/moby/buildkit/util/tracing/detect
github.com/moby/buildkit/util/tracing/detect/delegated
github.com/moby/buildkit/util/tracing/env
github.com/moby/buildkit/util/tracing/otlptracegrpc
+github.com/moby/buildkit/version
# github.com/moby/locker v1.0.1
github.com/moby/locker
# github.com/moby/spdystream v0.2.0