Add new "bashbrew remote arches" command
This command will, given a remote image reference, look up the list of platforms from it and match them to supported bashbrew architectures (providing content descriptors for each). Also, refactor registry code to be more correct: previously, this couldn't fetch from Docker without `DOCKERHUB_PUBLIC_PROXY` (see `registry-1.docker.io` change) and was ignoring content digests. Now it works correctly with or without `DOCKERHUB_PUBLIC_PROXY`, verifies the size of every object it pulls, verifies the digest, _and_ should continue working with the in-progress Moby containerd-integration (where the local image ID becomes the digest of the manifest or index instead of the digest of the config blob as it is today).
This commit is contained in:
parent
0feb2b9342
commit
b20e82cb0d
|
|
@ -6,4 +6,5 @@
|
|||
!go.sum
|
||||
!manifest/
|
||||
!pkg/
|
||||
!registry/
|
||||
!scripts/
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package architecture
|
||||
|
||||
import "path"
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// https://github.com/opencontainers/image-spec/blob/v1.0.1/image-index.md#image-index-property-descriptions
|
||||
// see "platform" (under "manifests")
|
||||
type OCIPlatform struct {
|
||||
OS string `json:"os"`
|
||||
Architecture string `json:"architecture"`
|
||||
Variant string `json:"variant,omitempty"`
|
||||
|
||||
//OSVersion string `json:"os.version,omitempty"`
|
||||
//OSFeatures []string `json:"os.features,omitempty"`
|
||||
}
|
||||
type OCIPlatform ocispec.Platform
|
||||
|
||||
var SupportedArches = map[string]OCIPlatform{
|
||||
"amd64": {OS: "linux", Architecture: "amd64"},
|
||||
|
|
@ -36,3 +34,18 @@ func (p OCIPlatform) String() string {
|
|||
p.Variant,
|
||||
)
|
||||
}
|
||||
|
||||
func Normalize(p ocispec.Platform) ocispec.Platform {
|
||||
p = platforms.Normalize(p)
|
||||
if p.Architecture == "arm64" && p.Variant == "" {
|
||||
// 😭 https://github.com/containerd/containerd/blob/1c90a442489720eec95342e1789ee8a5e1b9536f/platforms/database.go#L98 (inconsistent normalization of "linux/arm -> linux/arm/v7" vs "linux/arm64/v8 -> linux/arm64")
|
||||
p.Variant = "v8"
|
||||
// TODO get pedantic about amd64 variants too? (in our defense, those variants didn't exist when we defined our "amd64", unlike "arm64v8" 👀)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p OCIPlatform) Is(q OCIPlatform) bool {
|
||||
// (assumes "p" and "q" are both already bashbrew normalized, like one of the SupportedArches above)
|
||||
return p.OS == q.OS && p.Architecture == q.Architecture && p.Variant == q.Variant
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker-library/bashbrew/architecture"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
|
|
@ -21,3 +23,45 @@ func TestString(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIs(t *testing.T) {
|
||||
tests := map[bool][][2]architecture.OCIPlatform{
|
||||
true: {
|
||||
{architecture.SupportedArches["amd64"], architecture.SupportedArches["amd64"]},
|
||||
{architecture.SupportedArches["arm32v5"], architecture.SupportedArches["arm32v5"]},
|
||||
{architecture.SupportedArches["arm32v6"], architecture.SupportedArches["arm32v6"]},
|
||||
{architecture.SupportedArches["arm32v7"], architecture.SupportedArches["arm32v7"]},
|
||||
{architecture.SupportedArches["arm64v8"], architecture.OCIPlatform{OS: "linux", Architecture: "arm64", Variant: "v8"}},
|
||||
{architecture.SupportedArches["windows-amd64"], architecture.OCIPlatform{OS: "windows", Architecture: "amd64", OSVersion: "1.2.3.4"}},
|
||||
},
|
||||
false: {
|
||||
{architecture.SupportedArches["amd64"], architecture.OCIPlatform{OS: "linux", Architecture: "amd64", Variant: "v4"}},
|
||||
{architecture.SupportedArches["amd64"], architecture.SupportedArches["arm64v8"]},
|
||||
{architecture.SupportedArches["amd64"], architecture.SupportedArches["i386"]},
|
||||
{architecture.SupportedArches["amd64"], architecture.SupportedArches["windows-amd64"]},
|
||||
{architecture.SupportedArches["arm32v7"], architecture.SupportedArches["arm32v6"]},
|
||||
{architecture.SupportedArches["arm32v7"], architecture.SupportedArches["arm64v8"]},
|
||||
{architecture.SupportedArches["arm64v8"], architecture.OCIPlatform{OS: "linux", Architecture: "arm64", Variant: "v9"}},
|
||||
},
|
||||
}
|
||||
for expected, test := range tests {
|
||||
for _, platforms := range test {
|
||||
t.Run(platforms[0].String()+" vs "+platforms[1].String(), func(t *testing.T) {
|
||||
if got := platforms[0].Is(platforms[1]); got != expected {
|
||||
t.Errorf("expected %v; got %v", expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
for arch, expected := range architecture.SupportedArches {
|
||||
t.Run(arch, func(t *testing.T) {
|
||||
normal := architecture.OCIPlatform(architecture.Normalize(ocispec.Platform(expected)))
|
||||
if !expected.Is(normal) {
|
||||
t.Errorf("expected %#v; got %#v", expected, normal)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ func cmdPush(c *cli.Context) error {
|
|||
}
|
||||
|
||||
// we can't use "r.Tags()" here because it will include SharedTags, which we never want to push directly (see "cmd-put-shared.go")
|
||||
TagsLoop:
|
||||
for i, tag := range entry.Tags {
|
||||
if uniq && i > 0 {
|
||||
break
|
||||
|
|
@ -47,10 +48,12 @@ func cmdPush(c *cli.Context) error {
|
|||
|
||||
if !force {
|
||||
localImageId, _ := dockerInspect("{{.Id}}", tag)
|
||||
registryImageId := fetchRegistryImageId(tag)
|
||||
if registryImageId != "" && localImageId == registryImageId {
|
||||
fmt.Fprintf(os.Stderr, "skipping %s (remote image matches local)\n", tag)
|
||||
continue
|
||||
registryImageIds := fetchRegistryImageIds(tag)
|
||||
for _, registryImageId := range registryImageIds {
|
||||
if localImageId == registryImageId {
|
||||
fmt.Fprintf(os.Stderr, "skipping %s (remote image matches local)\n", tag)
|
||||
continue TagsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("Pushing %s\n", tag)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/docker-library/bashbrew/registry"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func cmdRemoteArches(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("expected at least one argument")
|
||||
}
|
||||
doJson := c.Bool("json")
|
||||
ctx := context.Background()
|
||||
for _, arg := range args {
|
||||
img, err := registry.Resolve(ctx, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
arches, err := img.Architectures(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if doJson {
|
||||
ret := struct {
|
||||
Ref string `json:"ref"`
|
||||
Desc ocispec.Descriptor `json:"desc"`
|
||||
Arches map[string][]ocispec.Descriptor `json:"arches"`
|
||||
}{
|
||||
Ref: img.ImageRef,
|
||||
Desc: img.Desc,
|
||||
Arches: map[string][]ocispec.Descriptor{},
|
||||
}
|
||||
for arch, imgs := range arches {
|
||||
for _, obj := range imgs {
|
||||
ret.Arches[arch] = append(ret.Arches[arch], obj.Desc)
|
||||
}
|
||||
}
|
||||
out, err := json.Marshal(ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
} else {
|
||||
fmt.Printf("%s -> %s\n", img.ImageRef, img.Desc.Digest)
|
||||
|
||||
// Go.....
|
||||
keys := []string{}
|
||||
for arch := range arches {
|
||||
keys = append(keys, arch)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, arch := range keys {
|
||||
for _, obj := range arches[arch] {
|
||||
fmt.Printf(" %s -> %s\n", arch, obj.Desc.Digest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -239,6 +239,11 @@ func main() {
|
|||
Name: "target-namespace",
|
||||
Usage: `target namespace to act into ("docker tag namespace/repo:tag target-namespace/repo:tag", "docker push target-namespace/repo:tag")`,
|
||||
},
|
||||
|
||||
"json": cli.BoolFlag{
|
||||
Name: "json",
|
||||
Usage: "output machine-readable JSON instead of human-readable text",
|
||||
},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
|
|
@ -395,6 +400,22 @@ func main() {
|
|||
|
||||
Category: "plumbing",
|
||||
},
|
||||
{
|
||||
Name: "remote",
|
||||
Usage: "query registries for bashbrew-related data",
|
||||
Before: subcommandBeforeFactory("remote"),
|
||||
Category: "plumbing",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "arches",
|
||||
Usage: "returns a list of bashbrew architectures and content descriptors for the specified image(s)",
|
||||
Flags: []cli.Flag{
|
||||
commonFlags["json"],
|
||||
},
|
||||
Action: cmdRemoteArches,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
|
|
|
|||
|
|
@ -2,63 +2,48 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
dockerremote "github.com/containerd/containerd/remotes/docker"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/docker-library/bashbrew/registry"
|
||||
)
|
||||
|
||||
var registryImageIdCache = map[string]string{}
|
||||
var registryImageIdsCache = map[string][]string{}
|
||||
|
||||
// assumes the provided image name is NOT a manifest list (used for testing whether we need to "bashbrew push" or whether the remote image is already up-to-date)
|
||||
// this does NOT handle authentication, and will return the empty string for repositories which require it (causing "bashbrew push" to simply shell out to "docker push" which will handle authentication appropriately)
|
||||
func fetchRegistryImageId(image string) string {
|
||||
func fetchRegistryImageIds(image string) []string {
|
||||
ctx := context.Background()
|
||||
|
||||
ref, resolver, err := fetchRegistryResolveHelper(image)
|
||||
img, err := registry.Resolve(ctx, image)
|
||||
if err != nil {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
name, desc, err := resolver.Resolve(ctx, ref)
|
||||
digest := img.Desc.Digest.String()
|
||||
if ids, ok := registryImageIdsCache[digest]; ok {
|
||||
return ids
|
||||
}
|
||||
|
||||
manifests, err := img.Manifests(ctx)
|
||||
if err != nil {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
if desc.MediaType != images.MediaTypeDockerSchema2Manifest && desc.MediaType != ocispec.MediaTypeImageManifest {
|
||||
return ""
|
||||
ids := []string{}
|
||||
if img.IsImageIndex() {
|
||||
ids = append(ids, digest)
|
||||
}
|
||||
|
||||
digest := desc.Digest.String()
|
||||
if id, ok := registryImageIdCache[digest]; ok {
|
||||
return id
|
||||
for _, manifestDesc := range manifests {
|
||||
ids = append(ids, manifestDesc.Digest.String())
|
||||
manifest, err := img.At(manifestDesc).Manifest(ctx)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, manifest.Config.Digest.String())
|
||||
}
|
||||
|
||||
fetcher, err := resolver.Fetcher(ctx, name)
|
||||
if err != nil {
|
||||
return ""
|
||||
if len(ids) > 0 {
|
||||
registryImageIdsCache[digest] = ids
|
||||
}
|
||||
|
||||
r, err := fetcher.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var manifest ocispec.Manifest
|
||||
if err := json.NewDecoder(r).Decode(&manifest); err != nil {
|
||||
return ""
|
||||
}
|
||||
id := manifest.Config.Digest.String()
|
||||
if id != "" {
|
||||
registryImageIdCache[digest] = id
|
||||
}
|
||||
return id
|
||||
return ids
|
||||
}
|
||||
|
||||
var registryManifestListCache = map[string][]string{}
|
||||
|
|
@ -67,46 +52,22 @@ var registryManifestListCache = map[string][]string{}
|
|||
func fetchRegistryManiestListDigests(image string) []string {
|
||||
ctx := context.Background()
|
||||
|
||||
ref, resolver, err := fetchRegistryResolveHelper(image)
|
||||
img, err := registry.Resolve(ctx, image)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
name, desc, err := resolver.Resolve(ctx, ref)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
digest := desc.Digest.String()
|
||||
if desc.MediaType == images.MediaTypeDockerSchema2Manifest || desc.MediaType == ocispec.MediaTypeImageManifest {
|
||||
return []string{digest}
|
||||
}
|
||||
|
||||
if desc.MediaType != images.MediaTypeDockerSchema2ManifestList && desc.MediaType != ocispec.MediaTypeImageIndex {
|
||||
return nil
|
||||
}
|
||||
|
||||
digest := img.Desc.Digest.String()
|
||||
if digests, ok := registryManifestListCache[digest]; ok {
|
||||
return digests
|
||||
}
|
||||
|
||||
fetcher, err := resolver.Fetcher(ctx, name)
|
||||
manifests, err := img.Manifests(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := fetcher.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var manifestList ocispec.Index
|
||||
if err := json.NewDecoder(r).Decode(&manifestList); err != nil {
|
||||
return nil
|
||||
}
|
||||
digests := []string{}
|
||||
for _, manifest := range manifestList.Manifests {
|
||||
for _, manifest := range manifests {
|
||||
if manifest.Digest != "" {
|
||||
digests = append(digests, manifest.Digest.String())
|
||||
}
|
||||
|
|
@ -116,30 +77,3 @@ func fetchRegistryManiestListDigests(image string) []string {
|
|||
}
|
||||
return digests
|
||||
}
|
||||
|
||||
func fetchRegistryResolveHelper(image string) (string, remotes.Resolver, error) {
|
||||
ref, err := docker.ParseAnyReference(image)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if namedRef, ok := ref.(docker.Named); ok {
|
||||
// add ":latest" if necessary
|
||||
namedRef = docker.TagNameOnly(namedRef)
|
||||
ref = namedRef
|
||||
}
|
||||
return ref.String(), dockerremote.NewResolver(dockerremote.ResolverOptions{
|
||||
Host: func(host string) (string, error) {
|
||||
if host == "docker.io" {
|
||||
if publicProxy := os.Getenv("DOCKERHUB_PUBLIC_PROXY"); publicProxy != "" {
|
||||
if publicProxyURL, err := url.Parse(publicProxy); err == nil {
|
||||
// TODO Scheme (also not sure if "host:port" will be satisfactory to containerd here, but 🤷)
|
||||
return publicProxyURL.Host, nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return host, nil
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
|
|
|
|||
19
go.mod
19
go.mod
|
|
@ -3,9 +3,9 @@ module github.com/docker-library/bashbrew
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/containerd/containerd v1.5.11
|
||||
github.com/containerd/containerd v1.6.9
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/opencontainers/image-spec v1.0.2
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221013174636-8159c8264e2e
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/urfave/cli v1.22.5
|
||||
pault.ag/go/debian v0.12.0
|
||||
|
|
@ -13,7 +13,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
|
|
@ -28,16 +28,15 @@ require (
|
|||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 // indirect
|
||||
google.golang.org/grpc v1.42.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
|||
58
go.sum
58
go.sum
|
|
@ -5,9 +5,8 @@ github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwS
|
|||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
|
|
@ -19,18 +18,16 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
|
|||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/containerd v1.5.11 h1:+biZCY9Kns9t2J8L9hOqubjvNQBr1ULdmR7kL+omKoY=
|
||||
github.com/containerd/containerd v1.5.11/go.mod h1:FJl/l1urLXpO3oKDx2No2ouBno2GSI56nTl02HfHeZY=
|
||||
github.com/containerd/containerd v1.6.9 h1:IN/r8DUes/B5lEGTNfIiUkfZBtIQJGx2ai703dV6lRA=
|
||||
github.com/containerd/containerd v1.6.9/go.mod h1:XVicUvkxOrftE2Q1YWUXgZwkkAxwQYNOFzYWvfVfEfQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
|
|
@ -44,8 +41,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
|||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
|
|
@ -81,8 +77,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/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/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
|
|
@ -113,8 +110,8 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ
|
|||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221013174636-8159c8264e2e h1:s/Yjbl65/SrXqrMXDSP7eeC1vGZP3mOpya4rNeTwTKY=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221013174636-8159c8264e2e/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
@ -133,7 +130,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
|
@ -155,8 +151,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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=
|
||||
|
|
@ -168,11 +164,12 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||
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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -190,7 +187,9 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -199,14 +198,14 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
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=
|
||||
|
|
@ -221,17 +220,17 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
|||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 h1:DN5b3HU13J4sMd/QjDx34U6afpaexKTDdop+26pdjdk=
|
||||
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
@ -243,8 +242,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -257,8 +257,8 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
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=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"unicode"
|
||||
|
||||
// thanks, go-digest...
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
|
||||
"github.com/docker-library/bashbrew/architecture"
|
||||
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
dockerremote "github.com/containerd/containerd/remotes/docker"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type ResolvedObject struct {
|
||||
Desc ocispec.Descriptor
|
||||
|
||||
ImageRef string
|
||||
resolver remotes.Resolver
|
||||
fetcher remotes.Fetcher
|
||||
}
|
||||
|
||||
func (obj ResolvedObject) fetchJSON(ctx context.Context, v interface{}) error {
|
||||
// prevent go-digest panics later
|
||||
if err := obj.Desc.Digest.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// (perhaps use a containerd content store?? they do validation of all content they ingest, and then there's a cache)
|
||||
|
||||
r, err := obj.fetcher.Fetch(ctx, obj.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// make sure we can't possibly read (much) more than we're supposed to
|
||||
limited := &io.LimitedReader{
|
||||
R: r,
|
||||
N: obj.Desc.Size + 1, // +1 to allow us to detect if we read too much (see verification below)
|
||||
}
|
||||
|
||||
// copy all read data into the digest verifier so we can validate afterwards
|
||||
verifier := obj.Desc.Digest.Verifier()
|
||||
tee := io.TeeReader(limited, verifier)
|
||||
|
||||
// decode directly! (mostly avoids double memory hit for big objects)
|
||||
// (TODO protect against malicious objects somehow?)
|
||||
if err := json.NewDecoder(tee).Decode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read anything leftover ...
|
||||
bs, err := io.ReadAll(tee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ... and make sure it was just whitespace, if anything
|
||||
for _, b := range bs {
|
||||
if !unicode.IsSpace(rune(b)) {
|
||||
return fmt.Errorf("unexpected non-whitespace at the end of %q: %+v\n", obj.Desc.Digest.String(), rune(b))
|
||||
}
|
||||
}
|
||||
|
||||
// after reading *everything*, we should have exactly one byte left in our LimitedReader (anything else is an error)
|
||||
if limited.N < 1 {
|
||||
return fmt.Errorf("size of %q is bigger than it should be (%d)", obj.Desc.Digest.String(), obj.Desc.Size)
|
||||
} else if limited.N > 1 {
|
||||
return fmt.Errorf("size of %q is %d bytes smaller than it should be (%d)", obj.Desc.Digest.String(), limited.N-1, obj.Desc.Size)
|
||||
}
|
||||
|
||||
// and finally, let's verify our checksum
|
||||
if !verifier.Verified() {
|
||||
return fmt.Errorf("digest of %q not correct", obj.Desc.Digest.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func get[T any](ctx context.Context, obj ResolvedObject) (*T, error) {
|
||||
var ret T
|
||||
if err := obj.fetchJSON(ctx, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
// At returns a new object pointing to the given descriptor (still within the context of the same repository as the original resolved object)
|
||||
func (obj ResolvedObject) At(desc ocispec.Descriptor) *ResolvedObject {
|
||||
obj.Desc = desc
|
||||
return &obj
|
||||
}
|
||||
|
||||
// Index assumes the given object is an "index" or "manifest list" and fetches/returns the parsed index JSON
|
||||
func (obj ResolvedObject) Index(ctx context.Context) (*ocispec.Index, error) {
|
||||
if !obj.IsImageIndex() {
|
||||
return nil, fmt.Errorf("unknown media type: %q", obj.Desc.MediaType)
|
||||
}
|
||||
return get[ocispec.Index](ctx, obj)
|
||||
}
|
||||
|
||||
// Manifests returns a list of "content descriptors" that corresponds to either this object (if it is a single-image manifest) or all the manifests of the index/manifest list this object represents
|
||||
func (obj ResolvedObject) Manifests(ctx context.Context) ([]ocispec.Descriptor, error) {
|
||||
if obj.IsImageManifest() {
|
||||
return []ocispec.Descriptor{obj.Desc}, nil
|
||||
}
|
||||
index, err := obj.Index(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return index.Manifests, nil
|
||||
}
|
||||
|
||||
// Architectures returns a map of "bashbrew architecture" strings to a list of members of the object (as either a manifest or an index) which match the given "bashbrew architecture" (either in an explicit "platform" object or by reading all the way down into the image "config" object for the platform fields)
|
||||
func (obj ResolvedObject) Architectures(ctx context.Context) (map[string][]ResolvedObject, error) {
|
||||
manifests, err := obj.Manifests(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := map[string][]ResolvedObject{}
|
||||
for _, manifestDesc := range manifests {
|
||||
obj := obj.At(manifestDesc)
|
||||
|
||||
if obj.Desc.Platform == nil || obj.Desc.Platform.OS == "" || obj.Desc.Platform.Architecture == "" {
|
||||
manifest, err := obj.Manifest(ctx)
|
||||
if err != nil {
|
||||
return nil, err // TODO should we really return this, or should we ignore it?
|
||||
}
|
||||
config, err := obj.At(manifest.Config).ConfigBlob(ctx)
|
||||
if err != nil {
|
||||
return nil, err // TODO should we really return this, or should we ignore it?
|
||||
}
|
||||
obj.Desc.Platform = &config.Platform
|
||||
}
|
||||
|
||||
objPlat := architecture.Normalize(*obj.Desc.Platform)
|
||||
obj.Desc.Platform = &objPlat
|
||||
|
||||
for arch, plat := range architecture.SupportedArches {
|
||||
if plat.Is(architecture.OCIPlatform(objPlat)) {
|
||||
ret[arch] = append(ret[arch], *obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Manifest assumes the given object is a (single-image) "manifest" (see [ResolvedObject.At]) and fetches/returns the parsed manifest JSON
|
||||
func (obj ResolvedObject) Manifest(ctx context.Context) (*ocispec.Manifest, error) {
|
||||
if !obj.IsImageManifest() {
|
||||
return nil, fmt.Errorf("unknown media type: %q", obj.Desc.MediaType)
|
||||
}
|
||||
return get[ocispec.Manifest](ctx, obj)
|
||||
}
|
||||
|
||||
// ConfigBlob assumes the given object is a "config" blob (see [ResolvedObject.At]) and fetches/returns the parsed config object
|
||||
func (obj ResolvedObject) ConfigBlob(ctx context.Context) (*ocispec.Image, error) {
|
||||
if obj.Desc.MediaType != "application/vnd.oci.image.config.v1+json" && obj.Desc.MediaType != "application/vnd.docker.container.image.v1+json" {
|
||||
return nil, fmt.Errorf("unknown media type: %q", obj.Desc.MediaType)
|
||||
}
|
||||
return get[ocispec.Image](ctx, obj)
|
||||
}
|
||||
|
||||
func (obj ResolvedObject) IsImageManifest() bool {
|
||||
return obj.Desc.MediaType == ocispec.MediaTypeImageManifest || obj.Desc.MediaType == images.MediaTypeDockerSchema2Manifest
|
||||
}
|
||||
|
||||
func (obj ResolvedObject) IsImageIndex() bool {
|
||||
return obj.Desc.MediaType == ocispec.MediaTypeImageIndex || obj.Desc.MediaType == images.MediaTypeDockerSchema2ManifestList
|
||||
}
|
||||
|
||||
// Resolve returns an object which can be used to query a registry for manifest objects or certain blobs with type checking helpers
|
||||
func Resolve(ctx context.Context, image string) (*ResolvedObject, error) {
|
||||
var (
|
||||
obj = ResolvedObject{
|
||||
ImageRef: image,
|
||||
}
|
||||
err error
|
||||
)
|
||||
|
||||
obj.ImageRef, obj.resolver, err = resolverHelper(obj.ImageRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj.ImageRef, obj.Desc, err = obj.resolver.Resolve(ctx, obj.ImageRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj.fetcher, err = obj.resolver.Fetcher(ctx, obj.ImageRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
func resolverHelper(image string) (string, remotes.Resolver, error) {
|
||||
ref, err := docker.ParseAnyReference(image)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if namedRef, ok := ref.(docker.Named); ok {
|
||||
// add ":latest" if necessary
|
||||
namedRef = docker.TagNameOnly(namedRef)
|
||||
ref = namedRef
|
||||
}
|
||||
return ref.String(), dockerremote.NewResolver(dockerremote.ResolverOptions{
|
||||
// TODO port this to "Hosts:" (especially so we can return Scheme correctly) but requires reimplementing some of https://github.com/containerd/containerd/blob/v1.6.9/remotes/docker/resolver.go#L161-L184 😞
|
||||
Host: func(host string) (string, error) {
|
||||
if host == "docker.io" {
|
||||
if publicProxy := os.Getenv("DOCKERHUB_PUBLIC_PROXY"); publicProxy != "" {
|
||||
if publicProxyURL, err := url.Parse(publicProxy); err == nil {
|
||||
// TODO Scheme (also not sure if "host:port" will be satisfactory to containerd here, but 🤷)
|
||||
return publicProxyURL.Host, nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "registry-1.docker.io", nil // https://github.com/containerd/containerd/blob/1c90a442489720eec95342e1789ee8a5e1b9536f/remotes/docker/registry.go#L193
|
||||
}
|
||||
return host, nil
|
||||
},
|
||||
}), nil
|
||||
}
|
||||
Loading…
Reference in New Issue