diff --git a/blobs.go b/blobs.go index b7583cc7..eac8bc93 100644 --- a/blobs.go +++ b/blobs.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/go.mod b/go.mod index 57cf8509..05f2a351 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 github.com/aws/aws-sdk-go v1.44.325 github.com/bshuster-repo/logrus-logstash-hook v1.0.0 + github.com/distribution/reference v0.5.0 github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c github.com/docker/go-metrics v0.0.1 github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 diff --git a/go.sum b/go.sum index 0a8694e2..54d3626f 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,8 @@ 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= diff --git a/notifications/bridge.go b/notifications/bridge.go index ff5a7980..7404b021 100644 --- a/notifications/bridge.go +++ b/notifications/bridge.go @@ -6,8 +6,8 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/uuid" + "github.com/distribution/reference" events "github.com/docker/go-events" "github.com/opencontainers/go-digest" ) diff --git a/notifications/bridge_test.go b/notifications/bridge_test.go index e147fccb..4c3a4638 100644 --- a/notifications/bridge_test.go +++ b/notifications/bridge_test.go @@ -6,9 +6,9 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/uuid" + "github.com/distribution/reference" events "github.com/docker/go-events" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" diff --git a/notifications/listener.go b/notifications/listener.go index 57857e18..fad29341 100644 --- a/notifications/listener.go +++ b/notifications/listener.go @@ -8,7 +8,7 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/notifications/listener_test.go b/notifications/listener_test.go index 8d0c18a4..e33109be 100644 --- a/notifications/listener_test.go +++ b/notifications/listener_test.go @@ -8,11 +8,11 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/reference/fuzz_test.go b/reference/fuzz_test.go deleted file mode 100644 index c64c54e2..00000000 --- a/reference/fuzz_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package reference - -import ( - "testing" -) - -// fuzzParseNormalizedNamed implements a fuzzer -// that targets ParseNormalizedNamed -// nolint:deadcode -func FuzzParseNormalizedNamed(f *testing.F) { - f.Fuzz(func(t *testing.T, data string) { - _, _ = ParseNormalizedNamed(data) - }) -} diff --git a/reference/helpers_deprecated.go b/reference/helpers_deprecated.go new file mode 100644 index 00000000..cbd11925 --- /dev/null +++ b/reference/helpers_deprecated.go @@ -0,0 +1,34 @@ +package reference + +import "github.com/distribution/reference" + +// IsNameOnly returns true if reference only contains a repo name. +// +// Deprecated: use [reference.IsNameOnly]. +func IsNameOnly(ref reference.Named) bool { + return reference.IsNameOnly(ref) +} + +// FamiliarName returns the familiar name string +// for the given named, familiarizing if needed. +// +// Deprecated: use [reference.FamiliarName]. +func FamiliarName(ref reference.Named) string { + return reference.FamiliarName(ref) +} + +// FamiliarString returns the familiar string representation +// for the given reference, familiarizing if needed. +// +// Deprecated: use [reference.FamiliarString]. +func FamiliarString(ref reference.Reference) string { + return reference.FamiliarString(ref) +} + +// FamiliarMatch reports whether ref matches the specified pattern. +// See [path.Match] for supported patterns. +// +// Deprecated: use [reference.FamiliarMatch]. +func FamiliarMatch(pattern string, ref reference.Reference) (bool, error) { + return reference.FamiliarMatch(pattern, ref) +} diff --git a/reference/normalize_deprecated.go b/reference/normalize_deprecated.go new file mode 100644 index 00000000..ad2d8269 --- /dev/null +++ b/reference/normalize_deprecated.go @@ -0,0 +1,39 @@ +package reference + +import ( + "github.com/distribution/reference" +) + +// ParseNormalizedNamed parses a string into a named reference +// transforming a familiar name from Docker UI to a fully +// qualified reference. If the value may be an identifier +// use ParseAnyReference. +// +// Deprecated: use [reference.ParseNormalizedNamed]. +func ParseNormalizedNamed(s string) (reference.Named, error) { + return reference.ParseNormalizedNamed(s) +} + +// ParseDockerRef normalizes the image reference following the docker convention, +// which allows for references to contain both a tag and a digest. +// +// Deprecated: use [reference.ParseDockerRef]. +func ParseDockerRef(ref string) (reference.Named, error) { + return reference.ParseDockerRef(ref) +} + +// TagNameOnly adds the default tag "latest" to a reference if it only has +// a repo name. +// +// Deprecated: use [reference.TagNameOnly]. +func TagNameOnly(ref reference.Named) reference.Named { + return reference.TagNameOnly(ref) +} + +// ParseAnyReference parses a reference string as a possible identifier, +// full digest, or familiar name. +// +// Deprecated: use [reference.ParseAnyReference]. +func ParseAnyReference(ref string) (reference.Reference, error) { + return reference.ParseAnyReference(ref) +} diff --git a/reference/normalize_test.go b/reference/normalize_test.go deleted file mode 100644 index a973927b..00000000 --- a/reference/normalize_test.go +++ /dev/null @@ -1,711 +0,0 @@ -package reference - -import ( - "strconv" - "testing" - - "github.com/opencontainers/go-digest" -) - -func TestValidateReferenceName(t *testing.T) { - t.Parallel() - validRepoNames := []string{ - "docker/docker", - "library/debian", - "debian", - "docker.io/docker/docker", - "docker.io/library/debian", - "docker.io/debian", - "index.docker.io/docker/docker", - "index.docker.io/library/debian", - "index.docker.io/debian", - "127.0.0.1:5000/docker/docker", - "127.0.0.1:5000/library/debian", - "127.0.0.1:5000/debian", - "192.168.0.1", - "192.168.0.1:80", - "192.168.0.1:8/debian", - "192.168.0.2:25000/debian", - "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", - "[fc00::1]:5000/docker", - "[fc00::1]:5000/docker/docker", - "[fc00:1:2:3:4:5:6:7]:5000/library/debian", - - // This test case was moved from invalid to valid since it is valid input - // when specified with a hostname, it removes the ambiguity from about - // whether the value is an identifier or repository name - "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - "Docker/docker", - "DOCKER/docker", - } - invalidRepoNames := []string{ - "https://github.com/docker/docker", - "docker/Docker", - "-docker", - "-docker/docker", - "-docker.io/docker/docker", - "docker///docker", - "docker.io/docker/Docker", - "docker.io/docker///docker", - "[fc00::1]", - "[fc00::1]:5000", - "fc00::1:5000/debian", - "[fe80::1%eth0]:5000/debian", - "[2001:db8:3:4::192.0.2.33]:5000/debian", - "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - } - - for _, name := range invalidRepoNames { - _, err := ParseNormalizedNamed(name) - if err == nil { - t.Fatalf("Expected invalid repo name for %q", name) - } - } - - for _, name := range validRepoNames { - _, err := ParseNormalizedNamed(name) - if err != nil { - t.Fatalf("Error parsing repo name %s, got: %q", name, err) - } - } -} - -func TestValidateRemoteName(t *testing.T) { - t.Parallel() - validRepositoryNames := []string{ - // Sanity check. - "docker/docker", - - // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden). - "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", - - // Allow embedded hyphens. - "docker-rules/docker", - - // Allow multiple hyphens as well. - "docker---rules/docker", - - // Username doc and image name docker being tested. - "doc/docker", - - // single character names are now allowed. - "d/docker", - "jess/t", - - // Consecutive underscores. - "dock__er/docker", - } - for _, repositoryName := range validRepositoryNames { - _, err := ParseNormalizedNamed(repositoryName) - if err != nil { - t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err) - } - } - - invalidRepositoryNames := []string{ - // Disallow capital letters. - "docker/Docker", - - // Only allow one slash. - "docker///docker", - - // Disallow 64-character hexadecimal. - "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", - - // Disallow leading and trailing hyphens in namespace. - "-docker/docker", - "docker-/docker", - "-docker-/docker", - - // Don't allow underscores everywhere (as opposed to hyphens). - "____/____", - - "_docker/_docker", - - // Disallow consecutive periods. - "dock..er/docker", - "dock_.er/docker", - "dock-.er/docker", - - // No repository. - "docker/", - - // namespace too long - "this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker", - } - for _, repositoryName := range invalidRepositoryNames { - if _, err := ParseNormalizedNamed(repositoryName); err == nil { - t.Errorf("Repository name should be invalid: %v", repositoryName) - } - } -} - -func TestParseRepositoryInfo(t *testing.T) { - t.Parallel() - type tcase struct { - RemoteName, FamiliarName, FullName, AmbiguousName, Domain string - } - - tests := []tcase{ - { - RemoteName: "fooo/bar", - FamiliarName: "fooo/bar", - FullName: "docker.io/fooo/bar", - AmbiguousName: "index.docker.io/fooo/bar", - Domain: "docker.io", - }, - { - RemoteName: "library/ubuntu", - FamiliarName: "ubuntu", - FullName: "docker.io/library/ubuntu", - AmbiguousName: "library/ubuntu", - Domain: "docker.io", - }, - { - RemoteName: "nonlibrary/ubuntu", - FamiliarName: "nonlibrary/ubuntu", - FullName: "docker.io/nonlibrary/ubuntu", - AmbiguousName: "", - Domain: "docker.io", - }, - { - RemoteName: "other/library", - FamiliarName: "other/library", - FullName: "docker.io/other/library", - AmbiguousName: "", - Domain: "docker.io", - }, - { - RemoteName: "private/moonbase", - FamiliarName: "127.0.0.1:8000/private/moonbase", - FullName: "127.0.0.1:8000/private/moonbase", - AmbiguousName: "", - Domain: "127.0.0.1:8000", - }, - { - RemoteName: "privatebase", - FamiliarName: "127.0.0.1:8000/privatebase", - FullName: "127.0.0.1:8000/privatebase", - AmbiguousName: "", - Domain: "127.0.0.1:8000", - }, - { - RemoteName: "private/moonbase", - FamiliarName: "example.com/private/moonbase", - FullName: "example.com/private/moonbase", - AmbiguousName: "", - Domain: "example.com", - }, - { - RemoteName: "privatebase", - FamiliarName: "example.com/privatebase", - FullName: "example.com/privatebase", - AmbiguousName: "", - Domain: "example.com", - }, - { - RemoteName: "private/moonbase", - FamiliarName: "example.com:8000/private/moonbase", - FullName: "example.com:8000/private/moonbase", - AmbiguousName: "", - Domain: "example.com:8000", - }, - { - RemoteName: "privatebasee", - FamiliarName: "example.com:8000/privatebasee", - FullName: "example.com:8000/privatebasee", - AmbiguousName: "", - Domain: "example.com:8000", - }, - { - RemoteName: "library/ubuntu-12.04-base", - FamiliarName: "ubuntu-12.04-base", - FullName: "docker.io/library/ubuntu-12.04-base", - AmbiguousName: "index.docker.io/library/ubuntu-12.04-base", - Domain: "docker.io", - }, - { - RemoteName: "library/foo", - FamiliarName: "foo", - FullName: "docker.io/library/foo", - AmbiguousName: "docker.io/foo", - Domain: "docker.io", - }, - { - RemoteName: "library/foo/bar", - FamiliarName: "library/foo/bar", - FullName: "docker.io/library/foo/bar", - AmbiguousName: "", - Domain: "docker.io", - }, - { - RemoteName: "store/foo/bar", - FamiliarName: "store/foo/bar", - FullName: "docker.io/store/foo/bar", - AmbiguousName: "", - Domain: "docker.io", - }, - { - RemoteName: "bar", - FamiliarName: "Foo/bar", - FullName: "Foo/bar", - AmbiguousName: "", - Domain: "Foo", - }, - { - RemoteName: "bar", - FamiliarName: "FOO/bar", - FullName: "FOO/bar", - AmbiguousName: "", - Domain: "FOO", - }, - } - - for i, tc := range tests { - tc := tc - refStrings := []string{tc.FamiliarName, tc.FullName} - if tc.AmbiguousName != "" { - refStrings = append(refStrings, tc.AmbiguousName) - } - - for _, r := range refStrings { - r := r - t.Run(strconv.Itoa(i)+"/"+r, func(t *testing.T) { - t.Parallel() - named, err := ParseNormalizedNamed(r) - if err != nil { - t.Fatalf("ref=%s: %v", r, err) - } - t.Run("FamiliarName", func(t *testing.T) { - if expected, actual := tc.FamiliarName, FamiliarName(named); expected != actual { - t.Errorf("Invalid familiar name for %q. Expected %q, got %q", named, expected, actual) - } - }) - t.Run("FullName", func(t *testing.T) { - if expected, actual := tc.FullName, named.String(); expected != actual { - t.Errorf("Invalid canonical reference for %q. Expected %q, got %q", named, expected, actual) - } - }) - t.Run("Domain", func(t *testing.T) { - if expected, actual := tc.Domain, Domain(named); expected != actual { - t.Errorf("Invalid domain for %q. Expected %q, got %q", named, expected, actual) - } - }) - t.Run("RemoteName", func(t *testing.T) { - if expected, actual := tc.RemoteName, Path(named); expected != actual { - t.Errorf("Invalid remoteName for %q. Expected %q, got %q", named, expected, actual) - } - }) - }) - } - } -} - -func TestParseReferenceWithTagAndDigest(t *testing.T) { - t.Parallel() - shortRef := "busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa" - ref, err := ParseNormalizedNamed(shortRef) - if err != nil { - t.Fatal(err) - } - if expected, actual := "docker.io/library/"+shortRef, ref.String(); actual != expected { - t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual) - } - - if _, isTagged := ref.(NamedTagged); !isTagged { - t.Fatalf("Reference from %q should support tag", ref) - } - if _, isCanonical := ref.(Canonical); !isCanonical { - t.Fatalf("Reference from %q should support digest", ref) - } - if expected, actual := shortRef, FamiliarString(ref); actual != expected { - t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual) - } -} - -func TestInvalidReferenceComponents(t *testing.T) { - t.Parallel() - if _, err := ParseNormalizedNamed("-foo"); err == nil { - t.Fatal("Expected WithName to detect invalid name") - } - ref, err := ParseNormalizedNamed("busybox") - if err != nil { - t.Fatal(err) - } - if _, err := WithTag(ref, "-foo"); err == nil { - t.Fatal("Expected WithName to detect invalid tag") - } - if _, err := WithDigest(ref, digest.Digest("foo")); err == nil { - t.Fatal("Expected WithDigest to detect invalid digest") - } -} - -func equalReference(r1, r2 Reference) bool { - switch v1 := r1.(type) { - case digestReference: - if v2, ok := r2.(digestReference); ok { - return v1 == v2 - } - case repository: - if v2, ok := r2.(repository); ok { - return v1 == v2 - } - case taggedReference: - if v2, ok := r2.(taggedReference); ok { - return v1 == v2 - } - case canonicalReference: - if v2, ok := r2.(canonicalReference); ok { - return v1 == v2 - } - case reference: - if v2, ok := r2.(reference); ok { - return v1 == v2 - } - } - return false -} - -func TestParseAnyReference(t *testing.T) { - t.Parallel() - tests := []struct { - Reference string - Equivalent string - Expected Reference - }{ - { - Reference: "redis", - Equivalent: "docker.io/library/redis", - }, - { - Reference: "redis:latest", - Equivalent: "docker.io/library/redis:latest", - }, - { - Reference: "docker.io/library/redis:latest", - Equivalent: "docker.io/library/redis:latest", - }, - { - Reference: "redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "dmcgowan/myapp", - Equivalent: "docker.io/dmcgowan/myapp", - }, - { - Reference: "dmcgowan/myapp:latest", - Equivalent: "docker.io/dmcgowan/myapp:latest", - }, - { - Reference: "docker.io/mcgowan/myapp:latest", - Equivalent: "docker.io/mcgowan/myapp:latest", - }, - { - Reference: "dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"), - Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"), - Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c", - }, - { - Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9", - Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9", - }, - { - Reference: "dbcc1", - Equivalent: "docker.io/library/dbcc1", - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.Reference, func(t *testing.T) { - t.Parallel() - var ref Reference - var err error - ref, err = ParseAnyReference(tc.Reference) - if err != nil { - t.Fatalf("Error parsing reference %s: %v", tc.Reference, err) - } - if ref.String() != tc.Equivalent { - t.Fatalf("Unexpected string: %s, expected %s", ref.String(), tc.Equivalent) - } - - expected := tc.Expected - if expected == nil { - expected, err = Parse(tc.Equivalent) - if err != nil { - t.Fatalf("Error parsing reference %s: %v", tc.Equivalent, err) - } - } - if !equalReference(ref, expected) { - t.Errorf("Unexpected reference %#v, expected %#v", ref, expected) - } - }) - } -} - -func TestNormalizedSplitHostname(t *testing.T) { - t.Parallel() - tests := []struct { - input string - domain string - name string - }{ - { - input: "test.com/foo", - domain: "test.com", - name: "foo", - }, - { - input: "test_com/foo", - domain: "docker.io", - name: "test_com/foo", - }, - { - input: "docker/migrator", - domain: "docker.io", - name: "docker/migrator", - }, - { - input: "test.com:8080/foo", - domain: "test.com:8080", - name: "foo", - }, - { - input: "test-com:8080/foo", - domain: "test-com:8080", - name: "foo", - }, - { - input: "foo", - domain: "docker.io", - name: "library/foo", - }, - { - input: "xn--n3h.com/foo", - domain: "xn--n3h.com", - name: "foo", - }, - { - input: "xn--n3h.com:18080/foo", - domain: "xn--n3h.com:18080", - name: "foo", - }, - { - input: "docker.io/foo", - domain: "docker.io", - name: "library/foo", - }, - { - input: "docker.io/library/foo", - domain: "docker.io", - name: "library/foo", - }, - { - input: "docker.io/library/foo/bar", - domain: "docker.io", - name: "library/foo/bar", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - named, err := ParseNormalizedNamed(tc.input) - if err != nil { - t.Errorf("error parsing name: %s", err) - } - domain, name := SplitHostname(named) //nolint:staticcheck // Ignore SA1019: SplitHostname is deprecated. - if domain != tc.domain { - t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain) - } - if name != tc.name { - t.Errorf("unexpected name: got %q, expected %q", name, tc.name) - } - }) - } -} - -func TestMatchError(t *testing.T) { - t.Parallel() - named, err := ParseAnyReference("foo") - if err != nil { - t.Fatal(err) - } - _, err = FamiliarMatch("[-x]", named) - if err == nil { - t.Fatalf("expected an error, got nothing") - } -} - -func TestMatch(t *testing.T) { - t.Parallel() - tests := []struct { - reference string - pattern string - expected bool - }{ - { - reference: "foo", - pattern: "foo/**/ba[rz]", - expected: false, - }, - { - reference: "foo/any/bat", - pattern: "foo/**/ba[rz]", - expected: false, - }, - { - reference: "foo/a/bar", - pattern: "foo/**/ba[rz]", - expected: true, - }, - { - reference: "foo/b/baz", - pattern: "foo/**/ba[rz]", - expected: true, - }, - { - reference: "foo/c/baz:tag", - pattern: "foo/**/ba[rz]", - expected: true, - }, - { - reference: "foo/c/baz:tag", - pattern: "foo/*/baz:tag", - expected: true, - }, - { - reference: "foo/c/baz:tag", - pattern: "foo/c/baz:tag", - expected: true, - }, - { - reference: "example.com/foo/c/baz:tag", - pattern: "*/foo/c/baz", - expected: true, - }, - { - reference: "example.com/foo/c/baz:tag", - pattern: "example.com/foo/c/baz", - expected: true, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.reference, func(t *testing.T) { - t.Parallel() - named, err := ParseAnyReference(tc.reference) - if err != nil { - t.Fatal(err) - } - actual, err := FamiliarMatch(tc.pattern, named) - if err != nil { - t.Fatal(err) - } - if actual != tc.expected { - t.Fatalf("expected %s match %s to be %v, was %v", tc.reference, tc.pattern, tc.expected, actual) - } - }) - } -} - -func TestParseDockerRef(t *testing.T) { - t.Parallel() - tests := []struct { - name string - input string - expected string - }{ - { - name: "nothing", - input: "busybox", - expected: "docker.io/library/busybox:latest", - }, - { - name: "tag only", - input: "busybox:latest", - expected: "docker.io/library/busybox:latest", - }, - { - name: "digest only", - input: "busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", - expected: "docker.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", - }, - { - name: "path only", - input: "library/busybox", - expected: "docker.io/library/busybox:latest", - }, - { - name: "hostname only", - input: "docker.io/busybox", - expected: "docker.io/library/busybox:latest", - }, - { - name: "no tag", - input: "docker.io/library/busybox", - expected: "docker.io/library/busybox:latest", - }, - { - name: "no path", - input: "docker.io/busybox:latest", - expected: "docker.io/library/busybox:latest", - }, - { - name: "no hostname", - input: "library/busybox:latest", - expected: "docker.io/library/busybox:latest", - }, - { - name: "full reference with tag", - input: "docker.io/library/busybox:latest", - expected: "docker.io/library/busybox:latest", - }, - { - name: "gcr reference without tag", - input: "gcr.io/library/busybox", - expected: "gcr.io/library/busybox:latest", - }, - { - name: "both tag and digest", - input: "gcr.io/library/busybox:latest@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", - expected: "gcr.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - normalized, err := ParseDockerRef(tc.input) - if err != nil { - t.Fatal(err) - } - output := normalized.String() - if output != tc.expected { - t.Fatalf("expected %q to be parsed as %v, got %v", tc.input, tc.expected, output) - } - _, err = Parse(output) - if err != nil { - t.Fatalf("%q should be a valid reference, but got an error: %v", output, err) - } - }) - } -} diff --git a/reference/reference_deprecated.go b/reference/reference_deprecated.go new file mode 100644 index 00000000..5b732498 --- /dev/null +++ b/reference/reference_deprecated.go @@ -0,0 +1,172 @@ +// Package reference is deprecated, and has moved to github.com/distribution/reference. +// +// Deprecated: use github.com/distribution/reference instead. +package reference + +import ( + "github.com/distribution/reference" + "github.com/opencontainers/go-digest" +) + +const ( + // NameTotalLengthMax is the maximum total number of characters in a repository name. + // + // Deprecated: use [reference.NameTotalLengthMax]. + NameTotalLengthMax = reference.NameTotalLengthMax +) + +var ( + // ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference. + // + // Deprecated: use [reference.ErrReferenceInvalidFormat]. + ErrReferenceInvalidFormat = reference.ErrReferenceInvalidFormat + + // ErrTagInvalidFormat represents an error while trying to parse a string as a tag. + // + // Deprecated: use [reference.ErrTagInvalidFormat]. + ErrTagInvalidFormat = reference.ErrTagInvalidFormat + + // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. + // + // Deprecated: use [reference.ErrDigestInvalidFormat]. + ErrDigestInvalidFormat = reference.ErrDigestInvalidFormat + + // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. + // + // Deprecated: use [reference.ErrNameContainsUppercase]. + ErrNameContainsUppercase = reference.ErrNameContainsUppercase + + // ErrNameEmpty is returned for empty, invalid repository names. + // + // Deprecated: use [reference.ErrNameEmpty]. + ErrNameEmpty = reference.ErrNameEmpty + + // ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. + // + // Deprecated: use [reference.ErrNameTooLong]. + ErrNameTooLong = reference.ErrNameTooLong + + // ErrNameNotCanonical is returned when a name is not canonical. + // + // Deprecated: use [reference.ErrNameNotCanonical]. + ErrNameNotCanonical = reference.ErrNameNotCanonical +) + +// Reference is an opaque object reference identifier that may include +// modifiers such as a hostname, name, tag, and digest. +// +// Deprecated: use [reference.Reference]. +type Reference = reference.Reference + +// Field provides a wrapper type for resolving correct reference types when +// working with encoding. +// +// Deprecated: use [reference.Field]. +type Field = reference.Field + +// AsField wraps a reference in a Field for encoding. +// +// Deprecated: use [reference.AsField]. +func AsField(ref reference.Reference) reference.Field { + return reference.AsField(ref) +} + +// Named is an object with a full name +// +// Deprecated: use [reference.Named]. +type Named = reference.Named + +// Tagged is an object which has a tag +// +// Deprecated: use [reference.Tagged]. +type Tagged = reference.Tagged + +// NamedTagged is an object including a name and tag. +// +// Deprecated: use [reference.NamedTagged]. +type NamedTagged reference.NamedTagged + +// Digested is an object which has a digest +// in which it can be referenced by +// +// Deprecated: use [reference.Digested]. +type Digested reference.Digested + +// Canonical reference is an object with a fully unique +// name including a name with domain and digest +// +// Deprecated: use [reference.Canonical]. +type Canonical reference.Canonical + +// Domain returns the domain part of the [Named] reference. +// +// Deprecated: use [reference.Domain]. +func Domain(named reference.Named) string { + return reference.Domain(named) +} + +// Path returns the name without the domain part of the [Named] reference. +// +// Deprecated: use [reference.Path]. +func Path(named reference.Named) (name string) { + return reference.Path(named) +} + +// SplitHostname splits a named reference into a +// hostname and name string. If no valid hostname is +// found, the hostname is empty and the full value +// is returned as name +// +// Deprecated: Use [reference.Domain] or [reference.Path]. +func SplitHostname(named reference.Named) (string, string) { + return reference.SplitHostname(named) +} + +// Parse parses s and returns a syntactically valid Reference. +// If an error was encountered it is returned, along with a nil Reference. +// +// Deprecated: use [reference.Parse]. +func Parse(s string) (reference.Reference, error) { + return reference.Parse(s) +} + +// ParseNamed parses s and returns a syntactically valid reference implementing +// the Named interface. The reference must have a name and be in the canonical +// form, otherwise an error is returned. +// If an error was encountered it is returned, along with a nil Reference. +// +// Deprecated: use [reference.ParseNamed]. +func ParseNamed(s string) (reference.Named, error) { + return reference.ParseNamed(s) +} + +// WithName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +// +// Deprecated: use [reference.WithName]. +func WithName(name string) (reference.Named, error) { + return reference.WithName(name) +} + +// WithTag combines the name from "name" and the tag from "tag" to form a +// reference incorporating both the name and the tag. +// +// Deprecated: use [reference.WithTag]. +func WithTag(name reference.Named, tag string) (reference.NamedTagged, error) { + return reference.WithTag(name, tag) +} + +// WithDigest combines the name from "name" and the digest from "digest" to form +// a reference incorporating both the name and the digest. +// +// Deprecated: use [reference.WithDigest]. +func WithDigest(name reference.Named, digest digest.Digest) (reference.Canonical, error) { + return reference.WithDigest(name, digest) +} + +// TrimNamed removes any tag or digest from the named reference. +// +// Deprecated: use [reference.TrimNamed]. +func TrimNamed(ref reference.Named) reference.Named { + return reference.TrimNamed(ref) +} diff --git a/reference/reference_test.go b/reference/reference_test.go deleted file mode 100644 index d5a897c7..00000000 --- a/reference/reference_test.go +++ /dev/null @@ -1,751 +0,0 @@ -package reference - -import ( - _ "crypto/sha256" - _ "crypto/sha512" - "encoding/json" - "strings" - "testing" - - "github.com/opencontainers/go-digest" -) - -func TestReferenceParse(t *testing.T) { - t.Parallel() - // tests is a unified set of testcases for - // testing the parsing of references - tests := []struct { - // input is the repository name or name component testcase - input string - // err is the error expected from Parse, or nil - err error - // repository is the string representation for the reference - repository string - // domain is the domain expected in the reference - domain string - // tag is the tag for the reference - tag string - // digest is the digest for the reference (enforces digest reference) - digest string - }{ - { - input: "test_com", - repository: "test_com", - }, - { - input: "test.com:tag", - repository: "test.com", - tag: "tag", - }, - { - input: "test.com:5000", - repository: "test.com", - tag: "5000", - }, - { - input: "test.com/repo:tag", - domain: "test.com", - repository: "test.com/repo", - tag: "tag", - }, - { - input: "test:5000/repo", - domain: "test:5000", - repository: "test:5000/repo", - }, - { - input: "test:5000/repo:tag", - domain: "test:5000", - repository: "test:5000/repo", - tag: "tag", - }, - { - input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - domain: "test:5000", - repository: "test:5000/repo", - digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - }, - { - input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - domain: "test:5000", - repository: "test:5000/repo", - tag: "tag", - digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - }, - { - input: "test:5000/repo", - domain: "test:5000", - repository: "test:5000/repo", - }, - { - input: "", - err: ErrNameEmpty, - }, - { - input: ":justtag", - err: ErrReferenceInvalidFormat, - }, - { - input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - err: ErrReferenceInvalidFormat, - }, - { - input: "repo@sha256:ffffffffffffffffffffffffffffffffff", - err: digest.ErrDigestInvalidLength, - }, - { - input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - err: digest.ErrDigestUnsupported, - }, - { - input: "Uppercase:tag", - err: ErrNameContainsUppercase, - }, - // FIXME "Uppercase" is incorrectly handled as a domain-name here, therefore passes. - // See https://github.com/distribution/distribution/pull/1778, and https://github.com/docker/docker/pull/20175 - // { - // input: "Uppercase/lowercase:tag", - // err: ErrNameContainsUppercase, - // }, - { - input: "test:5000/Uppercase/lowercase:tag", - err: ErrNameContainsUppercase, - }, - { - input: "lowercase:Uppercase", - repository: "lowercase", - tag: "Uppercase", - }, - { - input: strings.Repeat("a/", 128) + "a:tag", - err: ErrNameTooLong, - }, - { - input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max", - domain: "a", - repository: strings.Repeat("a/", 127) + "a", - tag: "tag-puts-this-over-max", - }, - { - input: "aa/asdf$$^/aa", - err: ErrReferenceInvalidFormat, - }, - { - input: "sub-dom1.foo.com/bar/baz/quux", - domain: "sub-dom1.foo.com", - repository: "sub-dom1.foo.com/bar/baz/quux", - }, - { - input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag", - domain: "sub-dom1.foo.com", - repository: "sub-dom1.foo.com/bar/baz/quux", - tag: "some-long-tag", - }, - { - input: "b.gcr.io/test.example.com/my-app:test.example.com", - domain: "b.gcr.io", - repository: "b.gcr.io/test.example.com/my-app", - tag: "test.example.com", - }, - { - input: "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode - domain: "xn--n3h.com", - repository: "xn--n3h.com/myimage", - tag: "xn--n3h.com", - }, - { - input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode - domain: "xn--7o8h.com", - repository: "xn--7o8h.com/myimage", - tag: "xn--7o8h.com", - digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - }, - { - input: "foo_bar.com:8080", - repository: "foo_bar.com", - tag: "8080", - }, - { - input: "foo/foo_bar.com:8080", - domain: "foo", - repository: "foo/foo_bar.com", - tag: "8080", - }, - { - input: "192.168.1.1", - repository: "192.168.1.1", - }, - { - input: "192.168.1.1:tag", - repository: "192.168.1.1", - tag: "tag", - }, - { - input: "192.168.1.1:5000", - repository: "192.168.1.1", - tag: "5000", - }, - { - input: "192.168.1.1/repo", - domain: "192.168.1.1", - repository: "192.168.1.1/repo", - }, - { - input: "192.168.1.1:5000/repo", - domain: "192.168.1.1:5000", - repository: "192.168.1.1:5000/repo", - }, - { - input: "192.168.1.1:5000/repo:5050", - domain: "192.168.1.1:5000", - repository: "192.168.1.1:5000/repo", - tag: "5050", - }, - { - input: "[2001:db8::1]", - err: ErrReferenceInvalidFormat, - }, - { - input: "[2001:db8::1]:5000", - err: ErrReferenceInvalidFormat, - }, - { - input: "[2001:db8::1]:tag", - err: ErrReferenceInvalidFormat, - }, - { - input: "[2001:db8::1]/repo", - domain: "[2001:db8::1]", - repository: "[2001:db8::1]/repo", - }, - { - input: "[2001:db8:1:2:3:4:5:6]/repo:tag", - domain: "[2001:db8:1:2:3:4:5:6]", - repository: "[2001:db8:1:2:3:4:5:6]/repo", - tag: "tag", - }, - { - input: "[2001:db8::1]:5000/repo", - domain: "[2001:db8::1]:5000", - repository: "[2001:db8::1]:5000/repo", - }, - { - input: "[2001:db8::1]:5000/repo:tag", - domain: "[2001:db8::1]:5000", - repository: "[2001:db8::1]:5000/repo", - tag: "tag", - }, - { - input: "[2001:db8::1]:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - domain: "[2001:db8::1]:5000", - repository: "[2001:db8::1]:5000/repo", - digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - }, - { - input: "[2001:db8::1]:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - domain: "[2001:db8::1]:5000", - repository: "[2001:db8::1]:5000/repo", - tag: "tag", - digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - }, - { - input: "[2001:db8::]:5000/repo", - domain: "[2001:db8::]:5000", - repository: "[2001:db8::]:5000/repo", - }, - { - input: "[::1]:5000/repo", - domain: "[::1]:5000", - repository: "[::1]:5000/repo", - }, - { - input: "[fe80::1%eth0]:5000/repo", - err: ErrReferenceInvalidFormat, - }, - { - input: "[fe80::1%@invalidzone]:5000/repo", - err: ErrReferenceInvalidFormat, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - repo, err := Parse(tc.input) - if tc.err != nil { - if err == nil { - t.Errorf("missing expected error: %v", tc.err) - } else if tc.err != err { - t.Errorf("mismatched error: got %v, expected %v", err, tc.err) - } - return - } else if err != nil { - t.Errorf("unexpected parse error: %v", err) - return - } - if repo.String() != tc.input { - t.Errorf("mismatched repo: got %q, expected %q", repo.String(), tc.input) - } - - if named, ok := repo.(Named); ok { - if named.Name() != tc.repository { - t.Errorf("unexpected repository: got %q, expected %q", named.Name(), tc.repository) - } - domain, _ := SplitHostname(named) - if domain != tc.domain { - t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain) - } - } else if tc.repository != "" || tc.domain != "" { - t.Errorf("expected named type, got %T", repo) - } - - tagged, ok := repo.(Tagged) - if tc.tag != "" { - if ok { - if tagged.Tag() != tc.tag { - t.Errorf("unexpected tag: got %q, expected %q", tagged.Tag(), tc.tag) - } - } else { - t.Errorf("expected tagged type, got %T", repo) - } - } else if ok { - t.Errorf("unexpected tagged type") - } - - digested, ok := repo.(Digested) - if tc.digest != "" { - if ok { - if digested.Digest().String() != tc.digest { - t.Errorf("unexpected digest: got %q, expected %q", digested.Digest().String(), tc.digest) - } - } else { - t.Errorf("expected digested type, got %T", repo) - } - } else if ok { - t.Errorf("unexpected digested type") - } - }) - } -} - -// TestWithNameFailure tests cases where WithName should fail. Cases where it -// should succeed are covered by TestSplitHostname, below. -func TestWithNameFailure(t *testing.T) { - t.Parallel() - tests := []struct { - input string - err error - }{ - { - input: "", - err: ErrNameEmpty, - }, - { - input: ":justtag", - err: ErrReferenceInvalidFormat, - }, - { - input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - err: ErrReferenceInvalidFormat, - }, - { - input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - err: ErrReferenceInvalidFormat, - }, - { - input: strings.Repeat("a/", 128) + "a:tag", - err: ErrNameTooLong, - }, - { - input: "aa/asdf$$^/aa", - err: ErrReferenceInvalidFormat, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - _, err := WithName(tc.input) - if err == nil { - t.Errorf("no error parsing name. expected: %s", tc.err) - } - }) - } -} - -func TestSplitHostname(t *testing.T) { - t.Parallel() - tests := []struct { - input string - domain string - name string - }{ - { - input: "test.com/foo", - domain: "test.com", - name: "foo", - }, - { - input: "test_com/foo", - domain: "", - name: "test_com/foo", - }, - { - input: "test:8080/foo", - domain: "test:8080", - name: "foo", - }, - { - input: "test.com:8080/foo", - domain: "test.com:8080", - name: "foo", - }, - { - input: "test-com:8080/foo", - domain: "test-com:8080", - name: "foo", - }, - { - input: "xn--n3h.com:18080/foo", - domain: "xn--n3h.com:18080", - name: "foo", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - named, err := WithName(tc.input) - if err != nil { - t.Errorf("error parsing name: %s", err) - } - domain, name := SplitHostname(named) - if domain != tc.domain { - t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain) - } - if name != tc.name { - t.Errorf("unexpected name: got %q, expected %q", name, tc.name) - } - }) - } -} - -type serializationType struct { - Description string - Field Field -} - -func TestSerialization(t *testing.T) { - t.Parallel() - tests := []struct { - description string - input string - name string - tag string - digest string - err error - }{ - { - description: "empty value", - err: ErrNameEmpty, - }, - { - description: "just a name", - input: "example.com:8000/named", - name: "example.com:8000/named", - }, - { - description: "name with a tag", - input: "example.com:8000/named:tagged", - name: "example.com:8000/named", - tag: "tagged", - }, - { - description: "name with digest", - input: "other.com/named@sha256:1234567890098765432112345667890098765432112345667890098765432112", - name: "other.com/named", - digest: "sha256:1234567890098765432112345667890098765432112345667890098765432112", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.description, func(t *testing.T) { - t.Parallel() - m := map[string]string{ - "Description": tc.description, - "Field": tc.input, - } - b, err := json.Marshal(m) - if err != nil { - t.Errorf("error marshalling: %v", err) - } - st := serializationType{} - - if err := json.Unmarshal(b, &st); err != nil { - if tc.err == nil { - t.Errorf("error unmarshalling: %v", err) - } - if err != tc.err { - t.Errorf("wrong error, expected %v, got %v", tc.err, err) - } - - return - } else if tc.err != nil { - t.Errorf("expected error unmarshalling: %v", tc.err) - } - - if st.Description != tc.description { - t.Errorf("wrong description, expected %q, got %q", tc.description, st.Description) - } - - ref := st.Field.Reference() - - if named, ok := ref.(Named); ok { - if named.Name() != tc.name { - t.Errorf("unexpected repository: got %q, expected %q", named.Name(), tc.name) - } - } else if tc.name != "" { - t.Errorf("expected named type, got %T", ref) - } - - tagged, ok := ref.(Tagged) - if tc.tag != "" { - if ok { - if tagged.Tag() != tc.tag { - t.Errorf("unexpected tag: got %q, expected %q", tagged.Tag(), tc.tag) - } - } else { - t.Errorf("expected tagged type, got %T", ref) - } - } else if ok { - t.Errorf("unexpected tagged type") - } - - digested, ok := ref.(Digested) - if tc.digest != "" { - if ok { - if digested.Digest().String() != tc.digest { - t.Errorf("unexpected digest: got %q, expected %q", digested.Digest().String(), tc.digest) - } - } else { - t.Errorf("expected digested type, got %T", ref) - } - } else if ok { - t.Errorf("unexpected digested type") - } - - st = serializationType{ - Description: tc.description, - Field: AsField(ref), - } - - b2, err := json.Marshal(st) - if err != nil { - t.Errorf("error marshing serialization type: %v", err) - } - - if string(b) != string(b2) { - t.Errorf("unexpected serialized value: expected %q, got %q", string(b), string(b2)) - } - - // Ensure st.Field is not implementing "Reference" directly, getting - // around the Reference type system - var fieldInterface interface{} = st.Field - if _, ok := fieldInterface.(Reference); ok { - t.Errorf("field should not implement Reference interface") - } - }) - } -} - -func TestWithTag(t *testing.T) { - t.Parallel() - tests := []struct { - name string - digest digest.Digest - tag string - combined string - }{ - { - name: "test.com/foo", - tag: "tag", - combined: "test.com/foo:tag", - }, - { - name: "foo", - tag: "tag2", - combined: "foo:tag2", - }, - { - name: "test.com:8000/foo", - tag: "tag4", - combined: "test.com:8000/foo:tag4", - }, - { - name: "test.com:8000/foo", - tag: "TAG5", - combined: "test.com:8000/foo:TAG5", - }, - { - name: "test.com:8000/foo", - digest: "sha256:1234567890098765432112345667890098765", - tag: "TAG5", - combined: "test.com:8000/foo:TAG5@sha256:1234567890098765432112345667890098765", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.combined, func(t *testing.T) { - t.Parallel() - named, err := WithName(tc.name) - if err != nil { - t.Errorf("error parsing name: %s", err) - } - if tc.digest != "" { - canonical, err := WithDigest(named, tc.digest) - if err != nil { - t.Errorf("error adding digest") - } - named = canonical - } - - tagged, err := WithTag(named, tc.tag) - if err != nil { - t.Errorf("WithTag failed: %s", err) - } - if tagged.String() != tc.combined { - t.Errorf("unexpected: got %q, expected %q", tagged.String(), tc.combined) - } - }) - } -} - -func TestWithDigest(t *testing.T) { - t.Parallel() - tests := []struct { - name string - digest digest.Digest - tag string - combined string - }{ - { - name: "test.com/foo", - digest: "sha256:1234567890098765432112345667890098765", - combined: "test.com/foo@sha256:1234567890098765432112345667890098765", - }, - { - name: "foo", - digest: "sha256:1234567890098765432112345667890098765", - combined: "foo@sha256:1234567890098765432112345667890098765", - }, - { - name: "test.com:8000/foo", - digest: "sha256:1234567890098765432112345667890098765", - combined: "test.com:8000/foo@sha256:1234567890098765432112345667890098765", - }, - { - name: "test.com:8000/foo", - digest: "sha256:1234567890098765432112345667890098765", - tag: "latest", - combined: "test.com:8000/foo:latest@sha256:1234567890098765432112345667890098765", - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.combined, func(t *testing.T) { - t.Parallel() - named, err := WithName(tc.name) - if err != nil { - t.Errorf("error parsing name: %s", err) - } - if tc.tag != "" { - tagged, err := WithTag(named, tc.tag) - if err != nil { - t.Errorf("error adding tag") - } - named = tagged - } - digested, err := WithDigest(named, tc.digest) - if err != nil { - t.Errorf("WithDigest failed: %s", err) - } - if digested.String() != tc.combined { - t.Errorf("unexpected: got %q, expected %q", digested.String(), tc.combined) - } - }) - } -} - -func TestParseNamed(t *testing.T) { - t.Parallel() - tests := []struct { - input string - domain string - name string - err error - }{ - { - input: "test.com/foo", - domain: "test.com", - name: "foo", - }, - { - input: "test:8080/foo", - domain: "test:8080", - name: "foo", - }, - { - input: "test_com/foo", - err: ErrNameNotCanonical, - }, - { - input: "test.com", - err: ErrNameNotCanonical, - }, - { - input: "foo", - err: ErrNameNotCanonical, - }, - { - input: "library/foo", - err: ErrNameNotCanonical, - }, - { - input: "docker.io/library/foo", - domain: "docker.io", - name: "library/foo", - }, - // Ambiguous case, parser will add "library/" to foo - { - input: "docker.io/foo", - err: ErrNameNotCanonical, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - named, err := ParseNamed(tc.input) - if err != nil && tc.err == nil { - t.Errorf("error parsing name: %s", err) - return - } else if err == nil && tc.err != nil { - t.Errorf("parsing succeeded: expected error %v", tc.err) - return - } else if err != tc.err { - t.Errorf("unexpected error %v, expected %v", err, tc.err) - return - } else if err != nil { - return - } - - domain, name := SplitHostname(named) - if domain != tc.domain { - t.Errorf("unexpected domain: got %q, expected %q", domain, tc.domain) - } - if name != tc.name { - t.Errorf("unexpected name: got %q, expected %q", name, tc.name) - } - }) - } -} diff --git a/reference/regexp_bench_test.go b/reference/regexp_bench_test.go deleted file mode 100644 index 12cb6077..00000000 --- a/reference/regexp_bench_test.go +++ /dev/null @@ -1,270 +0,0 @@ -package reference - -import ( - "strings" - "testing" -) - -func BenchmarkParse(b *testing.B) { - tests := []regexpMatch{ - { - input: "", - match: false, - }, - { - input: "short", - match: true, - }, - { - input: "simple/name", - match: true, - }, - { - input: "library/ubuntu", - match: true, - }, - { - input: "docker/stevvooe/app", - match: true, - }, - { - input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb", - match: true, - }, - { - input: "aa/aa/bb/bb/bb", - match: true, - }, - { - input: "a/a/a/a", - match: true, - }, - { - input: "a/a/a/a/", - match: false, - }, - { - input: "a//a/a", - match: false, - }, - { - input: "a", - match: true, - }, - { - input: "a/aa", - match: true, - }, - { - input: "a/aa/a", - match: true, - }, - { - input: "foo.com", - match: true, - }, - { - input: "foo.com/", - match: false, - }, - { - input: "foo.com:8080/bar", - match: true, - }, - { - input: "foo.com:http/bar", - match: false, - }, - { - input: "foo.com/bar", - match: true, - }, - { - input: "foo.com/bar/baz", - match: true, - }, - { - input: "localhost:8080/bar", - match: true, - }, - { - input: "sub-dom1.foo.com/bar/baz/quux", - match: true, - }, - { - input: "blog.foo.com/bar/baz", - match: true, - }, - { - input: "a^a", - match: false, - }, - { - input: "aa/asdf$$^/aa", - match: false, - }, - { - input: "asdf$$^/aa", - match: false, - }, - { - input: "aa-a/a", - match: true, - }, - { - input: strings.Repeat("a/", 128) + "a", - match: true, - }, - { - input: "a-/a/a/a", - match: false, - }, - { - input: "foo.com/a-/a/a", - match: false, - }, - { - input: "-foo/bar", - match: false, - }, - { - input: "foo/bar-", - match: false, - }, - { - input: "foo-/bar", - match: false, - }, - { - input: "foo/-bar", - match: false, - }, - { - input: "_foo/bar", - match: false, - }, - { - input: "foo_bar", - match: true, - }, - { - input: "foo_bar.com", - match: true, - }, - { - input: "foo_bar.com:8080", - match: false, - }, - { - input: "foo_bar.com:8080/app", - match: false, - }, - { - input: "foo.com/foo_bar", - match: true, - }, - { - input: "____/____", - match: false, - }, - { - input: "_docker/_docker", - match: false, - }, - { - input: "docker_/docker_", - match: false, - }, - { - input: "b.gcr.io/test.example.com/my-app", - match: true, - }, - { - input: "xn--n3h.com/myimage", // ☃.com in punycode - match: true, - }, - { - input: "xn--7o8h.com/myimage", // 🐳.com in punycode - match: true, - }, - { - input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode - match: true, - }, - { - input: "example.com/some_separator__underscore/myimage", - match: true, - }, - { - input: "example.com/__underscore/myimage", - match: false, - }, - { - input: "example.com/..dots/myimage", - match: false, - }, - { - input: "example.com/.dots/myimage", - match: false, - }, - { - input: "example.com/nodouble..dots/myimage", - match: false, - }, - { - input: "example.com/nodouble..dots/myimage", - match: false, - }, - { - input: "docker./docker", - match: false, - }, - { - input: ".docker/docker", - match: false, - }, - { - input: "docker-/docker", - match: false, - }, - { - input: "-docker/docker", - match: false, - }, - { - input: "do..cker/docker", - match: false, - }, - { - input: "do__cker:8080/docker", - match: false, - }, - { - input: "do__cker/docker", - match: true, - }, - { - input: "b.gcr.io/test.example.com/my-app", - match: true, - }, - { - input: "registry.io/foo/project--id.module--name.ver---sion--name", - match: true, - }, - { - input: "Asdf.com/foo/bar", // uppercase character in hostname - match: true, - }, - { - input: "Foo/FarB", // uppercase characters in remote name - match: false, - }, - } - - b.ReportAllocs() - for i := 0; i < b.N; i++ { - for _, tc := range tests { - _, _ = Parse(tc.input) - } - } -} diff --git a/reference/regexp_deprecated.go b/reference/regexp_deprecated.go new file mode 100644 index 00000000..4b9c1b58 --- /dev/null +++ b/reference/regexp_deprecated.go @@ -0,0 +1,50 @@ +package reference + +import ( + "github.com/distribution/reference" +) + +// DigestRegexp matches well-formed digests, including algorithm (e.g. "sha256:"). +// +// Deprecated: use [reference.DigestRegexp]. +var DigestRegexp = reference.DigestRegexp + +// DomainRegexp matches hostname or IP-addresses, optionally including a port +// number. It defines the structure of potential domain components that may be +// part of image names. This is purposely a subset of what is allowed by DNS to +// ensure backwards compatibility with Docker image names. It may be a subset of +// DNS domain name, an IPv4 address in decimal format, or an IPv6 address between +// square brackets (excluding zone identifiers as defined by [RFC 6874] or special +// addresses such as IPv4-Mapped). +// +// Deprecated: use [reference.DomainRegexp]. +// +// [RFC 6874]: https://www.rfc-editor.org/rfc/rfc6874. +var DomainRegexp = reference.DigestRegexp + +// IdentifierRegexp is the format for string identifier used as a +// content addressable identifier using sha256. These identifiers +// are like digests without the algorithm, since sha256 is used. +// +// Deprecated: use [reference.IdentifierRegexp]. +var IdentifierRegexp = reference.IdentifierRegexp + +// NameRegexp is the format for the name component of references, including +// an optional domain and port, but without tag or digest suffix. +// +// Deprecated: use [reference.NameRegexp]. +var NameRegexp = reference.NameRegexp + +// ReferenceRegexp is the full supported format of a reference. The regexp +// is anchored and has capturing groups for name, tag, and digest +// components. +// +// Deprecated: use [reference.ReferenceRegexp]. +var ReferenceRegexp = reference.ReferenceRegexp + +// TagRegexp matches valid tag names. From [docker/docker:graph/tags.go]. +// +// Deprecated: use [reference.TagRegexp]. +// +// [docker/docker:graph/tags.go]: https://github.com/moby/moby/blob/v1.6.0/graph/tags.go#L26-L28 +var TagRegexp = reference.TagRegexp diff --git a/reference/regexp_test.go b/reference/regexp_test.go deleted file mode 100644 index ca4680d3..00000000 --- a/reference/regexp_test.go +++ /dev/null @@ -1,589 +0,0 @@ -package reference - -import ( - "regexp" - "strings" - "testing" -) - -type regexpMatch struct { - input string - match bool - subs []string -} - -func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) { - t.Helper() - matches := r.FindStringSubmatch(m.input) - if m.match && matches != nil { - if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input { - t.Fatalf("Bad match result %#v for %q", matches, m.input) - } - if len(matches) < (len(m.subs) + 1) { - t.Errorf("Expected %d sub matches, only have %d for %q", len(m.subs), len(matches)-1, m.input) - } - for i := range m.subs { - if m.subs[i] != matches[i+1] { - t.Errorf("Unexpected submatch %d: %q, expected %q for %q", i+1, matches[i+1], m.subs[i], m.input) - } - } - } else if m.match { - t.Errorf("Expected match for %q", m.input) - } else if matches != nil { - t.Errorf("Unexpected match for %q", m.input) - } -} - -func TestDomainRegexp(t *testing.T) { - t.Parallel() - tests := []struct { - input string - match bool - }{ - { - input: "test.com", - match: true, - }, - { - input: "test.com:10304", - match: true, - }, - { - input: "test.com:http", - match: false, - }, - { - input: "localhost", - match: true, - }, - { - input: "localhost:8080", - match: true, - }, - { - input: "a", - match: true, - }, - { - input: "a.b", - match: true, - }, - { - input: "ab.cd.com", - match: true, - }, - { - input: "a-b.com", - match: true, - }, - { - input: "-ab.com", - match: false, - }, - { - input: "ab-.com", - match: false, - }, - { - input: "ab.c-om", - match: true, - }, - { - input: "ab.-com", - match: false, - }, - { - input: "ab.com-", - match: false, - }, - { - input: "0101.com", - match: true, // TODO(dmcgowan): valid if this should be allowed - }, - { - input: "001a.com", - match: true, - }, - { - input: "b.gbc.io:443", - match: true, - }, - { - input: "b.gbc.io", - match: true, - }, - { - input: "xn--n3h.com", // ☃.com in punycode - match: true, - }, - { - input: "Asdf.com", // uppercase character - match: true, - }, - { - input: "192.168.1.1:75050", // ipv4 - match: true, - }, - { - input: "192.168.1.1:750050", // port with more than 5 digits, it will fail on validation - match: true, - }, - { - input: "[fd00:1:2::3]:75050", // ipv6 compressed - match: true, - }, - { - input: "[fd00:1:2::3]75050", // ipv6 wrong port separator - match: false, - }, - { - input: "[fd00:1:2::3]::75050", // ipv6 wrong port separator - match: false, - }, - { - input: "[fd00:1:2::3%eth0]:75050", // ipv6 with zone - match: false, - }, - { - input: "[fd00123123123]:75050", // ipv6 wrong format, will fail in validation - match: true, - }, - { - input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:75050", // ipv6 long format - match: true, - }, - { - input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:750505", // ipv6 long format and invalid port, it will fail in validation - match: true, - }, - { - input: "fd00:1:2::3:75050", // bad ipv6 without square brackets - match: false, - }, - } - r := regexp.MustCompile(`^` + DomainRegexp.String() + `$`) - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - match := r.MatchString(tc.input) - if match != tc.match { - t.Errorf("Expected match=%t, got %t", tc.match, match) - } - }) - } -} - -func TestFullNameRegexp(t *testing.T) { - t.Parallel() - if anchoredNameRegexp.NumSubexp() != 2 { - t.Fatalf("anchored name regexp should have two submatches: %v, %v != 2", - anchoredNameRegexp, anchoredNameRegexp.NumSubexp()) - } - - tests := []regexpMatch{ - { - input: "", - match: false, - }, - { - input: "short", - match: true, - subs: []string{"", "short"}, - }, - { - input: "simple/name", - match: true, - subs: []string{"simple", "name"}, - }, - { - input: "library/ubuntu", - match: true, - subs: []string{"library", "ubuntu"}, - }, - { - input: "docker/stevvooe/app", - match: true, - subs: []string{"docker", "stevvooe/app"}, - }, - { - input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb", - match: true, - subs: []string{"aa", "aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb"}, - }, - { - input: "aa/aa/bb/bb/bb", - match: true, - subs: []string{"aa", "aa/bb/bb/bb"}, - }, - { - input: "a/a/a/a", - match: true, - subs: []string{"a", "a/a/a"}, - }, - { - input: "a/a/a/a/", - match: false, - }, - { - input: "a//a/a", - match: false, - }, - { - input: "a", - match: true, - subs: []string{"", "a"}, - }, - { - input: "a/aa", - match: true, - subs: []string{"a", "aa"}, - }, - { - input: "a/aa/a", - match: true, - subs: []string{"a", "aa/a"}, - }, - { - input: "foo.com", - match: true, - subs: []string{"", "foo.com"}, - }, - { - input: "foo.com/", - match: false, - }, - { - input: "foo.com:8080/bar", - match: true, - subs: []string{"foo.com:8080", "bar"}, - }, - { - input: "foo.com:http/bar", - match: false, - }, - { - input: "foo.com/bar", - match: true, - subs: []string{"foo.com", "bar"}, - }, - { - input: "foo.com/bar/baz", - match: true, - subs: []string{"foo.com", "bar/baz"}, - }, - { - input: "localhost:8080/bar", - match: true, - subs: []string{"localhost:8080", "bar"}, - }, - { - input: "sub-dom1.foo.com/bar/baz/quux", - match: true, - subs: []string{"sub-dom1.foo.com", "bar/baz/quux"}, - }, - { - input: "blog.foo.com/bar/baz", - match: true, - subs: []string{"blog.foo.com", "bar/baz"}, - }, - { - input: "a^a", - match: false, - }, - { - input: "aa/asdf$$^/aa", - match: false, - }, - { - input: "asdf$$^/aa", - match: false, - }, - { - input: "aa-a/a", - match: true, - subs: []string{"aa-a", "a"}, - }, - { - input: strings.Repeat("a/", 128) + "a", - match: true, - subs: []string{"a", strings.Repeat("a/", 127) + "a"}, - }, - { - input: "a-/a/a/a", - match: false, - }, - { - input: "foo.com/a-/a/a", - match: false, - }, - { - input: "-foo/bar", - match: false, - }, - { - input: "foo/bar-", - match: false, - }, - { - input: "foo-/bar", - match: false, - }, - { - input: "foo/-bar", - match: false, - }, - { - input: "_foo/bar", - match: false, - }, - { - input: "foo_bar", - match: true, - subs: []string{"", "foo_bar"}, - }, - { - input: "foo_bar.com", - match: true, - subs: []string{"", "foo_bar.com"}, - }, - { - input: "foo_bar.com:8080", - match: false, - }, - { - input: "foo_bar.com:8080/app", - match: false, - }, - { - input: "foo.com/foo_bar", - match: true, - subs: []string{"foo.com", "foo_bar"}, - }, - { - input: "____/____", - match: false, - }, - { - input: "_docker/_docker", - match: false, - }, - { - input: "docker_/docker_", - match: false, - }, - { - input: "b.gcr.io/test.example.com/my-app", - match: true, - subs: []string{"b.gcr.io", "test.example.com/my-app"}, - }, - { - input: "xn--n3h.com/myimage", // ☃.com in punycode - match: true, - subs: []string{"xn--n3h.com", "myimage"}, - }, - { - input: "xn--7o8h.com/myimage", // 🐳.com in punycode - match: true, - subs: []string{"xn--7o8h.com", "myimage"}, - }, - { - input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode - match: true, - subs: []string{"example.com", "xn--7o8h.com/myimage"}, - }, - { - input: "example.com/some_separator__underscore/myimage", - match: true, - subs: []string{"example.com", "some_separator__underscore/myimage"}, - }, - { - input: "example.com/__underscore/myimage", - match: false, - }, - { - input: "example.com/..dots/myimage", - match: false, - }, - { - input: "example.com/.dots/myimage", - match: false, - }, - { - input: "example.com/nodouble..dots/myimage", - match: false, - }, - { - input: "example.com/nodouble..dots/myimage", - match: false, - }, - { - input: "docker./docker", - match: false, - }, - { - input: ".docker/docker", - match: false, - }, - { - input: "docker-/docker", - match: false, - }, - { - input: "-docker/docker", - match: false, - }, - { - input: "do..cker/docker", - match: false, - }, - { - input: "do__cker:8080/docker", - match: false, - }, - { - input: "do__cker/docker", - match: true, - subs: []string{"", "do__cker/docker"}, - }, - { - input: "b.gcr.io/test.example.com/my-app", - match: true, - subs: []string{"b.gcr.io", "test.example.com/my-app"}, - }, - { - input: "registry.io/foo/project--id.module--name.ver---sion--name", - match: true, - subs: []string{"registry.io", "foo/project--id.module--name.ver---sion--name"}, - }, - { - input: "Asdf.com/foo/bar", // uppercase character in hostname - match: true, - }, - { - input: "Foo/FarB", // uppercase characters in remote name - match: false, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - checkRegexp(t, anchoredNameRegexp, tc) - }) - } -} - -func TestReferenceRegexp(t *testing.T) { - t.Parallel() - if ReferenceRegexp.NumSubexp() != 3 { - t.Fatalf("anchored name regexp should have three submatches: %v, %v != 3", - ReferenceRegexp, ReferenceRegexp.NumSubexp()) - } - - tests := []regexpMatch{ - { - input: "registry.com:8080/myapp:tag", - match: true, - subs: []string{"registry.com:8080/myapp", "tag", ""}, - }, - { - input: "registry.com:8080/myapp@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: true, - subs: []string{"registry.com:8080/myapp", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, - }, - { - input: "registry.com:8080/myapp:tag2@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: true, - subs: []string{"registry.com:8080/myapp", "tag2", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, - }, - { - input: "registry.com:8080/myapp@sha256:badbadbadbad", - match: false, - }, - { - input: "registry.com:8080/myapp:invalid~tag", - match: false, - }, - { - input: "bad_hostname.com:8080/myapp:tag", - match: false, - }, - { - input:// localhost treated as name, missing tag with 8080 as tag - "localhost:8080@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: true, - subs: []string{"localhost", "8080", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, - }, - { - input: "localhost:8080/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: true, - subs: []string{"localhost:8080/name", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, - }, - { - input: "localhost:http/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: false, - }, - { - // localhost will be treated as an image name without a host - input: "localhost@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912", - match: true, - subs: []string{"localhost", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"}, - }, - { - input: "registry.com:8080/myapp@bad", - match: false, - }, - { - input: "registry.com:8080/myapp@2bad", - match: false, // TODO(dmcgowan): Support this as valid - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - checkRegexp(t, ReferenceRegexp, tc) - }) - } -} - -func TestIdentifierRegexp(t *testing.T) { - t.Parallel() - tests := []struct { - input string - match bool - }{ - { - input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821", - match: true, - }, - { - input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C", - match: false, - }, - { - input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf", - match: false, - }, - { - input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821", - match: false, - }, - { - input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482", - match: false, - }, - } - for _, tc := range tests { - tc := tc - t.Run(tc.input, func(t *testing.T) { - t.Parallel() - match := anchoredIdentifierRegexp.MatchString(tc.input) - if match != tc.match { - t.Errorf("Expected match=%t, got %t", tc.match, match) - } - }) - } -} diff --git a/reference/sort_deprecated.go b/reference/sort_deprecated.go new file mode 100644 index 00000000..a73251b6 --- /dev/null +++ b/reference/sort_deprecated.go @@ -0,0 +1,10 @@ +package reference + +import "github.com/distribution/reference" + +// Sort sorts string references preferring higher information references. +// +// Deprecated: use [reference.Sort]. +func Sort(references []string) []string { + return reference.Sort(references) +} diff --git a/reference/sort_test.go b/reference/sort_test.go deleted file mode 100644 index be072904..00000000 --- a/reference/sort_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - 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 reference - -import ( - "io" - "math/rand" - "testing" - - "github.com/opencontainers/go-digest" -) - -func TestReferenceSorting(t *testing.T) { - t.Parallel() - digested := func(seed int64) string { - b, err := io.ReadAll(io.LimitReader(rand.New(rand.NewSource(seed)), 64)) - if err != nil { - panic(err) - } - return digest.FromBytes(b).String() - } - // Add z. prefix to string sort after "sha256:" - r1 := func(name, tag string, seed int64) string { - return "z.containerd.io/" + name + ":" + tag + "@" + digested(seed) - } - r2 := func(name, tag string) string { - return "z.containerd.io/" + name + ":" + tag - } - r3 := func(name string, seed int64) string { - return "z.containerd.io/" + name + "@" + digested(seed) - } - - for i, tc := range []struct { - unsorted []string - expected []string - }{ - { - unsorted: []string{r2("name", "latest"), r3("name", 1), r1("name", "latest", 1)}, - expected: []string{r1("name", "latest", 1), r2("name", "latest"), r3("name", 1)}, - }, - { - unsorted: []string{"can't parse this:latest", r3("name", 1), r2("name", "latest")}, - expected: []string{r2("name", "latest"), r3("name", 1), "can't parse this:latest"}, - }, - { - unsorted: []string{digested(1), r3("name", 1), r2("name", "latest")}, - expected: []string{r2("name", "latest"), r3("name", 1), digested(1)}, - }, - { - unsorted: []string{r2("name", "tag2"), r2("name", "tag3"), r2("name", "tag1")}, - expected: []string{r2("name", "tag1"), r2("name", "tag2"), r2("name", "tag3")}, - }, - { - unsorted: []string{r2("name-2", "tag"), r2("name-3", "tag"), r2("name-1", "tag")}, - expected: []string{r2("name-1", "tag"), r2("name-2", "tag"), r2("name-3", "tag")}, - }, - } { - sorted := Sort(tc.unsorted) - if len(sorted) != len(tc.expected) { - t.Errorf("[%d]: Mismatched sized, got %d, expected %d", i, len(sorted), len(tc.expected)) - continue - } - for j := range sorted { - if sorted[j] != tc.expected[j] { - t.Errorf("[%d]: Wrong value at %d, got %q, expected %q", i, j, sorted[j], tc.expected[j]) - break - } - } - } -} diff --git a/registry.go b/registry.go index 658f2df0..d0deee65 100644 --- a/registry.go +++ b/registry.go @@ -3,7 +3,7 @@ package distribution import ( "context" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" ) // Scope defines the set of items that match a namespace. diff --git a/registry/api/v2/descriptors.go b/registry/api/v2/descriptors.go index 9d1cfdb8..d0e9620e 100644 --- a/registry/api/v2/descriptors.go +++ b/registry/api/v2/descriptors.go @@ -4,8 +4,8 @@ import ( "net/http" "regexp" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/api/v2/urls.go b/registry/api/v2/urls.go index f4aa9095..1a76565a 100644 --- a/registry/api/v2/urls.go +++ b/registry/api/v2/urls.go @@ -6,7 +6,7 @@ import ( "net/url" "strings" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" "github.com/gorilla/mux" ) diff --git a/registry/api/v2/urls_test.go b/registry/api/v2/urls_test.go index 0ccf3518..ba908531 100644 --- a/registry/api/v2/urls_test.go +++ b/registry/api/v2/urls_test.go @@ -7,7 +7,7 @@ import ( "reflect" "testing" - "github.com/distribution/distribution/v3/reference" + "github.com/distribution/reference" ) type urlBuilderTestCase struct { diff --git a/registry/client/repository.go b/registry/client/repository.go index 62f1c15d..58e763a4 100644 --- a/registry/client/repository.go +++ b/registry/client/repository.go @@ -15,11 +15,11 @@ import ( "time" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/client/transport" "github.com/distribution/distribution/v3/registry/storage/cache" "github.com/distribution/distribution/v3/registry/storage/cache/memory" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/client/repository_test.go b/registry/client/repository_test.go index 56681c0f..4cda067a 100644 --- a/registry/client/repository_test.go +++ b/registry/client/repository_test.go @@ -20,11 +20,11 @@ import ( "github.com/distribution/distribution/v3/context" "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/ocischema" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/testutil" "github.com/distribution/distribution/v3/uuid" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/registry/handlers/api_test.go b/registry/handlers/api_test.go index 2be94f51..c921ce9a 100644 --- a/registry/handlers/api_test.go +++ b/registry/handlers/api_test.go @@ -24,13 +24,13 @@ import ( "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/manifestlist" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" v2 "github.com/distribution/distribution/v3/registry/api/v2" storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/registry/storage/driver/factory" _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/docker/libtrust" "github.com/gorilla/handlers" "github.com/opencontainers/go-digest" diff --git a/registry/handlers/app.go b/registry/handlers/app.go index 04e3a070..7ea9f435 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -24,7 +24,6 @@ import ( "github.com/distribution/distribution/v3/health/checks" prometheus "github.com/distribution/distribution/v3/metrics" "github.com/distribution/distribution/v3/notifications" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/auth" @@ -38,6 +37,7 @@ import ( "github.com/distribution/distribution/v3/registry/storage/driver/factory" storagemiddleware "github.com/distribution/distribution/v3/registry/storage/driver/middleware" "github.com/distribution/distribution/v3/version" + "github.com/distribution/reference" events "github.com/docker/go-events" "github.com/docker/go-metrics" "github.com/gorilla/mux" diff --git a/registry/handlers/blobupload.go b/registry/handlers/blobupload.go index b7599a75..ee7626cb 100644 --- a/registry/handlers/blobupload.go +++ b/registry/handlers/blobupload.go @@ -8,10 +8,10 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/storage" + "github.com/distribution/reference" "github.com/gorilla/handlers" "github.com/opencontainers/go-digest" ) diff --git a/registry/handlers/manifests.go b/registry/handlers/manifests.go index e5815f3d..c5e2d064 100644 --- a/registry/handlers/manifests.go +++ b/registry/handlers/manifests.go @@ -12,11 +12,11 @@ import ( "github.com/distribution/distribution/v3/manifest/manifestlist" "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/api/errcode" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/registry/auth" "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" "github.com/gorilla/handlers" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" diff --git a/registry/proxy/proxyblobstore.go b/registry/proxy/proxyblobstore.go index a03b7237..fd473e45 100644 --- a/registry/proxy/proxyblobstore.go +++ b/registry/proxy/proxyblobstore.go @@ -12,8 +12,8 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/proxy/scheduler" + "github.com/distribution/reference" ) type proxyBlobStore struct { diff --git a/registry/proxy/proxyblobstore_test.go b/registry/proxy/proxyblobstore_test.go index d90d2384..60297b36 100644 --- a/registry/proxy/proxyblobstore_test.go +++ b/registry/proxy/proxyblobstore_test.go @@ -11,12 +11,12 @@ import ( "time" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/proxy/scheduler" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver/filesystem" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/proxy/proxymanifeststore.go b/registry/proxy/proxymanifeststore.go index 5673e69b..fa60a07e 100644 --- a/registry/proxy/proxymanifeststore.go +++ b/registry/proxy/proxymanifeststore.go @@ -8,8 +8,8 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/proxy/scheduler" + "github.com/distribution/reference" ) type proxyManifestStore struct { diff --git a/registry/proxy/proxymanifeststore_test.go b/registry/proxy/proxymanifeststore_test.go index bd1c7ad9..2ac325d4 100644 --- a/registry/proxy/proxymanifeststore_test.go +++ b/registry/proxy/proxymanifeststore_test.go @@ -8,7 +8,6 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/client/auth" "github.com/distribution/distribution/v3/registry/client/auth/challenge" "github.com/distribution/distribution/v3/registry/proxy/scheduler" @@ -16,6 +15,7 @@ import ( "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/proxy/proxyregistry.go b/registry/proxy/proxyregistry.go index b67ba1a2..8420138f 100644 --- a/registry/proxy/proxyregistry.go +++ b/registry/proxy/proxyregistry.go @@ -11,7 +11,6 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/configuration" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/client" "github.com/distribution/distribution/v3/registry/client/auth" "github.com/distribution/distribution/v3/registry/client/auth/challenge" @@ -19,6 +18,7 @@ import ( "github.com/distribution/distribution/v3/registry/proxy/scheduler" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" ) var repositoryTTL = 24 * 7 * time.Hour diff --git a/registry/proxy/scheduler/scheduler.go b/registry/proxy/scheduler/scheduler.go index 8e5f4160..e492cf71 100644 --- a/registry/proxy/scheduler/scheduler.go +++ b/registry/proxy/scheduler/scheduler.go @@ -8,8 +8,8 @@ import ( "time" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" ) // onTTLExpiryFunc is called when a repository's TTL expires diff --git a/registry/proxy/scheduler/scheduler_test.go b/registry/proxy/scheduler/scheduler_test.go index c1324ff8..1309a1b0 100644 --- a/registry/proxy/scheduler/scheduler_test.go +++ b/registry/proxy/scheduler/scheduler_test.go @@ -7,8 +7,8 @@ import ( "time" "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" + "github.com/distribution/reference" ) func testRefs(t *testing.T) (reference.Reference, reference.Reference, reference.Reference) { diff --git a/registry/storage/blob_test.go b/registry/storage/blob_test.go index 01158255..f3cc1179 100644 --- a/registry/storage/blob_test.go +++ b/registry/storage/blob_test.go @@ -11,10 +11,10 @@ import ( "testing" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/cache/memory/memory.go b/registry/storage/cache/memory/memory.go index 81994712..8097939f 100644 --- a/registry/storage/cache/memory/memory.go +++ b/registry/storage/cache/memory/memory.go @@ -5,8 +5,8 @@ import ( "math" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache" + "github.com/distribution/reference" "github.com/hashicorp/golang-lru/arc/v2" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/cache/redis/redis.go b/registry/storage/cache/redis/redis.go index 80286d71..e0f4192c 100644 --- a/registry/storage/cache/redis/redis.go +++ b/registry/storage/cache/redis/redis.go @@ -6,9 +6,9 @@ import ( "strconv" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache" "github.com/distribution/distribution/v3/registry/storage/cache/metrics" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" "github.com/redis/go-redis/v9" ) diff --git a/registry/storage/catalog.go b/registry/storage/catalog.go index 21040a98..08f9288e 100644 --- a/registry/storage/catalog.go +++ b/registry/storage/catalog.go @@ -7,8 +7,8 @@ import ( "path" "strings" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" ) // Returns a list, or partial list, of repositories in the registry. diff --git a/registry/storage/catalog_test.go b/registry/storage/catalog_test.go index 965aa8ab..26491bc7 100644 --- a/registry/storage/catalog_test.go +++ b/registry/storage/catalog_test.go @@ -8,11 +8,11 @@ import ( "testing" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/garbagecollect.go b/registry/storage/garbagecollect.go index 661af53f..7895c9b5 100644 --- a/registry/storage/garbagecollect.go +++ b/registry/storage/garbagecollect.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/garbagecollect_test.go b/registry/storage/garbagecollect_test.go index 695dbe9d..edf7f1c3 100644 --- a/registry/storage/garbagecollect_test.go +++ b/registry/storage/garbagecollect_test.go @@ -7,10 +7,10 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/linkedblobstore.go b/registry/storage/linkedblobstore.go index 48dc7722..0412bf43 100644 --- a/registry/storage/linkedblobstore.go +++ b/registry/storage/linkedblobstore.go @@ -10,9 +10,9 @@ import ( "github.com/distribution/distribution/v3" dcontext "github.com/distribution/distribution/v3/context" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/uuid" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/linkedblobstore_test.go b/registry/storage/linkedblobstore_test.go index 57bb8eb7..357e0ef9 100644 --- a/registry/storage/linkedblobstore_test.go +++ b/registry/storage/linkedblobstore_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) diff --git a/registry/storage/manifeststore_test.go b/registry/storage/manifeststore_test.go index 3285d541..82a6c97d 100644 --- a/registry/storage/manifeststore_test.go +++ b/registry/storage/manifeststore_test.go @@ -12,11 +12,11 @@ import ( "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/registry/storage/registry.go b/registry/storage/registry.go index 6c263e7c..49b604f2 100644 --- a/registry/storage/registry.go +++ b/registry/storage/registry.go @@ -5,9 +5,9 @@ import ( "regexp" "github.com/distribution/distribution/v3" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/cache" storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" + "github.com/distribution/reference" ) // registry is the top-level implementation of Registry for use in the storage diff --git a/registry/storage/tagstore_test.go b/registry/storage/tagstore_test.go index 650416c1..2659cfac 100644 --- a/registry/storage/tagstore_test.go +++ b/registry/storage/tagstore_test.go @@ -8,8 +8,8 @@ import ( "github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest/schema2" - "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" + "github.com/distribution/reference" digest "github.com/opencontainers/go-digest" ) diff --git a/vendor/github.com/distribution/reference/.gitattributes b/vendor/github.com/distribution/reference/.gitattributes new file mode 100644 index 00000000..d207b180 --- /dev/null +++ b/vendor/github.com/distribution/reference/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/vendor/github.com/distribution/reference/.gitignore b/vendor/github.com/distribution/reference/.gitignore new file mode 100644 index 00000000..dc07e6b0 --- /dev/null +++ b/vendor/github.com/distribution/reference/.gitignore @@ -0,0 +1,2 @@ +# Cover profiles +*.out diff --git a/vendor/github.com/distribution/reference/.golangci.yml b/vendor/github.com/distribution/reference/.golangci.yml new file mode 100644 index 00000000..793f0bb7 --- /dev/null +++ b/vendor/github.com/distribution/reference/.golangci.yml @@ -0,0 +1,18 @@ +linters: + enable: + - bodyclose + - dupword # Checks for duplicate words in the source code + - gofmt + - goimports + - ineffassign + - misspell + - revive + - staticcheck + - unconvert + - unused + - vet + disable: + - errcheck + +run: + deadline: 2m diff --git a/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md b/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md new file mode 100644 index 00000000..48f6704c --- /dev/null +++ b/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md @@ -0,0 +1,5 @@ +# Code of Conduct + +We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). + +Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) in order to report violations of the Code of Conduct. diff --git a/vendor/github.com/distribution/reference/CONTRIBUTING.md b/vendor/github.com/distribution/reference/CONTRIBUTING.md new file mode 100644 index 00000000..ab219466 --- /dev/null +++ b/vendor/github.com/distribution/reference/CONTRIBUTING.md @@ -0,0 +1,114 @@ +# Contributing to the reference library + +## Community help + +If you need help, please ask in the [#distribution](https://cloud-native.slack.com/archives/C01GVR8SY4R) channel on CNCF community slack. +[Click here for an invite to the CNCF community slack](https://slack.cncf.io/) + +## Reporting security issues + +The maintainers take security seriously. If you discover a security +issue, please bring it to their attention right away! + +Please **DO NOT** file a public issue, instead send your report privately to +[cncf-distribution-security@lists.cncf.io](mailto:cncf-distribution-security@lists.cncf.io). + +## Reporting an issue properly + +By following these simple rules you will get better and faster feedback on your issue. + + - search the bugtracker for an already reported issue + +### If you found an issue that describes your problem: + + - please read other user comments first, and confirm this is the same issue: a given error condition might be indicative of different problems - you may also find a workaround in the comments + - please refrain from adding "same thing here" or "+1" comments + - you don't need to comment on an issue to get notified of updates: just hit the "subscribe" button + - comment if you have some new, technical and relevant information to add to the case + - __DO NOT__ comment on closed issues or merged PRs. If you think you have a related problem, open up a new issue and reference the PR or issue. + +### If you have not found an existing issue that describes your problem: + + 1. create a new issue, with a succinct title that describes your issue: + - bad title: "It doesn't work with my docker" + - good title: "Private registry push fail: 400 error with E_INVALID_DIGEST" + 2. copy the output of (or similar for other container tools): + - `docker version` + - `docker info` + - `docker exec registry --version` + 3. copy the command line you used to launch your Registry + 4. restart your docker daemon in debug mode (add `-D` to the daemon launch arguments) + 5. reproduce your problem and get your docker daemon logs showing the error + 6. if relevant, copy your registry logs that show the error + 7. provide any relevant detail about your specific Registry configuration (e.g., storage backend used) + 8. indicate if you are using an enterprise proxy, Nginx, or anything else between you and your Registry + +## Contributing Code + +Contributions should be made via pull requests. Pull requests will be reviewed +by one or more maintainers or reviewers and merged when acceptable. + +You should follow the basic GitHub workflow: + + 1. Use your own [fork](https://help.github.com/en/articles/about-forks) + 2. Create your [change](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes) + 3. Test your code + 4. [Commit](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages) your work, always [sign your commits](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages) + 5. Push your change to your fork and create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) + +Refer to [containerd's contribution guide](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes) +for tips on creating a successful contribution. + +## Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/vendor/github.com/distribution/reference/GOVERNANCE.md b/vendor/github.com/distribution/reference/GOVERNANCE.md new file mode 100644 index 00000000..200045b0 --- /dev/null +++ b/vendor/github.com/distribution/reference/GOVERNANCE.md @@ -0,0 +1,144 @@ +# distribution/reference Project Governance + +Distribution [Code of Conduct](./CODE-OF-CONDUCT.md) can be found here. + +For specific guidance on practical contribution steps please +see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide. + +## Maintainership + +There are different types of maintainers, with different responsibilities, but +all maintainers have 3 things in common: + +1) They share responsibility in the project's success. +2) They have made a long-term, recurring time investment to improve the project. +3) They spend that time doing whatever needs to be done, not necessarily what +is the most interesting or fun. + +Maintainers are often under-appreciated, because their work is harder to appreciate. +It's easy to appreciate a really cool and technically advanced feature. It's harder +to appreciate the absence of bugs, the slow but steady improvement in stability, +or the reliability of a release process. But those things distinguish a good +project from a great one. + +## Reviewers + +A reviewer is a core role within the project. +They share in reviewing issues and pull requests and their LGTM counts towards the +required LGTM count to merge a code change into the project. + +Reviewers are part of the organization but do not have write access. +Becoming a reviewer is a core aspect in the journey to becoming a maintainer. + +## Adding maintainers + +Maintainers are first and foremost contributors that have shown they are +committed to the long term success of a project. Contributors wanting to become +maintainers are expected to be deeply involved in contributing code, pull +request review, and triage of issues in the project for more than three months. + +Just contributing does not make you a maintainer, it is about building trust +with the current maintainers of the project and being a person that they can +depend on and trust to make decisions in the best interest of the project. + +Periodically, the existing maintainers curate a list of contributors that have +shown regular activity on the project over the prior months. From this list, +maintainer candidates are selected and proposed in a pull request or a +maintainers communication channel. + +After a candidate has been announced to the maintainers, the existing +maintainers are given five business days to discuss the candidate, raise +objections and cast their vote. Votes may take place on the communication +channel or via pull request comment. Candidates must be approved by at least 66% +of the current maintainers by adding their vote on the mailing list. The +reviewer role has the same process but only requires 33% of current maintainers. +Only maintainers of the repository that the candidate is proposed for are +allowed to vote. + +If a candidate is approved, a maintainer will contact the candidate to invite +the candidate to open a pull request that adds the contributor to the +MAINTAINERS file. The voting process may take place inside a pull request if a +maintainer has already discussed the candidacy with the candidate and a +maintainer is willing to be a sponsor by opening the pull request. The candidate +becomes a maintainer once the pull request is merged. + +## Stepping down policy + +Life priorities, interests, and passions can change. If you're a maintainer but +feel you must remove yourself from the list, inform other maintainers that you +intend to step down, and if possible, help find someone to pick up your work. +At the very least, ensure your work can be continued where you left off. + +After you've informed other maintainers, create a pull request to remove +yourself from the MAINTAINERS file. + +## Removal of inactive maintainers + +Similar to the procedure for adding new maintainers, existing maintainers can +be removed from the list if they do not show significant activity on the +project. Periodically, the maintainers review the list of maintainers and their +activity over the last three months. + +If a maintainer has shown insufficient activity over this period, a neutral +person will contact the maintainer to ask if they want to continue being +a maintainer. If the maintainer decides to step down as a maintainer, they +open a pull request to be removed from the MAINTAINERS file. + +If the maintainer wants to remain a maintainer, but is unable to perform the +required duties they can be removed with a vote of at least 66% of the current +maintainers. In this case, maintainers should first propose the change to +maintainers via the maintainers communication channel, then open a pull request +for voting. The voting period is five business days. The voting pull request +should not come as a surpise to any maintainer and any discussion related to +performance must not be discussed on the pull request. + +## How are decisions made? + +Docker distribution is an open-source project with an open design philosophy. +This means that the repository is the source of truth for EVERY aspect of the +project, including its philosophy, design, road map, and APIs. *If it's part of +the project, it's in the repo. If it's in the repo, it's part of the project.* + +As a result, all decisions can be expressed as changes to the repository. An +implementation change is a change to the source code. An API change is a change +to the API specification. A philosophy change is a change to the philosophy +manifesto, and so on. + +All decisions affecting distribution, big and small, follow the same 3 steps: + +* Step 1: Open a pull request. Anyone can do this. + +* Step 2: Discuss the pull request. Anyone can do this. + +* Step 3: Merge or refuse the pull request. Who does this depends on the nature +of the pull request and which areas of the project it affects. + +## Helping contributors with the DCO + +The [DCO or `Sign your work`](./CONTRIBUTING.md#sign-your-work) +requirement is not intended as a roadblock or speed bump. + +Some contributors are not as familiar with `git`, or have used a web +based editor, and thus asking them to `git commit --amend -s` is not the best +way forward. + +In this case, maintainers can update the commits based on clause (c) of the DCO. +The most trivial way for a contributor to allow the maintainer to do this, is to +add a DCO signature in a pull requests's comment, or a maintainer can simply +note that the change is sufficiently trivial that it does not substantially +change the existing contribution - i.e., a spelling change. + +When you add someone's DCO, please also add your own to keep a log. + +## I'm a maintainer. Should I make pull requests too? + +Yes. Nobody should ever push to master directly. All changes should be +made through a pull request. + +## Conflict Resolution + +If you have a technical dispute that you feel has reached an impasse with a +subset of the community, any contributor may open an issue, specifically +calling for a resolution vote of the current core maintainers to resolve the +dispute. The same voting quorums required (2/3) for adding and removing +maintainers will apply to conflict resolution. diff --git a/vendor/github.com/distribution/reference/LICENSE b/vendor/github.com/distribution/reference/LICENSE new file mode 100644 index 00000000..e06d2081 --- /dev/null +++ b/vendor/github.com/distribution/reference/LICENSE @@ -0,0 +1,202 @@ +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/distribution/reference/MAINTAINERS b/vendor/github.com/distribution/reference/MAINTAINERS new file mode 100644 index 00000000..9e0a60c8 --- /dev/null +++ b/vendor/github.com/distribution/reference/MAINTAINERS @@ -0,0 +1,26 @@ +# Distribution project maintainers & reviewers +# +# See GOVERNANCE.md for maintainer versus reviewer roles +# +# MAINTAINERS (cncf-distribution-maintainers@lists.cncf.io) +# GitHub ID, Name, Email address +"chrispat","Chris Patterson","chrispat@github.com" +"clarkbw","Bryan Clark","clarkbw@github.com" +"corhere","Cory Snider","csnider@mirantis.com" +"deleteriousEffect","Hayley Swimelar","hswimelar@gitlab.com" +"heww","He Weiwei","hweiwei@vmware.com" +"joaodrp","João Pereira","jpereira@gitlab.com" +"justincormack","Justin Cormack","justin.cormack@docker.com" +"squizzi","Kyle Squizzato","ksquizzato@mirantis.com" +"milosgajdos","Milos Gajdos","milosthegajdos@gmail.com" +"sargun","Sargun Dhillon","sargun@sargun.me" +"wy65701436","Wang Yan","wangyan@vmware.com" +"stevelasker","Steve Lasker","steve.lasker@microsoft.com" +# +# REVIEWERS +# GitHub ID, Name, Email address +"dmcgowan","Derek McGowan","derek@mcgstyle.net" +"stevvooe","Stephen Day","stevvooe@gmail.com" +"thajeztah","Sebastiaan van Stijn","github@gone.nl" +"DavidSpek", "David van der Spek", "vanderspek.david@gmail.com" +"Jamstah", "James Hewitt", "james.hewitt@gmail.com" diff --git a/vendor/github.com/distribution/reference/Makefile b/vendor/github.com/distribution/reference/Makefile new file mode 100644 index 00000000..c78576b7 --- /dev/null +++ b/vendor/github.com/distribution/reference/Makefile @@ -0,0 +1,25 @@ +# Project packages. +PACKAGES=$(shell go list ./...) + +# Flags passed to `go test` +BUILDFLAGS ?= +TESTFLAGS ?= + +.PHONY: all build test coverage +.DEFAULT: all + +all: build + +build: ## no binaries to build, so just check compilation suceeds + go build ${BUILDFLAGS} ./... + +test: ## run tests + go test ${TESTFLAGS} ./... + +coverage: ## generate coverprofiles from the unit tests + rm -f coverage.txt + go test ${TESTFLAGS} -cover -coverprofile=cover.out ./... + +.PHONY: help +help: + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_\/%-]+:.*?##/ { printf " \033[36m%-27s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) diff --git a/vendor/github.com/distribution/reference/README.md b/vendor/github.com/distribution/reference/README.md new file mode 100644 index 00000000..e2531e49 --- /dev/null +++ b/vendor/github.com/distribution/reference/README.md @@ -0,0 +1,30 @@ +# Distribution reference + +Go library to handle references to container images. + + + +[![Build Status](https://github.com/distribution/reference/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/distribution/reference/actions?query=workflow%3ACI) +[![GoDoc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/distribution/reference) +[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) +[![codecov](https://codecov.io/gh/distribution/reference/branch/main/graph/badge.svg)](https://codecov.io/gh/distribution/reference) +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B162%2Fgithub.com%2Fdistribution%2Freference.svg?type=shield)](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fdistribution%2Freference?ref=badge_shield) + +This repository contains a library for handling refrences to container images held in container registries. Please see [godoc](https://pkg.go.dev/github.com/distribution/reference) for details. + +## Contribution + +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute +issues, fixes, and patches to this project. + +## Communication + +For async communication and long running discussions please use issues and pull requests on the github repo. +This will be the best place to discuss design and implementation. + +For sync communication we have a #distribution channel in the [CNCF Slack](https://slack.cncf.io/) +that everyone is welcome to join and chat about development. + +## Licenses + +The distribution codebase is released under the [Apache 2.0 license](LICENSE). diff --git a/vendor/github.com/distribution/reference/SECURITY.md b/vendor/github.com/distribution/reference/SECURITY.md new file mode 100644 index 00000000..aaf983c0 --- /dev/null +++ b/vendor/github.com/distribution/reference/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +## Reporting a Vulnerability + +The maintainers take security seriously. If you discover a security issue, please bring it to their attention right away! + +Please DO NOT file a public issue, instead send your report privately to cncf-distribution-security@lists.cncf.io. diff --git a/vendor/github.com/distribution/reference/distribution-logo.svg b/vendor/github.com/distribution/reference/distribution-logo.svg new file mode 100644 index 00000000..cc9f4073 --- /dev/null +++ b/vendor/github.com/distribution/reference/distribution-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/reference/helpers.go b/vendor/github.com/distribution/reference/helpers.go similarity index 100% rename from reference/helpers.go rename to vendor/github.com/distribution/reference/helpers.go diff --git a/reference/normalize.go b/vendor/github.com/distribution/reference/normalize.go similarity index 99% rename from reference/normalize.go rename to vendor/github.com/distribution/reference/normalize.go index f2ce13ad..a30229d0 100644 --- a/reference/normalize.go +++ b/vendor/github.com/distribution/reference/normalize.go @@ -140,7 +140,7 @@ func splitDockerDomain(name string) (domain, remainder string) { } // familiarizeName returns a shortened version of the name familiar -// to to the Docker UI. Familiar names have the default domain +// to the Docker UI. Familiar names have the default domain // "docker.io" and "library/" repository prefix removed. // For example, "docker.io/library/redis" will have the familiar // name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp". diff --git a/reference/reference.go b/vendor/github.com/distribution/reference/reference.go similarity index 100% rename from reference/reference.go rename to vendor/github.com/distribution/reference/reference.go diff --git a/reference/regexp.go b/vendor/github.com/distribution/reference/regexp.go similarity index 100% rename from reference/regexp.go rename to vendor/github.com/distribution/reference/regexp.go diff --git a/reference/sort.go b/vendor/github.com/distribution/reference/sort.go similarity index 100% rename from reference/sort.go rename to vendor/github.com/distribution/reference/sort.go diff --git a/vendor/modules.txt b/vendor/modules.txt index db082910..ace76b88 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -160,6 +160,9 @@ github.com/cyphar/filepath-securejoin # github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f ## explicit github.com/dgryski/go-rendezvous +# github.com/distribution/reference v0.5.0 +## explicit; go 1.20 +github.com/distribution/reference # github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c ## explicit github.com/docker/go-events