diff --git a/.github/workflows/ociregistry-lint.yml b/.github/workflows/oci-lint.yml similarity index 52% rename from .github/workflows/ociregistry-lint.yml rename to .github/workflows/oci-lint.yml index c9fcc68..b6d8bdd 100644 --- a/.github/workflows/ociregistry-lint.yml +++ b/.github/workflows/oci-lint.yml @@ -1,10 +1,10 @@ -name: "ociregistry: lint" +name: "oci: lint" on: pull_request: paths: - - "ociregistry/**" - - ".github/workflows/ociregistry-lint.yml" + - "./**" + - ".github/workflows/oci-lint.yml" permissions: contents: read @@ -13,19 +13,15 @@ jobs: lint: name: Lint runs-on: ubuntu-latest - defaults: - run: - working-directory: ociregistry steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version-file: ociregistry/go.mod - cache-dependency-path: ociregistry/go.sum + go-version-file: go.mod + cache-dependency-path: go.sum - name: Run golangci-lint uses: golangci/golangci-lint-action@v7 with: version: v2.10.1 - working-directory: ociregistry diff --git a/.github/workflows/ociregistry-tag.yml b/.github/workflows/oci-tag.yml similarity index 76% rename from .github/workflows/ociregistry-tag.yml rename to .github/workflows/oci-tag.yml index 0aea47e..f4ce8bf 100644 --- a/.github/workflows/ociregistry-tag.yml +++ b/.github/workflows/oci-tag.yml @@ -1,12 +1,12 @@ -name: "ociregistry: tag" +name: "oci: tag" on: push: branches: - main paths: - - "ociregistry/**" - - ".github/workflows/ociregistry-tag.yml" + - "./**" + - ".github/workflows/oci-tag.yml" permissions: contents: write @@ -25,8 +25,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DEFAULT_BUMP: patch - INITIAL_VERSION: 0.0.0 - TAG_PREFIX: ociregistry/v + INITIAL_VERSION: 0.0.1 + TAG_PREFIX: v MAJOR_STRING_TOKEN: (MAJOR) MINOR_STRING_TOKEN: (MINOR) BRANCH_HISTORY: compare diff --git a/.github/workflows/ociregistry-test.yml b/.github/workflows/oci-test.yml similarity index 51% rename from .github/workflows/ociregistry-test.yml rename to .github/workflows/oci-test.yml index 03d085a..ce417a6 100644 --- a/.github/workflows/ociregistry-test.yml +++ b/.github/workflows/oci-test.yml @@ -1,10 +1,10 @@ -name: "ociregistry: test" +name: "oci: test" on: pull_request: paths: - - "ociregistry/**" - - ".github/workflows/ociregistry-test.yml" + - "./**" + - ".github/workflows/oci-test.yml" permissions: contents: read @@ -13,16 +13,13 @@ jobs: test: name: Test runs-on: ubuntu-latest - defaults: - run: - working-directory: ociregistry steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version-file: ociregistry/go.mod - cache-dependency-path: ociregistry/go.sum + go-version-file: go.mod + cache-dependency-path: go.sum - name: Run tests run: go test -race -count=1 ./... diff --git a/ociregistry/.golangci.yml b/.golangci.yml similarity index 98% rename from ociregistry/.golangci.yml rename to .golangci.yml index b75a41d..2abd605 100644 --- a/ociregistry/.golangci.yml +++ b/.golangci.yml @@ -150,7 +150,7 @@ linters: # Inherited code: resp.Body.Close and io.Copy in HTTP handlers. - linters: - errcheck - source: "(resp\\.Write|io\\.Copy|ociregistry\\.WriteError)" + source: "(resp\\.Write|io\\.Copy|oci\\.WriteError)" # Inherited code: string HTTP method constants. - linters: - usestdlibvars diff --git a/README.md b/README.md index e342785..1f68bbb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,22 @@ # OCI Go modules This repository holds functionality related to OCI (Open Container Initiative). -Currently it holds only a single module: `ociregistry`. -See the documentation for [that package](./ociregistry) for details. \ No newline at end of file + +The top level package (`oci`) this module defines a [Go interface](./interface.go) that encapsulates the operations provided by an OCI +registry. + +Full reference documentation can be found [here](https://pkg.go.dev/cuelabs.dev/go/oci/oci). + +It also provides a lightweight in-memory implementation of that interface (`ocimem`) +and an HTTP server that implements the [OCI registry protocol](https://github.com/opencontainers/distribution-spec/blob/main/spec.md) on top of it. + +The server currently passes the [conformance tests](https://pkg.go.dev/github.com/opencontainers/distribution-spec/conformance). + +The aim is to provide an ergonomic interface for defining and layering +OCI registry implementations. + +Although the API is fairly stable, it's still in v0 currently, so incompatible changes can't be ruled out. + +The code was originally derived from [cue-labs/oci](https://github.com/cue-labs/oci) which was originally derived from +the [go-containerregistry](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/registry) package, but has +considerably diverged since then. diff --git a/ociregistry/Taskfile.yml b/Taskfile.yml similarity index 100% rename from ociregistry/Taskfile.yml rename to Taskfile.yml diff --git a/cmd/ocisrv/go.mod b/cmd/ocisrv/go.mod index eb24ae5..12ff5bf 100644 --- a/cmd/ocisrv/go.mod +++ b/cmd/ocisrv/go.mod @@ -5,13 +5,13 @@ go 1.25.0 require ( github.com/cue-exp/cueconfig v0.0.1 github.com/go-json-experiment/json v0.0.0-20240524174822-2d9f40f7385b - github.com/jcarter3/oci/ociregistry v0.0.0 + github.com/jcarter3/oci v0.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/rogpeppe/go-internal v1.14.1 github.com/rogpeppe/retry v0.1.0 ) -replace github.com/jcarter3/oci/ociregistry => ../../ociregistry +replace github.com/jcarter3/oci => ../../oci require ( cuelang.org/go v0.6.0-alpha.2.0.20230628162133-7be6224cbc4f // indirect diff --git a/cmd/ocisrv/main.go b/cmd/ocisrv/main.go index d310edd..9716e28 100644 --- a/cmd/ocisrv/main.go +++ b/cmd/ocisrv/main.go @@ -26,7 +26,7 @@ import ( "github.com/cue-exp/cueconfig" "github.com/go-json-experiment/json" "github.com/go-json-experiment/json/jsontext" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci/ociserver" ) var ( @@ -44,7 +44,7 @@ type config struct { func main() { if err := main1(); err != nil { - fmt.Fprintf(os.Stderr, "ociregistry: %v\n", err) + fmt.Fprintf(os.Stderr, "oci: %v\n", err) os.Exit(1) } } diff --git a/cmd/ocisrv/main_test.go b/cmd/ocisrv/main_test.go index 06472b9..ba3f7dc 100644 --- a/cmd/ocisrv/main_test.go +++ b/cmd/ocisrv/main_test.go @@ -27,9 +27,7 @@ import ( "testing" "time" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociclient" - "github.com/opencontainers/go-digest" + "github.com/jcarter3/oci/ociclient" "github.com/rogpeppe/go-internal/testscript" "github.com/rogpeppe/retry" ) @@ -80,7 +78,7 @@ func cmdPushBlob(ts *testscript.TestScript, neg bool, args []string) { ts.Fatalf("blob digest mismatch") } - _, err = r.PushBlob(context.Background(), repo, ociregistry.Descriptor{ + _, err = r.PushBlob(context.Background(), repo, oci.Descriptor{ Size: int64(len(data)), Digest: digest.Digest(dg), }, bytes.NewReader(data)) @@ -125,7 +123,7 @@ var waitStrategy = retry.Strategy{ MaxDuration: 500 * time.Millisecond, } -func connect(ts *testscript.TestScript) (ociregistry.Interface, error) { +func connect(ts *testscript.TestScript) (oci.Interface, error) { addrFile := ts.Getenv("ADDR_FILE") if addrFile == "" { return nil, fmt.Errorf("$ADDR_FILE not set") diff --git a/cmd/ocisrv/registry.go b/cmd/ocisrv/registry.go index aa906cc..414b100 100644 --- a/cmd/ocisrv/registry.go +++ b/cmd/ocisrv/registry.go @@ -20,12 +20,12 @@ import ( "regexp" "strings" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociclient" - "github.com/jcarter3/oci/ociregistry/ocidebug" - "github.com/jcarter3/oci/ociregistry/ocifilter" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ociunify" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociclient" + "github.com/jcarter3/oci/ocidebug" + "github.com/jcarter3/oci/ocifilter" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ociunify" ) var kindToRegistryType = make(map[string]reflect.Type) @@ -50,7 +50,7 @@ func init() { } type registry interface { - new() (ociregistry.Interface, error) + new() (oci.Interface, error) } type clientRegistry struct { @@ -59,7 +59,7 @@ type clientRegistry struct { DebugID string `json:"debugID,omitempty"` } -func (r clientRegistry) new() (ociregistry.Interface, error) { +func (r clientRegistry) new() (oci.Interface, error) { return ociclient.New(r.Host, &ociclient.Options{ DebugID: r.DebugID, Insecure: r.Insecure, @@ -72,7 +72,7 @@ type selectRegistry struct { Exclude *regexp.Regexp `json:"exclude,omitempty"` } -func (r selectRegistry) new() (ociregistry.Interface, error) { +func (r selectRegistry) new() (oci.Interface, error) { r1, err := r.Registry.new() if err != nil { return nil, err @@ -92,7 +92,7 @@ type readOnlyRegistry struct { Registry registry `json:"registry"` } -func (r readOnlyRegistry) new() (ociregistry.Interface, error) { +func (r readOnlyRegistry) new() (oci.Interface, error) { r1, err := r.Registry.new() if err != nil { return nil, err @@ -104,7 +104,7 @@ type immutableRegistry struct { Registry registry `json:"registry"` } -func (r immutableRegistry) new() (ociregistry.Interface, error) { +func (r immutableRegistry) new() (oci.Interface, error) { r1, err := r.Registry.new() if err != nil { return nil, err @@ -117,11 +117,11 @@ type unifyRegistry struct { // TODO options } -func (r unifyRegistry) new() (ociregistry.Interface, error) { +func (r unifyRegistry) new() (oci.Interface, error) { if len(r.Registries) != 2 { return nil, fmt.Errorf("can currently unify exactly two registries only") } - r1 := make([]ociregistry.Interface, len(r.Registries)) + r1 := make([]oci.Interface, len(r.Registries)) for i := range r.Registries { ri, err := r.Registries[i].new() if err != nil { @@ -134,7 +134,7 @@ func (r unifyRegistry) new() (ociregistry.Interface, error) { type memRegistry struct{} -func (r memRegistry) new() (ociregistry.Interface, error) { +func (r memRegistry) new() (oci.Interface, error) { return ocimem.New(), nil } @@ -142,7 +142,7 @@ type debugRegistry struct { Registry registry `json:"registry"` } -func (r debugRegistry) new() (ociregistry.Interface, error) { +func (r debugRegistry) new() (oci.Interface, error) { r1, err := r.Registry.new() if err != nil { return nil, err diff --git a/ociregistry/error.go b/error.go similarity index 99% rename from ociregistry/error.go rename to error.go index b274a41..a686a10 100644 --- a/ociregistry/error.go +++ b/error.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ociregistry +package oci import ( "encoding/json" @@ -79,7 +79,7 @@ type WireError struct { Detail_ json.RawMessage `json:"detail,omitempty"` } -// Is makes it possible for users to write `if errors.Is(err, ociregistry.ErrBlobUnknown)` +// Is makes it possible for users to write `if errors.Is(err, oci.ErrBlobUnknown)` // even when the error hasn't exactly wrapped that error. func (e *WireError) Is(err error) bool { var rerr Error @@ -196,7 +196,7 @@ func (e *httpError) Unwrap() error { return e.underlying } -// Is makes it possible for users to write `if errors.Is(err, ociregistry.ErrRangeInvalid)` +// Is makes it possible for users to write `if errors.Is(err, oci.ErrRangeInvalid)` // even when the error hasn't exactly wrapped that error. func (e *httpError) Is(err error) bool { switch e.statusCode { diff --git a/ociregistry/error_test.go b/error_test.go similarity index 99% rename from ociregistry/error_test.go rename to error_test.go index 01b8f3d..4ff5b67 100644 --- a/ociregistry/error_test.go +++ b/error_test.go @@ -1,4 +1,4 @@ -package ociregistry +package oci import ( "encoding/json" diff --git a/ociregistry/func.go b/func.go similarity index 98% rename from ociregistry/func.go rename to func.go index 37c5d60..5555fd9 100644 --- a/ociregistry/func.go +++ b/func.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ociregistry +package oci import ( "context" @@ -32,7 +32,7 @@ var _ Interface = (*Funcs)(nil) // returns ErrUnsupported from its Err method. // // If Funcs is nil itself, all methods will behave as if the corresponding field was nil, -// so (*ociregistry.Funcs)(nil) is a useful placeholder to implement Interface. +// so (*oci.Funcs)(nil) is a useful placeholder to implement Interface. // // If you're writing your own implementation of Funcs, you'll need to embed a *Funcs // value to get an implementation of the private method. This means that it will diff --git a/ociregistry/go.mod b/go.mod similarity index 90% rename from ociregistry/go.mod rename to go.mod index 30b7fc7..47eba45 100644 --- a/ociregistry/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/jcarter3/oci/ociregistry +module github.com/jcarter3/oci go 1.25.0 diff --git a/ociregistry/go.sum b/go.sum similarity index 100% rename from ociregistry/go.sum rename to go.sum diff --git a/ociregistry/interface.go b/interface.go similarity index 95% rename from ociregistry/interface.go rename to interface.go index 4241e14..bbb18b6 100644 --- a/ociregistry/interface.go +++ b/interface.go @@ -12,35 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package ociregistry provides an abstraction that represents the +// Package oci provides an abstraction that represents the // capabilities provided by an OCI registry. // // See the [OCI distribution specification] for more information on OCI registries. // // Packages within this module provide the capability to translate to and // from the HTTP protocol documented in that specification: -// - [github.com/jcarter3/oci/ociregistry/ociclient] provides an [Interface] value +// - [github.com/jcarter3/oci/ociclient] provides an [Interface] value // that acts as an HTTP client. -// - [github.com/jcarter3/oci/ociregistry/ociserver] provides an HTTP server +// - [github.com/jcarter3/oci/ociserver] provides an HTTP server // that serves the distribution protocol by making calls to an arbitrary // [Interface] value. // // When used together in a stack, the above two packages can be used // to provide a simple proxy server. // -// The [github.com/jcarter3/oci/ociregistry/ocimem] package provides a trivial +// The [github.com/jcarter3/oci/ocimem] package provides a trivial // in-memory implementation of the interface. // // Other packages provide some utilities that manipulate [Interface] values: -// - [github.com/jcarter3/oci/ociregistry/ocifilter] provides functionality for exposing +// - [github.com/jcarter3/oci/ocifilter] provides functionality for exposing // modified or restricted views onto a registry. -// - [github.com/jcarter3/oci/ociregistry/ociunify] can combine two registries into one +// - [github.com/jcarter3/oci/ociunify] can combine two registries into one // unified view across both. // // # Notes on [Interface] // // In general, the caller cannot assume that the implementation of a given [Interface] value -// is present on the network. For example, [github.com/jcarter3/oci/ociregistry/ocimem] +// is present on the network. For example, [github.com/jcarter3/oci/ocimem] // doesn't know about the network at all. But there are times when an implementation // might want to provide information about the location of blobs or manifests so // that a client can go direct if it wishes. That is, a proxy might not wish @@ -54,14 +54,14 @@ // to redirect clients appropriately. // // [OCI distribution specification]: https://github.com/opencontainers/distribution-spec/blob/main/spec.md -package ociregistry +package oci import ( "context" "io" "iter" - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci/ociref" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/ociregistry/internal/ocirequest/create.go b/internal/ocirequest/create.go similarity index 100% rename from ociregistry/internal/ocirequest/create.go rename to internal/ocirequest/create.go diff --git a/ociregistry/internal/ocirequest/request.go b/internal/ocirequest/request.go similarity index 93% rename from ociregistry/internal/ocirequest/request.go rename to internal/ocirequest/request.go index cee5815..5321a65 100644 --- a/ociregistry/internal/ocirequest/request.go +++ b/internal/ocirequest/request.go @@ -23,12 +23,12 @@ import ( "strings" "unicode/utf8" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociref" ) var ( - errBadlyFormedDigest = ociregistry.NewError("badly formed digest", ociregistry.ErrDigestInvalid.Code(), nil) + errBadlyFormedDigest = oci.NewError("badly formed digest", oci.ErrDigestInvalid.Code(), nil) errMethodNotAllowed = httpErrorf(http.StatusMethodNotAllowed, "method not allowed") errNotFound = httpErrorf(http.StatusNotFound, "page not found") ) @@ -38,7 +38,7 @@ func badRequestf(f string, a ...any) error { } func httpErrorf(statusCode int, f string, a ...any) error { - return ociregistry.NewHTTPError(fmt.Errorf(f, a...), statusCode, nil, nil) + return oci.NewHTTPError(fmt.Errorf(f, a...), statusCode, nil, nil) } type Request struct { @@ -188,7 +188,7 @@ const ( // Parse parses the given HTTP method and URL as an OCI registry request. // It understands the endpoints described in the [distribution spec]. // -// If it returns an error, it will be of type [ociregistry.Error] or [ociregistry.HTTPError]. +// If it returns an error, it will be of type [oci.Error] or [oci.HTTPError]. // // [distribution spec]: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints func Parse(method string, u *url.URL) (*Request, error) { @@ -205,7 +205,7 @@ func Parse(method string, u *url.URL) (*Request, error) { } path, ok := strings.CutPrefix(path, "/v2/") if !ok { - return nil, ociregistry.NewError("unknown URL path", ociregistry.ErrNameUnknown.Code(), nil) + return nil, oci.NewError("unknown URL path", oci.ErrNameUnknown.Code(), nil) } if path == "_catalog" { if method != "GET" { @@ -222,7 +222,7 @@ func Parse(method string, u *url.URL) (*Request, error) { if ok { rreq.Repo = uploadPath if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } if method != "POST" { return nil, errMethodNotAllowed @@ -231,7 +231,7 @@ func Parse(method string, u *url.URL) (*Request, error) { // end-11 rreq.Digest = d if !ociref.IsValidDigest(rreq.Digest) { - return nil, ociregistry.ErrDigestInvalid + return nil, oci.ErrDigestInvalid } rreq.FromRepo = urlq.Get("from") if rreq.FromRepo == "" { @@ -243,7 +243,7 @@ func Parse(method string, u *url.URL) (*Request, error) { return &rreq, nil } if !ociref.IsValidRepository(rreq.FromRepo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } rreq.Kind = ReqBlobMount return &rreq, nil @@ -276,7 +276,7 @@ func Parse(method string, u *url.URL) (*Request, error) { return nil, errBadlyFormedDigest } if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } rreq.Digest = last switch method { @@ -299,7 +299,7 @@ func Parse(method string, u *url.URL) (*Request, error) { } rreq.Repo = repo if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } uploadID64 := last if uploadID64 == "" { @@ -332,7 +332,7 @@ func Parse(method string, u *url.URL) (*Request, error) { case "manifests": rreq.Repo = path if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } switch { case ociref.IsValidDigest(last): @@ -369,7 +369,7 @@ func Parse(method string, u *url.URL) (*Request, error) { } rreq.Repo = path if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } rreq.Kind = ReqTagsList return &rreq, nil @@ -382,7 +382,7 @@ func Parse(method string, u *url.URL) (*Request, error) { } rreq.Repo = path if !ociref.IsValidRepository(rreq.Repo) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } // Unlike other list-oriented endpoints, there appears to be no defined way for the client // to indicate the desired number of results, but set ListN anyway to be future-proof. diff --git a/ociregistry/internal/ocirequest/request_test.go b/internal/ocirequest/request_test.go similarity index 100% rename from ociregistry/internal/ocirequest/request_test.go rename to internal/ocirequest/request_test.go diff --git a/ociregistry/iter.go b/iter.go similarity index 99% rename from ociregistry/iter.go rename to iter.go index e34d70e..71828ea 100644 --- a/ociregistry/iter.go +++ b/iter.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ociregistry +package oci import "iter" diff --git a/ociregistry/iter_test.go b/iter_test.go similarity index 96% rename from ociregistry/iter_test.go rename to iter_test.go index 250fb6e..b2f5a5e 100644 --- a/ociregistry/iter_test.go +++ b/iter_test.go @@ -1,4 +1,4 @@ -package ociregistry +package oci import ( "errors" diff --git a/ociregistry/ociauth/auth.go b/ociauth/auth.go similarity index 98% rename from ociregistry/ociauth/auth.go rename to ociauth/auth.go index 0716a57..0a864f3 100644 --- a/ociregistry/ociauth/auth.go +++ b/ociauth/auth.go @@ -14,7 +14,7 @@ import ( "sync" "time" - "github.com/jcarter3/oci/ociregistry" + oci "github.com/jcarter3/oci" ) // TODO decide on a good value for this. @@ -199,9 +199,9 @@ func (a *stdTransport) RoundTrip(req *http.Request) (*http.Response, error) { // provided a token that it gave us. Treat it as Forbidden (403) instead. // TODO include the original body/error as part of the message or message detail? resp.Body.Close() - data, err := json.Marshal(&ociregistry.WireErrors{ - Errors: []ociregistry.WireError{{ - Code_: ociregistry.ErrDenied.Code(), + data, err := json.Marshal(&oci.WireErrors{ + Errors: []oci.WireError{{ + Code_: oci.ErrDenied.Code(), Message: "unauthorized response with freshly acquired auth token", }}, }) @@ -332,7 +332,7 @@ func (r *registry) acquireAccessToken(ctx context.Context, requiredScope, wantSc scope := requiredScope.Union(wantScope) tok, err := r.acquireToken(ctx, scope) if err != nil { - var herr ociregistry.HTTPError + var herr oci.HTTPError if !errors.As(err, &herr) || herr.StatusCode() != http.StatusUnauthorized { return "", err } @@ -406,7 +406,7 @@ func (r *registry) acquireToken(ctx context.Context, scope Scope) (*wireToken, e if err == nil { return tok, nil } - var herr ociregistry.HTTPError + var herr oci.HTTPError if !errors.As(err, &herr) || herr.StatusCode() != http.StatusNotFound { return tok, err } @@ -484,7 +484,7 @@ func (r *registry) doTokenRequest(req *http.Request) (*wireToken, error) { defer resp.Body.Close() data, bodyErr := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusOK { - return nil, ociregistry.NewHTTPError(nil, resp.StatusCode, resp, data) + return nil, oci.NewHTTPError(nil, resp.StatusCode, resp, data) } if bodyErr != nil { return nil, fmt.Errorf("error reading response body: %v", err) diff --git a/ociregistry/ociauth/auth_test.go b/ociauth/auth_test.go similarity index 100% rename from ociregistry/ociauth/auth_test.go rename to ociauth/auth_test.go diff --git a/ociregistry/ociauth/authfile.go b/ociauth/authfile.go similarity index 100% rename from ociregistry/ociauth/authfile.go rename to ociauth/authfile.go diff --git a/ociregistry/ociauth/authfile_test.go b/ociauth/authfile_test.go similarity index 100% rename from ociregistry/ociauth/authfile_test.go rename to ociauth/authfile_test.go diff --git a/ociregistry/ociauth/challenge.go b/ociauth/challenge.go similarity index 100% rename from ociregistry/ociauth/challenge.go rename to ociauth/challenge.go diff --git a/ociregistry/ociauth/context.go b/ociauth/context.go similarity index 100% rename from ociregistry/ociauth/context.go rename to ociauth/context.go diff --git a/ociregistry/ociauth/scope.go b/ociauth/scope.go similarity index 100% rename from ociregistry/ociauth/scope.go rename to ociauth/scope.go diff --git a/ociregistry/ociauth/scope_test.go b/ociauth/scope_test.go similarity index 100% rename from ociregistry/ociauth/scope_test.go rename to ociauth/scope_test.go diff --git a/ociregistry/ociclient/auth_test.go b/ociclient/auth_test.go similarity index 81% rename from ociregistry/ociclient/auth_test.go rename to ociclient/auth_test.go index 86a0865..80050c6 100644 --- a/ociregistry/ociclient/auth_test.go +++ b/ociclient/auth_test.go @@ -8,10 +8,10 @@ import ( "strings" "testing" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociauth" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociauth" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ociserver" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,39 +27,39 @@ func TestAuthScopes(t *testing.T) { defer srv.Close() srvURL, _ := url.Parse(srv.URL) - assertScope := func(scope string, f func(ctx context.Context, r ociregistry.Interface)) { + assertScope := func(scope string, f func(ctx context.Context, r oci.Interface)) { assertAuthScope(t, srvURL.Host, scope, f) } - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.GetBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.GetBlobRange(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 100, 200) }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.GetManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.GetTag(ctx, "foo/bar", "sometag") }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.ResolveBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.ResolveManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { r.ResolveTag(ctx, "foo/bar", "sometag") }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { - r.PushBlob(ctx, "foo/bar", ociregistry.Descriptor{ + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { + r.PushBlob(ctx, "foo/bar", oci.Descriptor{ MediaType: "application/json", Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", Size: 3, }, strings.NewReader("foo")) }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { w, err := r.PushBlobChunked(ctx, "foo/bar", 0) require.NoError(t, err) w.Write([]byte("foo")) @@ -72,37 +72,37 @@ func TestAuthScopes(t *testing.T) { _, err = w.Commit(digest.FromString("foobar")) require.NoError(t, err) }) - assertScope("repository:x/y:pull repository:z/w:push", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:x/y:pull repository:z/w:push", func(ctx context.Context, r oci.Interface) { r.MountBlob(ctx, "x/y", "z/w", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { - r.PushManifest(ctx, "foo/bar", []byte("something"), "application/json", &ociregistry.PushManifestParameters{ + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { + r.PushManifest(ctx, "foo/bar", []byte("something"), "application/json", &oci.PushManifestParameters{ Tags: []string{"sometag"}, }) }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { r.DeleteBlob(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { r.DeleteManifest(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertScope("repository:foo/bar:push", func(ctx context.Context, r ociregistry.Interface) { + assertScope("repository:foo/bar:push", func(ctx context.Context, r oci.Interface) { r.DeleteTag(ctx, "foo/bar", "sometag") }) - assertScope("registry:catalog:*", func(ctx context.Context, r ociregistry.Interface) { - ociregistry.All(r.Repositories(ctx, "")) + assertScope("registry:catalog:*", func(ctx context.Context, r oci.Interface) { + oci.All(r.Repositories(ctx, "")) }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { - ociregistry.All(r.Tags(ctx, "foo/bar", nil)) + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { + oci.All(r.Tags(ctx, "foo/bar", nil)) }) - assertScope("repository:foo/bar:pull", func(ctx context.Context, r ociregistry.Interface) { - ociregistry.All(r.Referrers(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) + assertScope("repository:foo/bar:pull", func(ctx context.Context, r oci.Interface) { + oci.All(r.Referrers(ctx, "foo/bar", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) }) } // assertAuthScope asserts that the given function makes a client request with the // given scope to the given URL. -func assertAuthScope(t *testing.T, host string, scope string, f func(ctx context.Context, r ociregistry.Interface)) { +func assertAuthScope(t *testing.T, host string, scope string, f func(ctx context.Context, r oci.Interface)) { requestedScopes := make(map[string]bool) // Check that the context is passed through with values intact. diff --git a/ociregistry/ociclient/badname_test.go b/ociclient/badname_test.go similarity index 100% rename from ociregistry/ociclient/badname_test.go rename to ociclient/badname_test.go diff --git a/ociregistry/ociclient/client.go b/ociclient/client.go similarity index 88% rename from ociregistry/ociclient/client.go rename to ociclient/client.go index 95dc887..7bc06ce 100644 --- a/ociregistry/ociclient/client.go +++ b/ociclient/client.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package ociclient provides an implementation of ociregistry.Interface that +// Package ociclient provides an implementation of oci.Interface that // uses HTTP to talk to the remote registry. package ociclient @@ -30,13 +30,13 @@ import ( "strings" "sync/atomic" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" - "github.com/jcarter3/oci/ociregistry/ociauth" - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci/internal/ocirequest" + "github.com/jcarter3/oci/ociauth" + "github.com/jcarter3/oci/ociref" ) // debug enables logging. @@ -71,7 +71,7 @@ var debugID int32 // // The host specifies the host name to talk to; it may // optionally be a host:port pair. -func New(host string, opts0 *Options) (ociregistry.Interface, error) { +func New(host string, opts0 *Options) (oci.Interface, error) { var opts Options if opts0 != nil { opts = *opts0 @@ -104,7 +104,7 @@ func New(host string, opts0 *Options) (ociregistry.Interface, error) { } type client struct { - *ociregistry.Funcs + *oci.Funcs httpScheme string httpHost string httpClient *http.Client @@ -124,7 +124,7 @@ const ( // // Note: this implies that the Digest field will be empty if there is no // digest in the response and knownDigest is empty. -func descriptorFromResponse(resp *http.Response, knownDigest digest.Digest, require descriptorRequired) (ociregistry.Descriptor, error) { +func descriptorFromResponse(resp *http.Response, knownDigest digest.Digest, require descriptorRequired) (oci.Descriptor, error) { contentType := resp.Header.Get("Content-Type") if contentType == "" { contentType = "application/octet-stream" @@ -134,20 +134,20 @@ func descriptorFromResponse(resp *http.Response, knownDigest digest.Digest, requ if resp.StatusCode == http.StatusPartialContent { contentRange := resp.Header.Get("Content-Range") if contentRange == "" { - return ociregistry.Descriptor{}, fmt.Errorf("no Content-Range in partial content response") + return oci.Descriptor{}, fmt.Errorf("no Content-Range in partial content response") } i := strings.LastIndex(contentRange, "/") if i == -1 { - return ociregistry.Descriptor{}, fmt.Errorf("malformed Content-Range %q", contentRange) + return oci.Descriptor{}, fmt.Errorf("malformed Content-Range %q", contentRange) } contentSize, err := strconv.ParseInt(contentRange[i+1:], 10, 64) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("malformed Content-Range %q", contentRange) + return oci.Descriptor{}, fmt.Errorf("malformed Content-Range %q", contentRange) } size = contentSize } else { if resp.ContentLength < 0 { - return ociregistry.Descriptor{}, fmt.Errorf("unknown content length") + return oci.Descriptor{}, fmt.Errorf("unknown content length") } size = resp.ContentLength } @@ -155,22 +155,22 @@ func descriptorFromResponse(resp *http.Response, knownDigest digest.Digest, requ digest := digest.Digest(resp.Header.Get("Docker-Content-Digest")) if digest != "" { if !ociref.IsValidDigest(string(digest)) { - return ociregistry.Descriptor{}, fmt.Errorf("bad digest %q found in response", digest) + return oci.Descriptor{}, fmt.Errorf("bad digest %q found in response", digest) } } else { digest = knownDigest } if (require&requireDigest) != 0 && digest == "" { - return ociregistry.Descriptor{}, fmt.Errorf("no digest found in response") + return oci.Descriptor{}, fmt.Errorf("no digest found in response") } - return ociregistry.Descriptor{ + return oci.Descriptor{ Digest: digest, MediaType: contentType, Size: size, }, nil } -func newBlobReader(r io.ReadCloser, desc ociregistry.Descriptor) *blobReader { +func newBlobReader(r io.ReadCloser, desc oci.Descriptor) *blobReader { return &blobReader{ r: r, digester: desc.Digest.Algorithm().Hash(), @@ -179,7 +179,7 @@ func newBlobReader(r io.ReadCloser, desc ociregistry.Descriptor) *blobReader { } } -func newBlobReaderUnverified(r io.ReadCloser, desc ociregistry.Descriptor) *blobReader { +func newBlobReaderUnverified(r io.ReadCloser, desc oci.Descriptor) *blobReader { br := newBlobReader(r, desc) br.verify = false return br @@ -189,11 +189,11 @@ type blobReader struct { r io.ReadCloser n int64 digester hash.Hash - desc ociregistry.Descriptor + desc oci.Descriptor verify bool } -func (r *blobReader) Descriptor() ociregistry.Descriptor { +func (r *blobReader) Descriptor() oci.Descriptor { return r.desc } @@ -205,7 +205,7 @@ func (r *blobReader) Read(buf []byte) (int, error) { if r.n > r.desc.Size { // Fail early when the blob is too big; we can do that even // when we're not verifying for other use cases. - return n, fmt.Errorf("blob size exceeds content length %d: %w", r.desc.Size, ociregistry.ErrSizeInvalid) + return n, fmt.Errorf("blob size exceeds content length %d: %w", r.desc.Size, oci.ErrSizeInvalid) } return n, nil } @@ -216,7 +216,7 @@ func (r *blobReader) Read(buf []byte) (int, error) { return n, io.EOF } if r.n != r.desc.Size { - return n, fmt.Errorf("blob size mismatch (%d/%d): %w", r.n, r.desc.Size, ociregistry.ErrSizeInvalid) + return n, fmt.Errorf("blob size mismatch (%d/%d): %w", r.n, r.desc.Size, oci.ErrSizeInvalid) } gotDigest := digest.NewDigest(r.desc.Digest.Algorithm(), r.digester) if gotDigest != r.desc.Digest { diff --git a/ociregistry/ociclient/deleter.go b/ociclient/deleter.go similarity index 89% rename from ociregistry/ociclient/deleter.go rename to ociclient/deleter.go index 8d4509a..03dcc39 100644 --- a/ociregistry/ociclient/deleter.go +++ b/ociclient/deleter.go @@ -18,11 +18,11 @@ import ( "context" "net/http" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/internal/ocirequest" ) -func (c *client) DeleteBlob(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (c *client) DeleteBlob(ctx context.Context, repoName string, digest oci.Digest) error { return c.delete(ctx, &ocirequest.Request{ Kind: ocirequest.ReqBlobDelete, Repo: repoName, @@ -30,7 +30,7 @@ func (c *client) DeleteBlob(ctx context.Context, repoName string, digest ociregi }) } -func (c *client) DeleteManifest(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (c *client) DeleteManifest(ctx context.Context, repoName string, digest oci.Digest) error { return c.delete(ctx, &ocirequest.Request{ Kind: ocirequest.ReqManifestDelete, Repo: repoName, diff --git a/ociregistry/ociclient/error.go b/ociclient/error.go similarity index 90% rename from ociregistry/ociclient/error.go rename to ociclient/error.go index cf82703..aeb5e24 100644 --- a/ociregistry/ociclient/error.go +++ b/ociclient/error.go @@ -22,7 +22,7 @@ import ( "net/http" "strings" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // errorBodySizeLimit holds the maximum number of response bytes aallowed in @@ -47,7 +47,7 @@ func makeError(resp *http.Response) error { } } // We always include the status code and response in the error. - return ociregistry.NewHTTPError(err, resp.StatusCode, resp, data) + return oci.NewHTTPError(err, resp.StatusCode, resp, data) } func makeError1(resp *http.Response, bodyData []byte) error { @@ -61,15 +61,15 @@ func makeError1(resp *http.Response, bodyData []byte) error { var err error switch resp.StatusCode { case http.StatusNotFound: - err = ociregistry.ErrNameUnknown + err = oci.ErrNameUnknown case http.StatusUnauthorized: - err = ociregistry.ErrUnauthorized + err = oci.ErrUnauthorized case http.StatusForbidden: - err = ociregistry.ErrDenied + err = oci.ErrDenied case http.StatusTooManyRequests: - err = ociregistry.ErrTooManyRequests + err = oci.ErrTooManyRequests case http.StatusBadRequest: - err = ociregistry.ErrUnsupported + err = oci.ErrUnsupported default: // Our caller will turn this into a non-nil error. return nil @@ -79,7 +79,7 @@ func makeError1(resp *http.Response, bodyData []byte) error { if ctype := resp.Header.Get("Content-Type"); !isJSONMediaType(ctype) { return fmt.Errorf("non-JSON error response %q; body %q", ctype, bodyData) } - var errs ociregistry.WireErrors + var errs oci.WireErrors if err := json.Unmarshal(bodyData, &errs); err != nil { return fmt.Errorf("%s: malformed error response: %v", resp.Status, err) } diff --git a/ociregistry/ociclient/error_test.go b/ociclient/error_test.go similarity index 63% rename from ociregistry/ociclient/error_test.go rename to ociclient/error_test.go index 381e1ba..26d405e 100644 --- a/ociregistry/ociclient/error_test.go +++ b/ociclient/error_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociserver" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,9 +19,9 @@ import ( func TestErrorStuttering(t *testing.T) { // This checks that the stuttering observed in issue #31 // isn't an issue when ociserver wraps ociclient. - srv := httptest.NewServer(ociserver.New(&ociregistry.Funcs{ + srv := httptest.NewServer(ociserver.New(&oci.Funcs{ NewError: func(ctx context.Context, methodName, repo string) error { - return ociregistry.ErrManifestUnknown + return oci.ErrManifestUnknown }, }, nil)) defer srv.Close() @@ -32,7 +32,7 @@ func TestErrorStuttering(t *testing.T) { }) require.NoError(t, err) _, err = r.GetTag(context.Background(), "foo", "sometag") - assert.ErrorIs(t, err, ociregistry.ErrManifestUnknown) + assert.ErrorIs(t, err, oci.ErrManifestUnknown) assert.Regexp(t, `404 Not Found: manifest unknown: manifest unknown to registry`, err.Error()) // ResolveTag uses HEAD rather than GET, so here we're testing @@ -40,7 +40,7 @@ func TestErrorStuttering(t *testing.T) { // something vaguely resembling the original error, which is why // the code and message have changed. _, err = r.ResolveTag(context.Background(), "foo", "sometag") - assert.ErrorIs(t, err, ociregistry.ErrNameUnknown) + assert.ErrorIs(t, err, oci.ErrNameUnknown) assert.Regexp(t, `404 Not Found: name unknown: repository name not known to registry`, err.Error()) } @@ -56,62 +56,62 @@ func TestNonJSONErrorResponse(t *testing.T) { Insecure: true, }) require.NoError(t, err) - assertStatusCode := func(f func(ctx context.Context, r ociregistry.Interface) error) { + assertStatusCode := func(f func(ctx context.Context, r oci.Interface) error) { err := f(context.Background(), r) - var herr ociregistry.HTTPError + var herr oci.HTTPError ok := errors.As(err, &herr) require.True(t, ok) require.Equal(t, http.StatusTeapot, herr.StatusCode()) } - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { rd, err := r.GetBlob(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") if rd != nil { rd.Close() } return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { rd, err := r.GetBlobRange(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 100, 200) if rd != nil { rd.Close() } return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { rd, err := r.GetManifest(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") if rd != nil { rd.Close() } return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { rd, err := r.GetTag(ctx, "foo/read", "sometag") if rd != nil { rd.Close() } return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveBlob(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveManifest(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveTag(ctx, "foo/read", "sometag") return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { - _, err := r.PushBlob(ctx, "foo/write", ociregistry.Descriptor{ + assertStatusCode(func(ctx context.Context, r oci.Interface) error { + _, err := r.PushBlob(ctx, "foo/write", oci.Descriptor{ MediaType: "application/json", Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", Size: 3, }, strings.NewReader("foo")) return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { w, err := r.PushBlobChunked(ctx, "foo/write", 0) if err != nil { return err @@ -119,7 +119,7 @@ func TestNonJSONErrorResponse(t *testing.T) { w.Close() return nil }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { w, err := r.PushBlobChunkedResume(ctx, "foo/write", "/someid", 3, 0) if err != nil { return err @@ -131,35 +131,35 @@ func TestNonJSONErrorResponse(t *testing.T) { _, err = w.Commit(digest.FromBytes(data)) return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { _, err := r.MountBlob(ctx, "foo/read", "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { - _, err := r.PushManifest(ctx, "foo/write", []byte("something"), "application/json", &ociregistry.PushManifestParameters{ + assertStatusCode(func(ctx context.Context, r oci.Interface) error { + _, err := r.PushManifest(ctx, "foo/write", []byte("something"), "application/json", &oci.PushManifestParameters{ Tags: []string{"sometag"}, }) return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { return r.DeleteBlob(ctx, "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { return r.DeleteManifest(ctx, "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { + assertStatusCode(func(ctx context.Context, r oci.Interface) error { return r.DeleteTag(ctx, "foo/write", "sometag") }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Repositories(ctx, "")) + assertStatusCode(func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Repositories(ctx, "")) return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Tags(ctx, "foo/read", nil)) + assertStatusCode(func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Tags(ctx, "foo/read", nil)) return err }) - assertStatusCode(func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Referrers(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) + assertStatusCode(func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Referrers(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) return err }) } diff --git a/ociregistry/ociclient/lister.go b/ociclient/lister.go similarity index 94% rename from ociregistry/ociclient/lister.go rename to ociclient/lister.go index bc8fab8..4d1b886 100644 --- a/ociregistry/ociclient/lister.go +++ b/ociclient/lister.go @@ -25,10 +25,10 @@ import ( "slices" "strings" + "github.com/jcarter3/oci" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci/internal/ocirequest" ) func (c *client) Repositories(ctx context.Context, startAfter string) iter.Seq2[string, error] { @@ -50,7 +50,7 @@ func (c *client) Repositories(ctx context.Context, startAfter string) iter.Seq2[ }) } -func (c *client) Tags(ctx context.Context, repoName string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { +func (c *client) Tags(ctx context.Context, repoName string, params *oci.TagsParameters) iter.Seq2[string, error] { var startAfter string var limit int if params != nil { @@ -81,7 +81,7 @@ func (c *client) Tags(ctx context.Context, repoName string, params *ociregistry. }) } -func (c *client) Referrers(ctx context.Context, repoName string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { +func (c *client) Referrers(ctx context.Context, repoName string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { var artifactType string if params != nil { artifactType = params.ArtifactType @@ -92,7 +92,7 @@ func (c *client) Referrers(ctx context.Context, repoName string, digest ociregis Digest: string(digest), ListN: 0, ArtifactType: artifactType, - }, false, func(resp *http.Response) ([]ociregistry.Descriptor, error) { + }, false, func(resp *http.Response) ([]oci.Descriptor, error) { body := resp.Body if resp.StatusCode == http.StatusNotFound { body.Close() @@ -105,7 +105,7 @@ func (c *client) Referrers(ctx context.Context, repoName string, digest ociregis // schema. r, err := c.GetTag(ctx, repoName, referrersTag(digest)) if err != nil { - if errors.Is(err, ociregistry.ErrManifestUnknown) { + if errors.Is(err, oci.ErrManifestUnknown) { return nil, nil } return nil, err @@ -128,7 +128,7 @@ func (c *client) Referrers(ctx context.Context, repoName string, digest ociregis // TODO is it OK to assume that the index contains correctly populated // artifact type and attributes fields when we've fallen back to the referrer tags API? // If not, we might have to retrieve all the individual manifests to check that info. - manifests := slices.DeleteFunc(referrersResponse.Manifests, func(desc ociregistry.Descriptor) bool { + manifests := slices.DeleteFunc(referrersResponse.Manifests, func(desc oci.Descriptor) bool { return desc.ArtifactType != artifactType }) return manifests, nil @@ -229,7 +229,7 @@ func nextLink[T any](ctx context.Context, resp *http.Response, initialReq *ocire // referrersTag returns the referrers tag for the given digest, as described // in https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema -func referrersTag(digest ociregistry.Digest) string { +func referrersTag(digest oci.Digest) string { // It's hard to know what the spec means by "with any characters not allowed by tags replaced with -", // because different characters are allowed in different contexts (for example, a dot character // is allowed except when it's at the start. diff --git a/ociregistry/ociclient/reader.go b/ociclient/reader.go similarity index 81% rename from ociregistry/ociclient/reader.go rename to ociclient/reader.go index dd2c272..82b7c4f 100644 --- a/ociregistry/ociclient/reader.go +++ b/ociclient/reader.go @@ -21,12 +21,12 @@ import ( "io" "net/http" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/internal/ocirequest" "github.com/opencontainers/go-digest" ) -func (c *client) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (c *client) GetBlob(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { return c.read(ctx, &ocirequest.Request{ Kind: ocirequest.ReqBlobGet, Repo: repo, @@ -34,7 +34,7 @@ func (c *client) GetBlob(ctx context.Context, repo string, digest ociregistry.Di }) } -func (c *client) GetBlobRange(ctx context.Context, repo string, digest ociregistry.Digest, o0, o1 int64) (_ ociregistry.BlobReader, _err error) { +func (c *client) GetBlobRange(ctx context.Context, repo string, digest oci.Digest, o0, o1 int64) (_ oci.BlobReader, _err error) { if o0 == 0 && o1 < 0 { return c.GetBlob(ctx, repo, digest) } @@ -60,14 +60,14 @@ func (c *client) GetBlobRange(ctx context.Context, repo string, digest ociregist // Fix that either by returning ErrUnsupported or by reading the whole // blob and returning only the required portion. defer closeOnError(&_err, resp.Body) - desc, err := descriptorFromResponse(resp, ociregistry.Digest(rreq.Digest), requireSize) + desc, err := descriptorFromResponse(resp, oci.Digest(rreq.Digest), requireSize) if err != nil { return nil, fmt.Errorf("invalid descriptor in response: %v", err) } return newBlobReaderUnverified(resp.Body, desc), nil } -func (c *client) ResolveBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (c *client) ResolveBlob(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { return c.resolve(ctx, &ocirequest.Request{ Kind: ocirequest.ReqBlobHead, Repo: repo, @@ -75,7 +75,7 @@ func (c *client) ResolveBlob(ctx context.Context, repo string, digest ociregistr }) } -func (c *client) ResolveManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (c *client) ResolveManifest(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { return c.resolve(ctx, &ocirequest.Request{ Kind: ocirequest.ReqManifestHead, Repo: repo, @@ -83,7 +83,7 @@ func (c *client) ResolveManifest(ctx context.Context, repo string, digest ocireg }) } -func (c *client) ResolveTag(ctx context.Context, repo string, tag string) (ociregistry.Descriptor, error) { +func (c *client) ResolveTag(ctx context.Context, repo string, tag string) (oci.Descriptor, error) { return c.resolve(ctx, &ocirequest.Request{ Kind: ocirequest.ReqManifestHead, Repo: repo, @@ -91,20 +91,20 @@ func (c *client) ResolveTag(ctx context.Context, repo string, tag string) (ocire }) } -func (c *client) resolve(ctx context.Context, rreq *ocirequest.Request) (ociregistry.Descriptor, error) { +func (c *client) resolve(ctx context.Context, rreq *ocirequest.Request) (oci.Descriptor, error) { resp, err := c.doRequest(ctx, rreq) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } resp.Body.Close() - desc, err := descriptorFromResponse(resp, ociregistry.Digest(rreq.Digest), requireSize|requireDigest) + desc, err := descriptorFromResponse(resp, oci.Digest(rreq.Digest), requireSize|requireDigest) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor in response: %v", err) + return oci.Descriptor{}, fmt.Errorf("invalid descriptor in response: %v", err) } return desc, nil } -func (c *client) GetManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (c *client) GetManifest(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { return c.read(ctx, &ocirequest.Request{ Kind: ocirequest.ReqManifestGet, Repo: repo, @@ -112,7 +112,7 @@ func (c *client) GetManifest(ctx context.Context, repo string, digest ociregistr }) } -func (c *client) GetTag(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { +func (c *client) GetTag(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { return c.read(ctx, &ocirequest.Request{ Kind: ocirequest.ReqManifestGet, Repo: repo, @@ -132,13 +132,13 @@ func (c *client) GetTag(ctx context.Context, repo string, tagName string) (ocire // a digest when doing a GET on a tag. const inMemThreshold = 128 * 1024 -func (c *client) read(ctx context.Context, rreq *ocirequest.Request) (_ ociregistry.BlobReader, _err error) { +func (c *client) read(ctx context.Context, rreq *ocirequest.Request) (_ oci.BlobReader, _err error) { resp, err := c.doRequest(ctx, rreq) if err != nil { return nil, err } defer closeOnError(&_err, resp.Body) - desc, err := descriptorFromResponse(resp, ociregistry.Digest(rreq.Digest), requireSize) + desc, err := descriptorFromResponse(resp, oci.Digest(rreq.Digest), requireSize) if err != nil { return nil, fmt.Errorf("invalid descriptor in response: %v", err) } @@ -176,7 +176,7 @@ func (c *client) read(ctx context.Context, rreq *ocirequest.Request) (_ ociregis return nil, err } resp1.Body.Close() - desc, err = descriptorFromResponse(resp1, ociregistry.Digest(rreq1.Digest), requireSize|requireDigest) + desc, err = descriptorFromResponse(resp1, oci.Digest(rreq1.Digest), requireSize|requireDigest) if err != nil { return nil, err } diff --git a/ociregistry/ociclient/referrers_test.go b/ociclient/referrers_test.go similarity index 73% rename from ociregistry/ociclient/referrers_test.go rename to ociclient/referrers_test.go index 5100296..beba3dd 100644 --- a/ociregistry/ociclient/referrers_test.go +++ b/ociclient/referrers_test.go @@ -10,15 +10,15 @@ import ( "strings" "testing" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/require" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociclient" - "github.com/jcarter3/oci/ociregistry/ocidebug" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci/ociclient" + "github.com/jcarter3/oci/ocidebug" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ociserver" ) func TestReferrersFallback(t *testing.T) { @@ -39,7 +39,7 @@ func TestReferrersFallback(t *testing.T) { config := pushScratchConfig(t, client, repo) // Push a manifest to refer to. - subject := pushManifest(t, client, repo, "sometag", &ociregistry.Manifest{ + subject := pushManifest(t, client, repo, "sometag", &oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: withMediaType(config, "subject/mediatype"), }, ocispec.MediaTypeImageManifest) @@ -50,7 +50,7 @@ func TestReferrersFallback(t *testing.T) { // Then push some manifests that refer to it and update the index at the same time. for i := range 5 { artifactType := fmt.Sprintf("referrer/%d", i) - desc := pushManifest(t, client, repo, "", &ociregistry.Manifest{ + desc := pushManifest(t, client, repo, "", &oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Subject: &subject, Config: withMediaType(config, artifactType), @@ -63,7 +63,7 @@ func TestReferrersFallback(t *testing.T) { pushManifest(t, client, repo, strings.ReplaceAll(string(subject.Digest), ":", "-"), index, ocispec.MediaTypeImageIndex) // Then ask for the referrers. - var got []ociregistry.Descriptor + var got []oci.Descriptor for desc, err := range client.Referrers(ctx, repo, subject.Digest, nil) { require.NoError(t, err) got = append(got, desc) @@ -72,19 +72,19 @@ func TestReferrersFallback(t *testing.T) { // Check that artifact type filtering still works OK. got = nil - for desc, err := range client.Referrers(ctx, repo, subject.Digest, &ociregistry.ReferrersParameters{ArtifactType: "referrer/2"}) { + for desc, err := range client.Referrers(ctx, repo, subject.Digest, &oci.ReferrersParameters{ArtifactType: "referrer/2"}) { require.NoError(t, err) got = append(got, desc) } - require.Equal(t, []ociregistry.Descriptor{index.Manifests[2]}, got) + require.Equal(t, []oci.Descriptor{index.Manifests[2]}, got) } -func withMediaType(desc ociregistry.Descriptor, mediaType string) ociregistry.Descriptor { +func withMediaType(desc oci.Descriptor, mediaType string) oci.Descriptor { desc.MediaType = mediaType return desc } -func pushScratchConfig(t *testing.T, client ociregistry.Interface, repo string) ociregistry.Descriptor { +func pushScratchConfig(t *testing.T, client oci.Interface, repo string) oci.Descriptor { content := []byte("{}") desc := ocispec.Descriptor{ Digest: digest.FromBytes(content), @@ -95,12 +95,12 @@ func pushScratchConfig(t *testing.T, client ociregistry.Interface, repo string) return desc } -func pushManifest(t *testing.T, client ociregistry.Interface, repo, tag string, content any, mediaType string) ociregistry.Descriptor { +func pushManifest(t *testing.T, client oci.Interface, repo, tag string, content any, mediaType string) oci.Descriptor { data, err := json.Marshal(content) require.NoError(t, err) - var params *ociregistry.PushManifestParameters + var params *oci.PushManifestParameters if tag != "" { - params = &ociregistry.PushManifestParameters{ + params = &oci.PushManifestParameters{ Tags: []string{tag}, } } @@ -109,7 +109,7 @@ func pushManifest(t *testing.T, client ociregistry.Interface, repo, tag string, return desc } -func mustNewOCIClient(srvURL string, opts *ociclient.Options) ociregistry.Interface { +func mustNewOCIClient(srvURL string, opts *ociclient.Options) oci.Interface { if opts == nil { opts = new(ociclient.Options) } diff --git a/ociregistry/ociclient/referrerstag_test.go b/ociclient/referrerstag_test.go similarity index 94% rename from ociregistry/ociclient/referrerstag_test.go rename to ociclient/referrerstag_test.go index 1551285..66528ec 100644 --- a/ociregistry/ociclient/referrerstag_test.go +++ b/ociclient/referrerstag_test.go @@ -3,12 +3,12 @@ package ociclient import ( "testing" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" "github.com/stretchr/testify/require" ) var referrersTagTests = []struct { - digest ociregistry.Digest + digest oci.Digest want string }{{ // Test case from the distribution spec. diff --git a/ociregistry/ociclient/writer.go b/ociclient/writer.go similarity index 88% rename from ociregistry/ociclient/writer.go rename to ociclient/writer.go index 076646a..f935066 100644 --- a/ociregistry/ociclient/writer.go +++ b/ociclient/writer.go @@ -25,26 +25,26 @@ import ( "strings" "sync" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" - "github.com/jcarter3/oci/ociregistry/ociauth" + "github.com/jcarter3/oci/internal/ocirequest" + "github.com/jcarter3/oci/ociauth" ) -// This file implements the ociregistry.Writer methods. +// This file implements the oci.Writer methods. -func (c *client) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (c *client) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { if mediaType == "" { - return ociregistry.Descriptor{}, fmt.Errorf("PushManifest called with empty mediaType") + return oci.Descriptor{}, fmt.Errorf("PushManifest called with empty mediaType") } - var dig ociregistry.Digest + var dig oci.Digest if params != nil && params.Digest != "" { dig = params.Digest } else { dig = digest.FromBytes(contents) } - desc := ociregistry.Descriptor{ + desc := oci.Descriptor{ Digest: dig, Size: int64(len(contents)), MediaType: mediaType, @@ -85,7 +85,7 @@ func (c *client) PushManifest(ctx context.Context, repo string, contents []byte, } _, err = c.putManifest(ctx, rreq, desc) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("creating tag %s failed: %w", tag, err) + return oci.Descriptor{}, fmt.Errorf("creating tag %s failed: %w", tag, err) } } } @@ -93,7 +93,7 @@ func (c *client) PushManifest(ctx context.Context, repo string, contents []byte, return desc, nil } -func (c *client) putManifest(ctx context.Context, rreq *ocirequest.Request, desc ociregistry.Descriptor) ([]string, error) { +func (c *client) putManifest(ctx context.Context, rreq *ocirequest.Request, desc oci.Descriptor) ([]string, error) { req, err := newRequest(ctx, rreq, bytes.NewReader(desc.Data)) if err != nil { return nil, err @@ -115,7 +115,7 @@ func (c *client) putManifest(ctx context.Context, rreq *ocirequest.Request, desc return tags, nil } -func (c *client) MountBlob(ctx context.Context, fromRepo, toRepo string, dig ociregistry.Digest) (ociregistry.Descriptor, error) { +func (c *client) MountBlob(ctx context.Context, fromRepo, toRepo string, dig oci.Digest) (oci.Descriptor, error) { rreq := &ocirequest.Request{ Kind: ocirequest.ReqBlobMount, Repo: toRepo, @@ -124,20 +124,20 @@ func (c *client) MountBlob(ctx context.Context, fromRepo, toRepo string, dig oci } resp, err := c.doRequest(ctx, rreq, http.StatusCreated, http.StatusAccepted) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } resp.Body.Close() if resp.StatusCode == http.StatusAccepted { // Mount isn't supported and technically the upload session has begun, // but we aren't in a great position to be able to continue it, so let's just // return Unsupported. - return ociregistry.Descriptor{}, fmt.Errorf("registry does not support mounts: %w", ociregistry.ErrUnsupported) + return oci.Descriptor{}, fmt.Errorf("registry does not support mounts: %w", oci.ErrUnsupported) } // TODO: is it OK to omit the size from the returned descriptor here? return descriptorFromResponse(resp, dig, requireDigest) } -func (c *client) PushBlob(ctx context.Context, repo string, desc ociregistry.Descriptor, r io.Reader) (_ ociregistry.Descriptor, _err error) { +func (c *client) PushBlob(ctx context.Context, repo string, desc oci.Descriptor, r io.Reader) (_ oci.Descriptor, _err error) { // TODO use the single-post blob-upload method (ReqBlobUploadBlob) // See: // https://github.com/distribution/distribution/issues/4065 @@ -148,16 +148,16 @@ func (c *client) PushBlob(ctx context.Context, repo string, desc ociregistry.Des } req, err := newRequest(ctx, rreq, nil) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } resp, err := c.do(req, http.StatusAccepted) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } resp.Body.Close() location, err := locationFromResponse(resp) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } // We've got the upload location. Now PUT the content. @@ -169,7 +169,7 @@ func (c *client) PushBlob(ctx context.Context, repo string, desc ociregistry.Des // specific to the ociserver implementation in this case. req, err = http.NewRequestWithContext(ctx, "PUT", "", r) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } req.URL = urlWithDigest(location, string(desc.Digest)) req.ContentLength = desc.Size @@ -178,7 +178,7 @@ func (c *client) PushBlob(ctx context.Context, repo string, desc ociregistry.Des req.Header.Set("Content-Range", ocirequest.RangeString(0, desc.Size)) resp, err = c.do(req, http.StatusCreated) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } defer closeOnError(&_err, resp.Body) resp.Body.Close() @@ -190,7 +190,7 @@ func (c *client) PushBlob(ctx context.Context, repo string, desc ociregistry.Des // TODO: make this default configurable. const defaultChunkSize = 64 * 1024 -func (c *client) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (ociregistry.BlobWriter, error) { +func (c *client) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (oci.BlobWriter, error) { if chunkSize <= 0 { chunkSize = defaultChunkSize } @@ -222,7 +222,7 @@ func (c *client) PushBlobChunked(ctx context.Context, repo string, chunkSize int }, nil } -func (c *client) PushBlobChunkedResume(ctx context.Context, repo string, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (c *client) PushBlobChunkedResume(ctx context.Context, repo string, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { if id == "" { return nil, fmt.Errorf("id must be non-empty to resume a chunked upload") } @@ -348,7 +348,7 @@ func (w *blobWriter) Write(buf []byte) (int, error) { // flush flushes any outstanding upload data to the server. // If commitDigest is non-empty, this is the final segment of data in the blob: // the blob is being committed and the digest should hold the digest of the entire blob content. -func (w *blobWriter) flush(buf []byte, commitDigest ociregistry.Digest) error { +func (w *blobWriter) flush(buf []byte, commitDigest oci.Digest) error { if commitDigest == "" && len(buf)+len(w.chunk) == 0 { return nil } @@ -432,16 +432,16 @@ func (w *blobWriter) ID() string { return w.location.String() } -func (w *blobWriter) Commit(digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (w *blobWriter) Commit(digest oci.Digest) (oci.Descriptor, error) { if digest == "" { - return ociregistry.Descriptor{}, fmt.Errorf("cannot commit with an empty digest") + return oci.Descriptor{}, fmt.Errorf("cannot commit with an empty digest") } w.mu.Lock() defer w.mu.Unlock() if err := w.flush(nil, digest); err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("cannot flush data before commit: %w", err) + return oci.Descriptor{}, fmt.Errorf("cannot flush data before commit: %w", err) } - return ociregistry.Descriptor{ + return oci.Descriptor{ MediaType: "application/octet-stream", Size: w.size, Digest: digest, diff --git a/ociregistry/ocidebug/debug.go b/ocidebug/debug.go similarity index 83% rename from ociregistry/ocidebug/debug.go rename to ocidebug/debug.go index f2d50c3..08419fa 100644 --- a/ociregistry/ocidebug/debug.go +++ b/ocidebug/debug.go @@ -24,12 +24,12 @@ import ( "log" "sync/atomic" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) -// New returns a new [ociregistry.Interface] that wraps r and logs all operations +// New returns a new [oci.Interface] that wraps r and logs all operations // using the given logf function. If logf is nil, [log.Printf] is used. -func New(r ociregistry.Interface, logf func(f string, a ...any)) ociregistry.Interface { +func New(r oci.Interface, logf func(f string, a ...any)) oci.Interface { if logf == nil { logf = log.Printf } @@ -43,18 +43,18 @@ var blobWriterID int32 type logger struct { logf func(f string, a ...any) - r ociregistry.Interface - *ociregistry.Funcs + r oci.Interface + *oci.Funcs } -func (r *logger) DeleteBlob(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (r *logger) DeleteBlob(ctx context.Context, repoName string, digest oci.Digest) error { r.logf("DeleteBlob %s %s {", repoName, digest) err := r.r.DeleteBlob(ctx, repoName, digest) r.logf("} -> %v", err) return err } -func (r *logger) DeleteManifest(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (r *logger) DeleteManifest(ctx context.Context, repoName string, digest oci.Digest) error { r.logf("DeleteManifest %s %s {", repoName, digest) err := r.r.DeleteManifest(ctx, repoName, digest) r.logf("} -> %v", err) @@ -68,42 +68,42 @@ func (r *logger) DeleteTag(ctx context.Context, repoName string, tagName string) return err } -func (r *logger) GetBlob(ctx context.Context, repoName string, dig ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *logger) GetBlob(ctx context.Context, repoName string, dig oci.Digest) (oci.BlobReader, error) { r.logf("GetBlob %s %s {", repoName, dig) rd, err := r.r.GetBlob(ctx, repoName, dig) r.logf("} -> %T, %v", rd, err) return rd, err } -func (r *logger) GetBlobRange(ctx context.Context, repoName string, dig ociregistry.Digest, o0, o1 int64) (ociregistry.BlobReader, error) { +func (r *logger) GetBlobRange(ctx context.Context, repoName string, dig oci.Digest, o0, o1 int64) (oci.BlobReader, error) { r.logf("GetBlob %s %s [%d, %d] {", repoName, dig, o0, o1) rd, err := r.r.GetBlobRange(ctx, repoName, dig, o0, o1) r.logf("} -> %T, %v", rd, err) return rd, err } -func (r *logger) GetManifest(ctx context.Context, repoName string, dig ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *logger) GetManifest(ctx context.Context, repoName string, dig oci.Digest) (oci.BlobReader, error) { r.logf("GetManifest %s %s {", repoName, dig) rd, err := r.r.GetManifest(ctx, repoName, dig) r.logf("} -> %T, %v", rd, err) return rd, err } -func (r *logger) GetTag(ctx context.Context, repoName string, tagName string) (ociregistry.BlobReader, error) { +func (r *logger) GetTag(ctx context.Context, repoName string, tagName string) (oci.BlobReader, error) { r.logf("GetTag %s %s {", repoName, tagName) rd, err := r.r.GetTag(ctx, repoName, tagName) r.logf("} -> %T, %v", rd, err) return rd, err } -func (r *logger) MountBlob(ctx context.Context, fromRepo, toRepo string, dig ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *logger) MountBlob(ctx context.Context, fromRepo, toRepo string, dig oci.Digest) (oci.Descriptor, error) { r.logf("MountBlob from=%s to=%s digest=%s {", fromRepo, toRepo, dig) desc, err := r.r.MountBlob(ctx, fromRepo, toRepo, dig) r.logf("} -> %#v, %v", desc, err) return desc, err } -func (r *logger) PushBlob(ctx context.Context, repoName string, desc ociregistry.Descriptor, content io.Reader) (ociregistry.Descriptor, error) { +func (r *logger) PushBlob(ctx context.Context, repoName string, desc oci.Descriptor, content io.Reader) (oci.Descriptor, error) { r.logf("PushBlob %s %#v %T {", repoName, desc, content) desc, err := r.r.PushBlob(ctx, repoName, desc, content) if err != nil { @@ -114,7 +114,7 @@ func (r *logger) PushBlob(ctx context.Context, repoName string, desc ociregistry return desc, err } -func (r *logger) PushBlobChunked(ctx context.Context, repoName string, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *logger) PushBlobChunked(ctx context.Context, repoName string, chunkSize int) (oci.BlobWriter, error) { bwid := fmt.Sprintf("bw%d", atomic.AddInt32(&blobWriterID, 1)) r.logf("PushBlobChunked %s chunkSize=%d {", repoName, chunkSize) w, err := r.r.PushBlobChunked(ctx, repoName, chunkSize) @@ -126,7 +126,7 @@ func (r *logger) PushBlobChunked(ctx context.Context, repoName string, chunkSize }, err } -func (r *logger) PushBlobChunkedResume(ctx context.Context, repoName, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *logger) PushBlobChunkedResume(ctx context.Context, repoName, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { bwid := fmt.Sprintf("bw%d", atomic.AddInt32(&blobWriterID, 1)) r.logf("PushBlobChunkedResume %s id=%q offset=%d chunkSize=%d {", repoName, id, offset, chunkSize) w, err := r.r.PushBlobChunkedResume(ctx, repoName, id, offset, chunkSize) @@ -138,7 +138,7 @@ func (r *logger) PushBlobChunkedResume(ctx context.Context, repoName, id string, }, err } -func (r *logger) PushManifest(ctx context.Context, repoName string, data []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (r *logger) PushManifest(ctx context.Context, repoName string, data []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { r.logf("PushManifest %s params=%+v mediaType=%q data=%q {", repoName, params, mediaType, data) desc, err := r.r.PushManifest(ctx, repoName, data, mediaType, params) if err != nil { @@ -149,7 +149,7 @@ func (r *logger) PushManifest(ctx context.Context, repoName string, data []byte, return desc, err } -func (r *logger) Referrers(ctx context.Context, repoName string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { +func (r *logger) Referrers(ctx context.Context, repoName string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { var artifactType string if params != nil { artifactType = params.ArtifactType @@ -169,7 +169,7 @@ func (r *logger) Repositories(ctx context.Context, startAfter string) iter.Seq2[ ) } -func (r *logger) Tags(ctx context.Context, repoName string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { +func (r *logger) Tags(ctx context.Context, repoName string, params *oci.TagsParameters) iter.Seq2[string, error] { var startAfter string var limit int if params != nil { @@ -183,7 +183,7 @@ func (r *logger) Tags(ctx context.Context, repoName string, params *ociregistry. ) } -func (r *logger) ResolveBlob(ctx context.Context, repoName string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *logger) ResolveBlob(ctx context.Context, repoName string, digest oci.Digest) (oci.Descriptor, error) { r.logf("ResolveBlob %s %s {", repoName, digest) desc, err := r.r.ResolveBlob(ctx, repoName, digest) if err != nil { @@ -194,7 +194,7 @@ func (r *logger) ResolveBlob(ctx context.Context, repoName string, digest ocireg return desc, err } -func (r *logger) ResolveManifest(ctx context.Context, repoName string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *logger) ResolveManifest(ctx context.Context, repoName string, digest oci.Digest) (oci.Descriptor, error) { r.logf("ResolveManifest %s %s {", repoName, digest) desc, err := r.r.ResolveManifest(ctx, repoName, digest) if err != nil { @@ -205,7 +205,7 @@ func (r *logger) ResolveManifest(ctx context.Context, repoName string, digest oc return desc, err } -func (r *logger) ResolveTag(ctx context.Context, repoName string, tagName string) (ociregistry.Descriptor, error) { +func (r *logger) ResolveTag(ctx context.Context, repoName string, tagName string) (oci.Descriptor, error) { r.logf("ResolveTag %s %s {", repoName, tagName) desc, err := r.r.ResolveTag(ctx, repoName, tagName) if err != nil { @@ -219,7 +219,7 @@ func (r *logger) ResolveTag(ctx context.Context, repoName string, tagName string type blobWriter struct { id string r *logger - w ociregistry.BlobWriter + w oci.BlobWriter } func (w blobWriter) logf(f string, a ...any) { @@ -256,7 +256,7 @@ func (w blobWriter) Close() error { return err } -func (w blobWriter) Commit(digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (w blobWriter) Commit(digest oci.Digest) (oci.Descriptor, error) { w.logf("Commit %q {", digest) desc, err := w.w.Commit(digest) w.logf("} -> %#v, %v", desc, err) diff --git a/ociregistry/ocifilter/immutable.go b/ocifilter/immutable.go similarity index 73% rename from ociregistry/ocifilter/immutable.go rename to ocifilter/immutable.go index 2c01fbd..205499c 100644 --- a/ociregistry/ocifilter/immutable.go +++ b/ocifilter/immutable.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package ocifilter implements "filter" functions that wrap or combine ociregistry +// Package ocifilter implements "filter" functions that wrap or combine oci // implementations in different ways. package ocifilter @@ -20,22 +20,22 @@ import ( "context" "fmt" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" ) // Immutable returns a registry wrap r but only allows content to be // added but not changed once added: nothing can be deleted and tags // can't be changed. -func Immutable(r ociregistry.Interface) ociregistry.Interface { +func Immutable(r oci.Interface) oci.Interface { return immutable{r} } type immutable struct { - ociregistry.Interface + oci.Interface } -func (r immutable) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (r immutable) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { var tags []string if params != nil { tags = params.Tags @@ -43,7 +43,7 @@ func (r immutable) PushManifest(ctx context.Context, repo string, contents []byt if len(tags) == 0 { return r.Interface.PushManifest(ctx, repo, contents, mediaType, params) } - var dig ociregistry.Digest + var dig oci.Digest if params != nil && params.Digest != "" { dig = params.Digest } else { @@ -56,12 +56,12 @@ func (r immutable) PushManifest(ctx context.Context, repo string, contents []byt // We're trying to push exactly the same content. That's OK. continue } - return ociregistry.Descriptor{}, fmt.Errorf("this store is immutable: %w", ociregistry.ErrDenied) + return oci.Descriptor{}, fmt.Errorf("this store is immutable: %w", oci.ErrDenied) } } desc, err := r.Interface.PushManifest(ctx, repo, contents, mediaType, params) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } // We've pushed the tags but someone else might also have pushed them at the same time. // UNFORTUNATELY if there was a race, then there's a small window in time where @@ -69,24 +69,24 @@ func (r immutable) PushManifest(ctx context.Context, repo string, contents []byt for _, tag := range tags { tagDesc, err := r.ResolveTag(ctx, repo, tag) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("cannot resolve tag %q that's just been pushed: %v", tag, err) + return oci.Descriptor{}, fmt.Errorf("cannot resolve tag %q that's just been pushed: %v", tag, err) } if tagDesc.Digest != dig { // We lost the race. - return ociregistry.Descriptor{}, fmt.Errorf("this store is immutable: %w", ociregistry.ErrDenied) + return oci.Descriptor{}, fmt.Errorf("this store is immutable: %w", oci.ErrDenied) } } return desc, nil } -func (r immutable) DeleteBlob(ctx context.Context, repo string, digest ociregistry.Digest) error { - return ociregistry.ErrDenied +func (r immutable) DeleteBlob(ctx context.Context, repo string, digest oci.Digest) error { + return oci.ErrDenied } -func (r immutable) DeleteManifest(ctx context.Context, repo string, digest ociregistry.Digest) error { - return ociregistry.ErrDenied +func (r immutable) DeleteManifest(ctx context.Context, repo string, digest oci.Digest) error { + return oci.ErrDenied } func (r immutable) DeleteTag(ctx context.Context, repo string, name string) error { - return ociregistry.ErrDenied + return oci.ErrDenied } diff --git a/ociregistry/ocifilter/readonly.go b/ocifilter/readonly.go similarity index 84% rename from ociregistry/ocifilter/readonly.go rename to ocifilter/readonly.go index 55e0107..0d7a4e5 100644 --- a/ociregistry/ocifilter/readonly.go +++ b/ocifilter/readonly.go @@ -14,20 +14,20 @@ package ocifilter -import "github.com/jcarter3/oci/ociregistry" +import "github.com/jcarter3/oci" // ReadOnly returns a registry implementation that returns // an "operation unsupported" error from all entry points that // mutate the registry. -func ReadOnly(r ociregistry.Interface) ociregistry.Interface { +func ReadOnly(r oci.Interface) oci.Interface { // One level deeper so the Reader and Lister values take precedence, // following Go's shallower-method-wins rules. type deeper struct { - *ociregistry.Funcs + *oci.Funcs } return struct { - ociregistry.Reader - ociregistry.Lister + oci.Reader + oci.Lister deeper }{ Reader: r, diff --git a/ociregistry/ocifilter/select.go b/ocifilter/select.go similarity index 71% rename from ociregistry/ocifilter/select.go rename to ocifilter/select.go index 99a4dcb..edfaca6 100644 --- a/ociregistry/ocifilter/select.go +++ b/ocifilter/select.go @@ -19,23 +19,23 @@ import ( "io" "iter" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // AccessKind represents the type of access being performed on a registry. type AccessKind int const ( - // AccessRead represents [ociregistry.Reader] methods. + // AccessRead represents [oci.Reader] methods. AccessRead AccessKind = iota - // AccessWrite represents [ociregistry.Writer] methods. + // AccessWrite represents [oci.Writer] methods. AccessWrite - // AccessDelete represents [ociregistry.Deleter] methods. + // AccessDelete represents [oci.Deleter] methods. AccessDelete - // AccessList represents [ociregistry.Lister] methods. + // AccessList represents [oci.Lister] methods. AccessList ) @@ -50,7 +50,7 @@ const ( // // When invoking the Repositories method, check is invoked for each repository in // the iteration - the repository will be omitted if check returns an error. -func AccessChecker(r ociregistry.Interface, check func(repoName string, access AccessKind) error) ociregistry.Interface { +func AccessChecker(r oci.Interface, check func(repoName string, access AccessKind) error) oci.Interface { return &accessCheckerRegistry{ check: check, r: r, @@ -61,9 +61,9 @@ type accessCheckerRegistry struct { // Embed Funcs rather than the interface directly so that // if new methods are added and selectRegistry isn't updated, // we fall back to returning an error rather than passing through the method. - *ociregistry.Funcs + *oci.Funcs check func(repoName string, kind AccessKind) error - r ociregistry.Interface + r oci.Interface } // Select returns a wrapper for r that provides only @@ -71,116 +71,116 @@ type accessCheckerRegistry struct { // // Requests for disallowed repositories will return ErrNameUnknown // errors on read and ErrDenied on write. -func Select(r ociregistry.Interface, allow func(repoName string) bool) ociregistry.Interface { +func Select(r oci.Interface, allow func(repoName string) bool) oci.Interface { return AccessChecker(r, func(repoName string, access AccessKind) error { if allow(repoName) { return nil } if access == AccessWrite { - return ociregistry.ErrDenied + return oci.ErrDenied } if access == AccessList && repoName == "*" { return nil } - return ociregistry.ErrNameUnknown + return oci.ErrNameUnknown }) } -func (r *accessCheckerRegistry) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *accessCheckerRegistry) GetBlob(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { if err := r.check(repo, AccessRead); err != nil { return nil, err } return r.r.GetBlob(ctx, repo, digest) } -func (r *accessCheckerRegistry) GetBlobRange(ctx context.Context, repo string, digest ociregistry.Digest, offset0, offset1 int64) (ociregistry.BlobReader, error) { +func (r *accessCheckerRegistry) GetBlobRange(ctx context.Context, repo string, digest oci.Digest, offset0, offset1 int64) (oci.BlobReader, error) { if err := r.check(repo, AccessRead); err != nil { return nil, err } return r.r.GetBlobRange(ctx, repo, digest, offset0, offset1) } -func (r *accessCheckerRegistry) GetManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *accessCheckerRegistry) GetManifest(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { if err := r.check(repo, AccessRead); err != nil { return nil, err } return r.r.GetManifest(ctx, repo, digest) } -func (r *accessCheckerRegistry) GetTag(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { +func (r *accessCheckerRegistry) GetTag(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { if err := r.check(repo, AccessRead); err != nil { return nil, err } return r.r.GetTag(ctx, repo, tagName) } -func (r *accessCheckerRegistry) ResolveBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) ResolveBlob(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { if err := r.check(repo, AccessRead); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.ResolveBlob(ctx, repo, digest) } -func (r *accessCheckerRegistry) ResolveManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) ResolveManifest(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { if err := r.check(repo, AccessRead); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.ResolveManifest(ctx, repo, digest) } -func (r *accessCheckerRegistry) ResolveTag(ctx context.Context, repo string, tagName string) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) ResolveTag(ctx context.Context, repo string, tagName string) (oci.Descriptor, error) { if err := r.check(repo, AccessRead); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.ResolveTag(ctx, repo, tagName) } -func (r *accessCheckerRegistry) PushBlob(ctx context.Context, repo string, desc ociregistry.Descriptor, rd io.Reader) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) PushBlob(ctx context.Context, repo string, desc oci.Descriptor, rd io.Reader) (oci.Descriptor, error) { if err := r.check(repo, AccessWrite); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.PushBlob(ctx, repo, desc, rd) } -func (r *accessCheckerRegistry) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *accessCheckerRegistry) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (oci.BlobWriter, error) { if err := r.check(repo, AccessWrite); err != nil { return nil, err } return r.r.PushBlobChunked(ctx, repo, chunkSize) } -func (r *accessCheckerRegistry) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *accessCheckerRegistry) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { if err := r.check(repo, AccessWrite); err != nil { return nil, err } return r.r.PushBlobChunkedResume(ctx, repo, id, offset, chunkSize) } -func (r *accessCheckerRegistry) MountBlob(ctx context.Context, fromRepo, toRepo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) MountBlob(ctx context.Context, fromRepo, toRepo string, digest oci.Digest) (oci.Descriptor, error) { if err := r.check(fromRepo, AccessRead); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } if err := r.check(toRepo, AccessWrite); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.MountBlob(ctx, fromRepo, toRepo, digest) } -func (r *accessCheckerRegistry) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (r *accessCheckerRegistry) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { if err := r.check(repo, AccessWrite); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return r.r.PushManifest(ctx, repo, contents, mediaType, params) } -func (r *accessCheckerRegistry) DeleteBlob(ctx context.Context, repo string, digest ociregistry.Digest) error { +func (r *accessCheckerRegistry) DeleteBlob(ctx context.Context, repo string, digest oci.Digest) error { if err := r.check(repo, AccessDelete); err != nil { return err } return r.r.DeleteBlob(ctx, repo, digest) } -func (r *accessCheckerRegistry) DeleteManifest(ctx context.Context, repo string, digest ociregistry.Digest) error { +func (r *accessCheckerRegistry) DeleteManifest(ctx context.Context, repo string, digest oci.Digest) error { if err := r.check(repo, AccessDelete); err != nil { return err } @@ -196,7 +196,7 @@ func (r *accessCheckerRegistry) DeleteTag(ctx context.Context, repo string, name func (r *accessCheckerRegistry) Repositories(ctx context.Context, startAfter string) iter.Seq2[string, error] { if err := r.check("*", AccessList); err != nil { - return ociregistry.ErrorSeq[string](err) + return oci.ErrorSeq[string](err) } return func(yield func(string, error) bool) { for repo, err := range r.r.Repositories(ctx, startAfter) { @@ -214,16 +214,16 @@ func (r *accessCheckerRegistry) Repositories(ctx context.Context, startAfter str } } -func (r *accessCheckerRegistry) Tags(ctx context.Context, repo string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { +func (r *accessCheckerRegistry) Tags(ctx context.Context, repo string, params *oci.TagsParameters) iter.Seq2[string, error] { if err := r.check(repo, AccessList); err != nil { - return ociregistry.ErrorSeq[string](err) + return oci.ErrorSeq[string](err) } return r.r.Tags(ctx, repo, params) } -func (r *accessCheckerRegistry) Referrers(ctx context.Context, repo string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { +func (r *accessCheckerRegistry) Referrers(ctx context.Context, repo string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { if err := r.check(repo, AccessList); err != nil { - return ociregistry.ErrorSeq[ociregistry.Descriptor](err) + return oci.ErrorSeq[oci.Descriptor](err) } return r.r.Referrers(ctx, repo, digest, params) } diff --git a/ociregistry/ocifilter/select_test.go b/ocifilter/select_test.go similarity index 74% rename from ociregistry/ocifilter/select_test.go rename to ocifilter/select_test.go index cbd85c3..231e65d 100644 --- a/ociregistry/ocifilter/select_test.go +++ b/ocifilter/select_test.go @@ -20,12 +20,12 @@ import ( "strings" "testing" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ocimem" + "github.com/jcarter3/oci/ocimem" ) func TestAccessCheckerErrorReturn(t *testing.T) { @@ -41,10 +41,10 @@ func TestAccessCheckerErrorReturn(t *testing.T) { } func TestAccessCheckerAccessRequest(t *testing.T) { - assertAccess := func(wantAccess []accessCheck, do func(ctx context.Context, r ociregistry.Interface) error) { + assertAccess := func(wantAccess []accessCheck, do func(ctx context.Context, r oci.Interface) error) { testErr := errors.New("some error") var gotAccess []accessCheck - r := AccessChecker(&ociregistry.Funcs{ + r := AccessChecker(&oci.Funcs{ NewError: func(ctx context.Context, methodName, repo string) error { return testErr }, @@ -58,13 +58,13 @@ func TestAccessCheckerAccessRequest(t *testing.T) { } assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { _, err := r.GetBlob(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { rd, err := r.GetBlobRange(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 100, 200) if rd != nil { rd.Close() @@ -74,7 +74,7 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { rd, err := r.GetManifest(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") if rd != nil { rd.Close() @@ -84,7 +84,7 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { rd, err := r.GetTag(ctx, "foo/read", "sometag") if rd != nil { rd.Close() @@ -94,29 +94,29 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveBlob(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveManifest(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) assertAccess([]accessCheck{ {"foo/read", AccessRead}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { _, err := r.ResolveTag(ctx, "foo/read", "sometag") return err }) assertAccess([]accessCheck{ {"foo/write", AccessWrite}, - }, func(ctx context.Context, r ociregistry.Interface) error { - _, err := r.PushBlob(ctx, "foo/write", ociregistry.Descriptor{ + }, func(ctx context.Context, r oci.Interface) error { + _, err := r.PushBlob(ctx, "foo/write", oci.Descriptor{ MediaType: "application/json", Digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", Size: 3, @@ -126,7 +126,7 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/write", AccessWrite}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { w, err := r.PushBlobChunked(ctx, "foo/write", 0) if err != nil { return err @@ -137,7 +137,7 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/write", AccessWrite}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { w, err := r.PushBlobChunkedResume(ctx, "foo/write", "/someid", 3, 0) if err != nil { return err @@ -153,15 +153,15 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/read", AccessRead}, {"foo/write", AccessWrite}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { _, err := r.MountBlob(ctx, "foo/read", "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") return err }) assertAccess([]accessCheck{ {"foo/write", AccessWrite}, - }, func(ctx context.Context, r ociregistry.Interface) error { - _, err := r.PushManifest(ctx, "foo/write", []byte("something"), "application/json", &ociregistry.PushManifestParameters{ + }, func(ctx context.Context, r oci.Interface) error { + _, err := r.PushManifest(ctx, "foo/write", []byte("something"), "application/json", &oci.PushManifestParameters{ Tags: []string{"sometag"}, }) return err @@ -169,40 +169,40 @@ func TestAccessCheckerAccessRequest(t *testing.T) { assertAccess([]accessCheck{ {"foo/write", AccessDelete}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { return r.DeleteBlob(ctx, "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) assertAccess([]accessCheck{ {"foo/write", AccessDelete}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { return r.DeleteManifest(ctx, "foo/write", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") }) assertAccess([]accessCheck{ {"foo/write", AccessDelete}, - }, func(ctx context.Context, r ociregistry.Interface) error { + }, func(ctx context.Context, r oci.Interface) error { return r.DeleteTag(ctx, "foo/write", "sometag") }) assertAccess([]accessCheck{ {"*", AccessList}, - }, func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Repositories(ctx, "")) + }, func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Repositories(ctx, "")) return err }) assertAccess([]accessCheck{ {"foo/read", AccessList}, - }, func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Tags(ctx, "foo/read", nil)) + }, func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Tags(ctx, "foo/read", nil)) return err }) assertAccess([]accessCheck{ {"foo/read", AccessList}, - }, func(ctx context.Context, r ociregistry.Interface) error { - _, err := ociregistry.All(r.Referrers(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) + }, func(ctx context.Context, r oci.Interface) error { + _, err := oci.All(r.Referrers(ctx, "foo/read", "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", nil)) return err }) } diff --git a/ociregistry/ocifilter/sub.go b/ocifilter/sub.go similarity index 80% rename from ociregistry/ocifilter/sub.go rename to ocifilter/sub.go index 1f6a32c..7dcf617 100644 --- a/ociregistry/ocifilter/sub.go +++ b/ocifilter/sub.go @@ -21,8 +21,8 @@ import ( "path" "strings" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociauth" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociauth" ) // Sub returns r wrapped so that it addresses only @@ -44,7 +44,7 @@ import ( // // b/c // d -func Sub(r ociregistry.Interface, pathPrefix string) ociregistry.Interface { +func Sub(r oci.Interface, pathPrefix string) oci.Interface { if pathPrefix == "" { return r } @@ -57,52 +57,52 @@ func Sub(r ociregistry.Interface, pathPrefix string) ociregistry.Interface { // TODO adjust any auth scopes in the context as they pass through. type subRegistry struct { - *ociregistry.Funcs + *oci.Funcs prefix string - r ociregistry.Interface + r oci.Interface } -func (r *subRegistry) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *subRegistry) GetBlob(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { ctx = r.mapScopes(ctx) return r.r.GetBlob(ctx, r.repo(repo), digest) } -func (r *subRegistry) GetBlobRange(ctx context.Context, repo string, digest ociregistry.Digest, offset0, offset1 int64) (ociregistry.BlobReader, error) { +func (r *subRegistry) GetBlobRange(ctx context.Context, repo string, digest oci.Digest, offset0, offset1 int64) (oci.BlobReader, error) { ctx = r.mapScopes(ctx) return r.r.GetBlobRange(ctx, r.repo(repo), digest, offset0, offset1) } -func (r *subRegistry) GetManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *subRegistry) GetManifest(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { ctx = r.mapScopes(ctx) return r.r.GetManifest(ctx, r.repo(repo), digest) } -func (r *subRegistry) GetTag(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { +func (r *subRegistry) GetTag(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { ctx = r.mapScopes(ctx) return r.r.GetTag(ctx, r.repo(repo), tagName) } -func (r *subRegistry) ResolveBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *subRegistry) ResolveBlob(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.ResolveBlob(ctx, r.repo(repo), digest) } -func (r *subRegistry) ResolveManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *subRegistry) ResolveManifest(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.ResolveManifest(ctx, r.repo(repo), digest) } -func (r *subRegistry) ResolveTag(ctx context.Context, repo string, tagName string) (ociregistry.Descriptor, error) { +func (r *subRegistry) ResolveTag(ctx context.Context, repo string, tagName string) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.ResolveTag(ctx, r.repo(repo), tagName) } -func (r *subRegistry) PushBlob(ctx context.Context, repo string, desc ociregistry.Descriptor, rd io.Reader) (ociregistry.Descriptor, error) { +func (r *subRegistry) PushBlob(ctx context.Context, repo string, desc oci.Descriptor, rd io.Reader) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.PushBlob(ctx, r.repo(repo), desc, rd) } -func (r *subRegistry) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *subRegistry) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (oci.BlobWriter, error) { ctx = r.mapScopes(ctx) // Luckily the context spans the entire lifetime of the blob writer (no // BlobWriter methods take a Context argument, so no need @@ -110,27 +110,27 @@ func (r *subRegistry) PushBlobChunked(ctx context.Context, repo string, chunkSiz return r.r.PushBlobChunked(ctx, r.repo(repo), chunkSize) } -func (r *subRegistry) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *subRegistry) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { ctx = r.mapScopes(ctx) return r.r.PushBlobChunkedResume(ctx, r.repo(repo), id, offset, chunkSize) } -func (r *subRegistry) MountBlob(ctx context.Context, fromRepo, toRepo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *subRegistry) MountBlob(ctx context.Context, fromRepo, toRepo string, digest oci.Digest) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.MountBlob(ctx, r.repo(fromRepo), r.repo(toRepo), digest) } -func (r *subRegistry) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (r *subRegistry) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { ctx = r.mapScopes(ctx) return r.r.PushManifest(ctx, r.repo(repo), contents, mediaType, params) } -func (r *subRegistry) DeleteBlob(ctx context.Context, repo string, digest ociregistry.Digest) error { +func (r *subRegistry) DeleteBlob(ctx context.Context, repo string, digest oci.Digest) error { ctx = r.mapScopes(ctx) return r.r.DeleteBlob(ctx, r.repo(repo), digest) } -func (r *subRegistry) DeleteManifest(ctx context.Context, repo string, digest ociregistry.Digest) error { +func (r *subRegistry) DeleteManifest(ctx context.Context, repo string, digest oci.Digest) error { ctx = r.mapScopes(ctx) return r.r.DeleteManifest(ctx, r.repo(repo), digest) } @@ -156,12 +156,12 @@ func (r *subRegistry) Repositories(ctx context.Context, startAfter string) iter. } } -func (r *subRegistry) Tags(ctx context.Context, repo string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { +func (r *subRegistry) Tags(ctx context.Context, repo string, params *oci.TagsParameters) iter.Seq2[string, error] { ctx = r.mapScopes(ctx) return r.r.Tags(ctx, r.repo(repo), params) } -func (r *subRegistry) Referrers(ctx context.Context, repo string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { +func (r *subRegistry) Referrers(ctx context.Context, repo string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { ctx = r.mapScopes(ctx) return r.r.Referrers(ctx, r.repo(repo), digest, params) } diff --git a/ociregistry/ocifilter/sub_test.go b/ocifilter/sub_test.go similarity index 79% rename from ociregistry/ocifilter/sub_test.go rename to ocifilter/sub_test.go index fc479a2..7a88539 100644 --- a/ociregistry/ocifilter/sub_test.go +++ b/ocifilter/sub_test.go @@ -22,14 +22,13 @@ import ( "slices" "testing" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociauth" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ocitest" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/require" - - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociauth" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ocitest" ) func TestSub(t *testing.T) { @@ -41,13 +40,13 @@ func TestSub(t *testing.T) { "b1": "hello", "scratch": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "scratch", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "b1", }}, }, @@ -61,10 +60,10 @@ func TestSub(t *testing.T) { Blobs: map[string]string{ "scratch": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "scratch", }, }, @@ -77,10 +76,10 @@ func TestSub(t *testing.T) { Blobs: map[string]string{ "scratch": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "scratch", }, }, @@ -98,7 +97,7 @@ func TestSub(t *testing.T) { b1Content := getBlob(t, r1, "bar", m.Layers[0].Digest) require.Equal(t, "hello", string(b1Content)) - repos, err := ociregistry.All(r1.Repositories(ctx, "")) + repos, err := oci.All(r1.Repositories(ctx, "")) require.NoError(t, err) slices.Sort(repos) require.Equal(t, []string{"bar"}, repos) @@ -117,7 +116,7 @@ func TestSubMaintainsAuthScope(t *testing.T) { // As the implementation is so uniform (and easily inspected in the source, // we use the GetBlob entry point as a proxy for testing all the entry points. // TODO it would be nice to have a reusable way (in ocitest, probably) of testing general properties - // across all ociregistry.Interface methods. + // across all oci.Interface methods. _, _ = r.GetBlob(ctx, "some/repo", "sha256:fffff") wantScope := ociauth.ParseScope( "other registry:catalog:* repository:foo/bar/a/b:pull,push repository:foo/bar/foo:delete,push", @@ -126,20 +125,20 @@ func TestSubMaintainsAuthScope(t *testing.T) { } type contextChecker struct { - ociregistry.Interface + oci.Interface check func(context.Context) } -func (r contextChecker) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r contextChecker) GetBlob(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { r.check(ctx) return nil, fmt.Errorf("nope") } -func getManifest(t *testing.T, r ociregistry.Interface, repo string, dg digest.Digest) ociregistry.Manifest { +func getManifest(t *testing.T, r oci.Interface, repo string, dg digest.Digest) oci.Manifest { rd, err := r.GetManifest(context.Background(), repo, dg) require.NoError(t, err) defer rd.Close() - var m ociregistry.Manifest + var m oci.Manifest data, err := io.ReadAll(rd) require.NoError(t, err) err = json.Unmarshal(data, &m) @@ -147,7 +146,7 @@ func getManifest(t *testing.T, r ociregistry.Interface, repo string, dg digest.D return m } -func getBlob(t *testing.T, r ociregistry.Interface, repo string, dg digest.Digest) []byte { +func getBlob(t *testing.T, r oci.Interface, repo string, dg digest.Digest) []byte { rd, err := r.GetBlob(context.Background(), repo, dg) require.NoError(t, err) defer rd.Close() diff --git a/ociregistry/ocimem/blob.go b/ocimem/blob.go similarity index 77% rename from ociregistry/ocimem/blob.go rename to ocimem/blob.go index e1879a0..a36dcf4 100644 --- a/ociregistry/ocimem/blob.go +++ b/ocimem/blob.go @@ -20,15 +20,14 @@ import ( "fmt" "sync" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" - - "github.com/jcarter3/oci/ociregistry" ) -// NewBytesReader returns an implementation of ociregistry.BlobReader +// NewBytesReader returns an implementation of oci.BlobReader // that returns the given bytes. The returned reader will return desc from its // Descriptor method. -func NewBytesReader(data []byte, desc ociregistry.Descriptor) ociregistry.BlobReader { +func NewBytesReader(data []byte, desc oci.Descriptor) oci.BlobReader { r := &bytesReader{ desc: desc, } @@ -38,15 +37,15 @@ func NewBytesReader(data []byte, desc ociregistry.Descriptor) ociregistry.BlobRe type bytesReader struct { r bytes.Reader - desc ociregistry.Descriptor + desc oci.Descriptor } func (r *bytesReader) Close() error { return nil } -// Descriptor implements [ociregistry.BlobReader.Descriptor]. -func (r *bytesReader) Descriptor() ociregistry.Descriptor { +// Descriptor implements [oci.BlobReader.Descriptor]. +func (r *bytesReader) Descriptor() oci.Descriptor { return r.desc } @@ -54,7 +53,7 @@ func (r *bytesReader) Read(data []byte) (int, error) { return r.r.Read(data) } -// Buffer holds an in-memory implementation of ociregistry.BlobWriter. +// Buffer holds an in-memory implementation of oci.BlobWriter. type Buffer struct { commit func(b *Buffer) error mu sync.Mutex @@ -62,7 +61,7 @@ type Buffer struct { checkStartOffset int64 uuid string committed bool - desc ociregistry.Descriptor + desc oci.Descriptor commitErr error } @@ -107,14 +106,14 @@ func (b *Buffer) ChunkSize() int { // GetBlob returns any committed data and is descriptor. It returns an error // if the data hasn't been committed or there was an error doing so. -func (b *Buffer) GetBlob() (ociregistry.Descriptor, []byte, error) { +func (b *Buffer) GetBlob() (oci.Descriptor, []byte, error) { b.mu.Lock() defer b.mu.Unlock() if !b.committed { - return ociregistry.Descriptor{}, nil, fmt.Errorf("blob not committed") + return oci.Descriptor{}, nil, fmt.Errorf("blob not committed") } if b.commitErr != nil { - return ociregistry.Descriptor{}, nil, b.commitErr + return oci.Descriptor{}, nil, b.commitErr } return b.desc, b.buf, nil } @@ -126,7 +125,7 @@ func (b *Buffer) Write(data []byte) (int, error) { if offset := b.checkStartOffset; offset != -1 { // Can't call Buffer.Size, since we are already holding the mutex. if int64(len(b.buf)) != offset { - return 0, fmt.Errorf("invalid offset %d in resumed upload (actual offset %d): %w", offset, len(b.buf), ociregistry.ErrRangeInvalid) + return 0, fmt.Errorf("invalid offset %d in resumed upload (actual offset %d): %w", offset, len(b.buf), oci.ErrRangeInvalid) } // Only check on the first write, since it's the start offset. b.checkStartOffset = -1 @@ -143,17 +142,17 @@ func newUUID() string { return fmt.Sprintf("%x", buf) } -// ID implements [ociregistry.BlobWriter.ID] by returning a randomly +// ID implements [oci.BlobWriter.ID] by returning a randomly // allocated hex UUID. func (b *Buffer) ID() string { return b.uuid } -// Commit implements [ociregistry.BlobWriter.Commit] by checking +// Commit implements [oci.BlobWriter.Commit] by checking // that everything looks OK and calling the commit function if so. -func (b *Buffer) Commit(dig ociregistry.Digest) (_ ociregistry.Descriptor, err error) { +func (b *Buffer) Commit(dig oci.Digest) (_ oci.Descriptor, err error) { if err := b.checkCommit(dig); err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } // Note: we're careful to call this function outside of the mutex so // that it can call locked Buffer methods OK. @@ -162,16 +161,16 @@ func (b *Buffer) Commit(dig ociregistry.Digest) (_ ociregistry.Descriptor, err e defer b.mu.Unlock() b.commitErr = err - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } - return ociregistry.Descriptor{ + return oci.Descriptor{ MediaType: "application/octet-stream", Size: int64(len(b.buf)), Digest: dig, }, nil } -func (b *Buffer) checkCommit(dig ociregistry.Digest) (err error) { +func (b *Buffer) checkCommit(dig oci.Digest) (err error) { b.mu.Lock() defer b.mu.Unlock() if b.commitErr != nil { @@ -183,9 +182,9 @@ func (b *Buffer) checkCommit(dig ociregistry.Digest) (err error) { } }() if digest.FromBytes(b.buf) != dig { - return fmt.Errorf("digest mismatch (sha256(%q) != %s): %w", b.buf, dig, ociregistry.ErrDigestInvalid) + return fmt.Errorf("digest mismatch (sha256(%q) != %s): %w", b.buf, dig, oci.ErrDigestInvalid) } - b.desc = ociregistry.Descriptor{ + b.desc = oci.Descriptor{ MediaType: "application/octet-stream", Digest: dig, Size: int64(len(b.buf)), diff --git a/ociregistry/ocimem/check_test.go b/ocimem/check_test.go similarity index 79% rename from ociregistry/ocimem/check_test.go rename to ocimem/check_test.go index 3a0f6e2..faac852 100644 --- a/ociregistry/ocimem/check_test.go +++ b/ocimem/check_test.go @@ -6,8 +6,8 @@ import ( "errors" "testing" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ocitest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ocitest" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/require" @@ -25,9 +25,9 @@ var pushManifestTests = []struct { testName: "NonExistentConfigReference", mediaType: ocispec.MediaTypeImageManifest, manifestData: func(ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ MediaType: "application/something", Size: 1, Digest: digest.FromString("a"), @@ -44,10 +44,10 @@ var pushManifestTests = []struct { }, mediaType: ocispec.MediaTypeImageManifest, manifestData: func(content ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: content.Blobs["a"], - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ MediaType: "application/something", Size: 1, Digest: digest.FromString("b"), @@ -64,10 +64,10 @@ var pushManifestTests = []struct { }, mediaType: ocispec.MediaTypeImageManifest, manifestData: func(content ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: content.Blobs["a"], - Subject: &ociregistry.Descriptor{ + Subject: &oci.Descriptor{ MediaType: "application/something", Size: 1, Digest: digest.FromString("b"), @@ -81,7 +81,7 @@ var pushManifestTests = []struct { manifestData: func(content ocitest.PushedRepoContent) []byte { return mustJSONMarshal(ocispec.Index{ MediaType: ocispec.MediaTypeImageIndex, - Manifests: []ociregistry.Descriptor{{ + Manifests: []oci.Descriptor{{ MediaType: ocispec.MediaTypeImageManifest, Size: 1, Digest: digest.FromString("a"), @@ -101,10 +101,10 @@ var pushManifestTests = []struct { }, mediaType: ocispec.MediaTypeImageManifest, manifestData: func(content ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: content.Blobs["a"], - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ MediaType: "application/something", Size: 1, Digest: digest.FromString("b"), @@ -117,7 +117,7 @@ var pushManifestTests = []struct { manifestData: func(content ocitest.PushedRepoContent) []byte { return mustJSONMarshal(ocispec.Index{ MediaType: ocispec.MediaTypeImageIndex, - Subject: &ociregistry.Descriptor{ + Subject: &oci.Descriptor{ MediaType: "application/something", Size: 1, Digest: digest.FromString("b"), @@ -132,13 +132,13 @@ var pushManifestTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "a", }}, }, @@ -153,10 +153,10 @@ var pushManifestTests = []struct { mediaType: ocispec.MediaTypeImageManifest, tag: "sometag", manifestData: func(content ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: content.Blobs["a"], - Layers: []ociregistry.Descriptor{content.Blobs["a"]}, + Layers: []oci.Descriptor{content.Blobs["a"]}, Annotations: map[string]string{ "different": "thing", }, @@ -170,13 +170,13 @@ var pushManifestTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "a", }}, }, @@ -200,13 +200,13 @@ var pushManifestTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "a", }}, }, @@ -231,13 +231,13 @@ var pushManifestTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "a", }}, }, @@ -249,10 +249,10 @@ var pushManifestTests = []struct { mediaType: ocispec.MediaTypeImageManifest, tag: "sometag", manifestData: func(content ocitest.PushedRepoContent) []byte { - return mustJSONMarshal(ociregistry.Manifest{ + return mustJSONMarshal(oci.Manifest{ MediaType: ocispec.MediaTypeImageManifest, Config: content.Blobs["a"], - Layers: []ociregistry.Descriptor{content.Blobs["a"]}, + Layers: []oci.Descriptor{content.Blobs["a"]}, Annotations: map[string]string{ "different": "thing", }, @@ -269,9 +269,9 @@ func TestPushManifest(t *testing.T) { "test": test.preload, })["test"] data := test.manifestData(content) - var params *ociregistry.PushManifestParameters + var params *oci.PushManifestParameters if test.tag != "" { - params = &ociregistry.PushManifestParameters{ + params = &oci.PushManifestParameters{ Tags: []string{test.tag}, } } @@ -290,11 +290,11 @@ var deleteBlobTests = []struct { testName string config Config preload ocitest.RepoContent - getDigest func(content ocitest.PushedRepoContent) ociregistry.Digest + getDigest func(content ocitest.PushedRepoContent) oci.Digest wantError string }{{ testName: "NonExistentRepo", - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return digest.FromString("blshdfsvg") }, wantError: "name unknown: repository name not known to registry", @@ -305,7 +305,7 @@ var deleteBlobTests = []struct { "a": "{}", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return digest.FromString("blshdfsvg") }, wantError: "blob unknown: blob unknown to registry", @@ -318,13 +318,13 @@ var deleteBlobTests = []struct { Blobs: map[string]string{ "a": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, - Layers: []ociregistry.Descriptor{{ + Layers: []oci.Descriptor{{ Digest: "a", }}, }, @@ -333,7 +333,7 @@ var deleteBlobTests = []struct { "sometag": "m", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return content.Blobs["a"].Digest }, wantError: "denied: requested access to the resource is denied: deletion of tagged blob not permitted", @@ -347,19 +347,19 @@ var deleteBlobTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m0": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "b", }, - Subject: &ociregistry.Descriptor{ + Subject: &oci.Descriptor{ Digest: "m0", }, }, @@ -368,7 +368,7 @@ var deleteBlobTests = []struct { "sometag": "m1", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return content.Blobs["a"].Digest }, wantError: "denied: requested access to the resource is denied: deletion of tagged blob not permitted", @@ -392,7 +392,7 @@ func TestDeleteBlob(t *testing.T) { } // Regardless of the result, the blob shouldn't be there afterwards // unless the operation was denied. - if !errors.Is(err, ociregistry.ErrDenied) { + if !errors.Is(err, oci.ErrDenied) { _, err := r.R.ResolveBlob(ctx, "test", digest) require.Error(t, err) } @@ -404,11 +404,11 @@ var deleteManifestTests = []struct { testName string config Config preload ocitest.RepoContent - getDigest func(content ocitest.PushedRepoContent) ociregistry.Digest + getDigest func(content ocitest.PushedRepoContent) oci.Digest wantError string }{{ testName: "NonExistentRepo", - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return digest.FromString("blshdfsvg") }, wantError: "name unknown: repository name not known to registry", @@ -419,7 +419,7 @@ var deleteManifestTests = []struct { "a": "{}", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return digest.FromString("blshdfsvg") }, wantError: "manifest unknown: manifest unknown to registry", @@ -432,10 +432,10 @@ var deleteManifestTests = []struct { Blobs: map[string]string{ "a": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, @@ -444,7 +444,7 @@ var deleteManifestTests = []struct { "sometag": "m", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return content.Manifests["m"].Digest }, wantError: "denied: requested access to the resource is denied: deletion of tagged manifest not permitted", @@ -458,19 +458,19 @@ var deleteManifestTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m0": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "b", }, - Subject: &ociregistry.Descriptor{ + Subject: &oci.Descriptor{ Digest: "m0", }, }, @@ -479,7 +479,7 @@ var deleteManifestTests = []struct { "sometag": "m1", }, }, - getDigest: func(content ocitest.PushedRepoContent) ociregistry.Digest { + getDigest: func(content ocitest.PushedRepoContent) oci.Digest { return content.Manifests["m0"].Digest }, wantError: "denied: requested access to the resource is denied: deletion of tagged manifest not permitted", @@ -503,7 +503,7 @@ func TestDeleteManifest(t *testing.T) { } // Regardless of the result, the manifest shouldn't be there afterwards // unless the operation was denied. - if !errors.Is(err, ociregistry.ErrDenied) { + if !errors.Is(err, oci.ErrDenied) { _, err := r.R.ResolveManifest(ctx, "test", digest) require.Error(t, err) } @@ -539,10 +539,10 @@ var deleteTagTests = []struct { Blobs: map[string]string{ "a": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, @@ -560,19 +560,19 @@ var deleteTagTests = []struct { "a": "{}", "b": "other", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m0": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, "m1": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "b", }, - Subject: &ociregistry.Descriptor{ + Subject: &oci.Descriptor{ Digest: "m0", }, }, @@ -601,7 +601,7 @@ func TestDeleteTag(t *testing.T) { } // Regardless of the result, the tag shouldn't be there afterwards // unless the operation was denied. - if !errors.Is(err, ociregistry.ErrDenied) { + if !errors.Is(err, oci.ErrDenied) { _, err := r.R.ResolveTag(ctx, "test", test.tag) require.Error(t, err) } @@ -631,10 +631,10 @@ func TestTagsLimit(t *testing.T) { Blobs: map[string]string{ "a": "{}", }, - Manifests: map[string]ociregistry.Manifest{ + Manifests: map[string]oci.Manifest{ "m": { MediaType: ocispec.MediaTypeImageManifest, - Config: ociregistry.Descriptor{ + Config: oci.Descriptor{ Digest: "a", }, }, @@ -650,55 +650,55 @@ func TestTagsLimit(t *testing.T) { }) t.Run("ZeroLimitReturnsAll", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", nil)) + tags, err := oci.All(r.R.Tags(ctx, "test", nil)) require.NoError(t, err) require.Equal(t, []string{"alpha", "bravo", "charlie", "delta", "echo"}, tags) }) t.Run("NegativeLimitReturnsAll", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{Limit: -1})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{Limit: -1})) require.NoError(t, err) require.Equal(t, []string{"alpha", "bravo", "charlie", "delta", "echo"}, tags) }) t.Run("LimitSmallerThanTotal", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{Limit: 3})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{Limit: 3})) require.NoError(t, err) require.Equal(t, []string{"alpha", "bravo", "charlie"}, tags) }) t.Run("LimitOfOne", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{Limit: 1})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{Limit: 1})) require.NoError(t, err) require.Equal(t, []string{"alpha"}, tags) }) t.Run("LimitLargerThanTotal", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{Limit: 100})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{Limit: 100})) require.NoError(t, err) require.Equal(t, []string{"alpha", "bravo", "charlie", "delta", "echo"}, tags) }) t.Run("LimitEqualToTotal", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{Limit: 5})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{Limit: 5})) require.NoError(t, err) require.Equal(t, []string{"alpha", "bravo", "charlie", "delta", "echo"}, tags) }) t.Run("LimitWithStartAfter", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{StartAfter: "bravo", Limit: 2})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{StartAfter: "bravo", Limit: 2})) require.NoError(t, err) require.Equal(t, []string{"charlie", "delta"}, tags) }) t.Run("LimitWithStartAfterReturnsAll", func(t *testing.T) { - tags, err := ociregistry.All(r.R.Tags(ctx, "test", &ociregistry.TagsParameters{StartAfter: "bravo"})) + tags, err := oci.All(r.R.Tags(ctx, "test", &oci.TagsParameters{StartAfter: "bravo"})) require.NoError(t, err) require.Equal(t, []string{"charlie", "delta", "echo"}, tags) }) t.Run("NonExistentRepo", func(t *testing.T) { - _, err := ociregistry.All(r.R.Tags(ctx, "nonexistent", &ociregistry.TagsParameters{Limit: 3})) + _, err := oci.All(r.R.Tags(ctx, "nonexistent", &oci.TagsParameters{Limit: 3})) require.Error(t, err) }) } diff --git a/ociregistry/ocimem/deleter.go b/ocimem/deleter.go similarity index 88% rename from ociregistry/ocimem/deleter.go rename to ocimem/deleter.go index 565ae0f..f0f1fe3 100644 --- a/ociregistry/ocimem/deleter.go +++ b/ocimem/deleter.go @@ -18,17 +18,17 @@ import ( "context" "fmt" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) var ( - errCannotDeleteTag = fmt.Errorf("%w: tag deletion not permitted", ociregistry.ErrDenied) - errCannotDeleteTaggedBlob = fmt.Errorf("%w: deletion of tagged blob not permitted", ociregistry.ErrDenied) - errCannotDeleteTaggedManifest = fmt.Errorf("%w: deletion of tagged manifest not permitted", ociregistry.ErrDenied) + errCannotDeleteTag = fmt.Errorf("%w: tag deletion not permitted", oci.ErrDenied) + errCannotDeleteTaggedBlob = fmt.Errorf("%w: deletion of tagged blob not permitted", oci.ErrDenied) + errCannotDeleteTaggedManifest = fmt.Errorf("%w: deletion of tagged manifest not permitted", oci.ErrDenied) ) // DeleteBlob deletes the blob with the given digest from the named repository. -func (r *Registry) DeleteBlob(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (r *Registry) DeleteBlob(ctx context.Context, repoName string, digest oci.Digest) error { r.mu.Lock() defer r.mu.Unlock() if _, err := r.blobForDigest(repoName, digest); err != nil { @@ -54,7 +54,7 @@ func (r *Registry) DeleteBlob(ctx context.Context, repoName string, digest ocire } // DeleteManifest deletes the manifest with the given digest from the named repository. -func (r *Registry) DeleteManifest(ctx context.Context, repoName string, digest ociregistry.Digest) error { +func (r *Registry) DeleteManifest(ctx context.Context, repoName string, digest oci.Digest) error { r.mu.Lock() defer r.mu.Unlock() if _, err := r.manifestForDigest(repoName, digest); err != nil { @@ -84,7 +84,7 @@ func (r *Registry) DeleteTag(ctx context.Context, repoName string, tagName strin return err } if _, ok := repo.tags[tagName]; !ok { - return fmt.Errorf("%w: tag does not exist", ociregistry.ErrManifestUnknown) + return fmt.Errorf("%w: tag does not exist", oci.ErrManifestUnknown) } if r.cfg.ImmutableTags { return errCannotDeleteTag diff --git a/ociregistry/ocimem/desciter.go b/ocimem/desciter.go similarity index 96% rename from ociregistry/ocimem/desciter.go rename to ocimem/desciter.go index c03fcb3..cd99ccf 100644 --- a/ociregistry/ocimem/desciter.go +++ b/ocimem/desciter.go @@ -6,7 +6,7 @@ import ( "fmt" "iter" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -21,14 +21,14 @@ const ( type descInfo struct { name string kind refKind - desc ociregistry.Descriptor + desc oci.Descriptor } type manifestInfo struct { // descriptors iterates over all direct references inside the manifest descriptors descIter // subject holds the subject (referred-to manifest) of the manifest - subject ociregistry.Digest + subject oci.Digest // artifactType holds the artifact type of the manifest artifactType string // annotations holds any annotations from the manifest. @@ -83,7 +83,7 @@ func manifestInfoForType[T any](getInfo func(T) manifestInfo) func(data []byte) } } -func imageInfo(m ociregistry.Manifest) manifestInfo { +func imageInfo(m oci.Manifest) manifestInfo { var info manifestInfo info.descriptors = func(yield func(descInfo) bool) { for i, layer := range m.Layers { diff --git a/ociregistry/ocimem/lister.go b/ocimem/lister.go similarity index 78% rename from ociregistry/ocimem/lister.go rename to ocimem/lister.go index ea6184a..8b19f10 100644 --- a/ociregistry/ocimem/lister.go +++ b/ocimem/lister.go @@ -20,7 +20,7 @@ import ( "slices" "strings" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // Repositories returns an iterator over all repository names in the registry. @@ -31,12 +31,12 @@ func (r *Registry) Repositories(_ context.Context, startAfter string) iter.Seq2[ } // Tags returns an iterator over tags in the named repository. -func (r *Registry) Tags(_ context.Context, repoName string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { +func (r *Registry) Tags(_ context.Context, repoName string, params *oci.TagsParameters) iter.Seq2[string, error] { r.mu.Lock() defer r.mu.Unlock() repo, err := r.repo(repoName) if err != nil { - return ociregistry.ErrorSeq[string](err) + return oci.ErrorSeq[string](err) } var startAfter string var limit int @@ -44,22 +44,22 @@ func (r *Registry) Tags(_ context.Context, repoName string, params *ociregistry. startAfter = params.StartAfter limit = params.Limit } - return ociregistry.LimitIter(mapKeysIter(repo.tags, strings.Compare, startAfter), limit) + return oci.LimitIter(mapKeysIter(repo.tags, strings.Compare, startAfter), limit) } // Referrers returns an iterator over descriptors that refer to the given digest. -func (r *Registry) Referrers(_ context.Context, repoName string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { +func (r *Registry) Referrers(_ context.Context, repoName string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { r.mu.Lock() defer r.mu.Unlock() repo, err := r.repo(repoName) if err != nil { - return ociregistry.ErrorSeq[ociregistry.Descriptor](err) + return oci.ErrorSeq[oci.Descriptor](err) } var artifactType string if params != nil { artifactType = params.ArtifactType } - var referrers []ociregistry.Descriptor + var referrers []oci.Descriptor for _, b := range repo.manifests { if b.info.subject != digest { continue @@ -70,7 +70,7 @@ func (r *Registry) Referrers(_ context.Context, repoName string, digest ociregis referrers = append(referrers, b.descriptor()) } slices.SortFunc(referrers, compareDescriptor) - return ociregistry.SliceSeq(referrers) + return oci.SliceSeq(referrers) } func mapKeysIter[K comparable, V any](m map[K]V, cmp func(K, K) int, startAfter K) iter.Seq2[K, error] { @@ -82,9 +82,9 @@ func mapKeysIter[K comparable, V any](m map[K]V, cmp func(K, K) int, startAfter } slices.SortFunc(ks, cmp) - return ociregistry.SliceSeq(ks) + return oci.SliceSeq(ks) } -func compareDescriptor(d0, d1 ociregistry.Descriptor) int { +func compareDescriptor(d0, d1 oci.Descriptor) int { return strings.Compare(string(d0.Digest), string(d1.Digest)) } diff --git a/ociregistry/ocimem/reader.go b/ocimem/reader.go similarity index 79% rename from ociregistry/ocimem/reader.go rename to ocimem/reader.go index 6cc9e5a..81aff1c 100644 --- a/ociregistry/ocimem/reader.go +++ b/ocimem/reader.go @@ -18,13 +18,13 @@ import ( "context" "fmt" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) -// This file implements the ociregistry.Reader methods. +// This file implements the oci.Reader methods. // GetBlob returns the content of the blob with the given digest. -func (r *Registry) GetBlob(ctx context.Context, repoName string, dig ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *Registry) GetBlob(ctx context.Context, repoName string, dig oci.Digest) (oci.BlobReader, error) { r.mu.Lock() defer r.mu.Unlock() b, err := r.blobForDigest(repoName, dig) @@ -35,7 +35,7 @@ func (r *Registry) GetBlob(ctx context.Context, repoName string, dig ociregistry } // GetBlobRange returns a range of bytes from the blob with the given digest. -func (r *Registry) GetBlobRange(ctx context.Context, repoName string, dig ociregistry.Digest, o0, o1 int64) (ociregistry.BlobReader, error) { +func (r *Registry) GetBlobRange(ctx context.Context, repoName string, dig oci.Digest, o0, o1 int64) (oci.BlobReader, error) { r.mu.Lock() defer r.mu.Unlock() b, err := r.blobForDigest(repoName, dig) @@ -52,7 +52,7 @@ func (r *Registry) GetBlobRange(ctx context.Context, repoName string, dig ocireg } // GetManifest returns the content of the manifest with the given digest. -func (r *Registry) GetManifest(ctx context.Context, repoName string, dig ociregistry.Digest) (ociregistry.BlobReader, error) { +func (r *Registry) GetManifest(ctx context.Context, repoName string, dig oci.Digest) (oci.BlobReader, error) { r.mu.Lock() defer r.mu.Unlock() b, err := r.manifestForDigest(repoName, dig) @@ -63,7 +63,7 @@ func (r *Registry) GetManifest(ctx context.Context, repoName string, dig ociregi } // GetTag returns the content of the manifest with the given tag. -func (r *Registry) GetTag(ctx context.Context, repoName string, tagName string) (ociregistry.BlobReader, error) { +func (r *Registry) GetTag(ctx context.Context, repoName string, tagName string) (oci.BlobReader, error) { desc, err := r.ResolveTag(ctx, repoName, tagName) if err != nil { return nil, err @@ -72,38 +72,38 @@ func (r *Registry) GetTag(ctx context.Context, repoName string, tagName string) } // ResolveTag returns the descriptor for the manifest with the given tag. -func (r *Registry) ResolveTag(ctx context.Context, repoName string, tagName string) (ociregistry.Descriptor, error) { +func (r *Registry) ResolveTag(ctx context.Context, repoName string, tagName string) (oci.Descriptor, error) { r.mu.Lock() defer r.mu.Unlock() repo, err := r.repo(repoName) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } desc, ok := repo.tags[tagName] if !ok { - return ociregistry.Descriptor{}, ociregistry.ErrManifestUnknown + return oci.Descriptor{}, oci.ErrManifestUnknown } return desc, nil } // ResolveBlob returns the descriptor for the blob with the given digest. -func (r *Registry) ResolveBlob(ctx context.Context, repoName string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *Registry) ResolveBlob(ctx context.Context, repoName string, digest oci.Digest) (oci.Descriptor, error) { r.mu.Lock() defer r.mu.Unlock() b, err := r.blobForDigest(repoName, digest) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return b.descriptor(), nil } // ResolveManifest returns the descriptor for the manifest with the given digest. -func (r *Registry) ResolveManifest(ctx context.Context, repoName string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *Registry) ResolveManifest(ctx context.Context, repoName string, digest oci.Digest) (oci.Descriptor, error) { r.mu.Lock() defer r.mu.Unlock() b, err := r.manifestForDigest(repoName, digest) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } return b.descriptor(), nil } diff --git a/ociregistry/ocimem/registry.go b/ocimem/registry.go similarity index 80% rename from ociregistry/ocimem/registry.go rename to ocimem/registry.go index 626759c..6637d2f 100644 --- a/ociregistry/ocimem/registry.go +++ b/ocimem/registry.go @@ -20,25 +20,25 @@ import ( "fmt" "sync" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociref" "github.com/opencontainers/go-digest" ) -var _ ociregistry.Interface = (*Registry)(nil) +var _ oci.Interface = (*Registry)(nil) -// Registry is an in-memory implementation of [ociregistry.Interface]. +// Registry is an in-memory implementation of [oci.Interface]. type Registry struct { - *ociregistry.Funcs + *oci.Funcs cfg Config mu sync.Mutex repos map[string]*repository } type repository struct { - tags map[string]ociregistry.Descriptor - manifests map[ociregistry.Digest]*blob - blobs map[ociregistry.Digest]*blob + tags map[string]oci.Descriptor + manifests map[oci.Digest]*blob + blobs map[oci.Digest]*blob uploads map[string]*Buffer } @@ -48,8 +48,8 @@ type blob struct { info manifestInfo } -func (b *blob) descriptor() ociregistry.Descriptor { - return ociregistry.Descriptor{ +func (b *blob) descriptor() oci.Descriptor { + return oci.Descriptor{ MediaType: b.mediaType, Size: int64(len(b.data)), Digest: digest.FromBytes(b.data), @@ -66,7 +66,7 @@ func New() *Registry { return NewWithConfig(nil) } -// NewWithConfig returns a new in-memory [ociregistry.Interface] +// NewWithConfig returns a new in-memory [oci.Interface] // implementation using the given configuration. If // cfg is nil, it's treated the same as a pointer to the zero [Config] value. func NewWithConfig(cfg0 *Config) *Registry { @@ -103,36 +103,36 @@ func (r *Registry) repo(repoName string) (*repository, error) { if repo, ok := r.repos[repoName]; ok { return repo, nil } - return nil, ociregistry.ErrNameUnknown + return nil, oci.ErrNameUnknown } -func (r *Registry) manifestForDigest(repoName string, dig ociregistry.Digest) (*blob, error) { +func (r *Registry) manifestForDigest(repoName string, dig oci.Digest) (*blob, error) { repo, err := r.repo(repoName) if err != nil { return nil, err } b := repo.manifests[dig] if b == nil { - return nil, ociregistry.ErrManifestUnknown + return nil, oci.ErrManifestUnknown } return b, nil } -func (r *Registry) blobForDigest(repoName string, dig ociregistry.Digest) (*blob, error) { +func (r *Registry) blobForDigest(repoName string, dig oci.Digest) (*blob, error) { repo, err := r.repo(repoName) if err != nil { return nil, err } b := repo.blobs[dig] if b == nil { - return nil, ociregistry.ErrBlobUnknown + return nil, oci.ErrBlobUnknown } return b, nil } func (r *Registry) makeRepo(repoName string) (*repository, error) { if !ociref.IsValidRepository(repoName) { - return nil, ociregistry.ErrNameInvalid + return nil, oci.ErrNameInvalid } if r.repos == nil { r.repos = make(map[string]*repository) @@ -141,7 +141,7 @@ func (r *Registry) makeRepo(repoName string) (*repository, error) { return repo, nil } repo := &repository{ - tags: make(map[string]ociregistry.Descriptor), + tags: make(map[string]oci.Descriptor), manifests: make(map[digest.Digest]*blob), blobs: make(map[digest.Digest]*blob), uploads: make(map[string]*Buffer), @@ -155,7 +155,7 @@ const emptyHash = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca49599 // CheckDescriptor checks that the given descriptor matches the given data or, // if data is nil, that the descriptor looks sane. -func CheckDescriptor(desc ociregistry.Descriptor, data []byte) error { +func CheckDescriptor(desc oci.Descriptor, data []byte) error { if err := desc.Digest.Validate(); err != nil { return fmt.Errorf("invalid digest: %v", err) } diff --git a/ociregistry/ocimem/writer.go b/ocimem/writer.go similarity index 77% rename from ociregistry/ocimem/writer.go rename to ocimem/writer.go index 55e1437..7f02a73 100644 --- a/ociregistry/ocimem/writer.go +++ b/ocimem/writer.go @@ -20,35 +20,35 @@ import ( "io" "slices" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/ociref" "github.com/opencontainers/go-digest" ) -// This file implements the ociregistry.Writer methods. +// This file implements the oci.Writer methods. // PushBlob pushes a blob to the named repository. -func (r *Registry) PushBlob(ctx context.Context, repoName string, desc ociregistry.Descriptor, content io.Reader) (ociregistry.Descriptor, error) { +func (r *Registry) PushBlob(ctx context.Context, repoName string, desc oci.Descriptor, content io.Reader) (oci.Descriptor, error) { data, err := io.ReadAll(content) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("cannot read content: %v", err) + return oci.Descriptor{}, fmt.Errorf("cannot read content: %v", err) } if err := CheckDescriptor(desc, data); err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err) + return oci.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err) } r.mu.Lock() defer r.mu.Unlock() repo, err := r.makeRepo(repoName) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, data: data} return desc, nil } // PushBlobChunked starts a chunked blob upload to the named repository. -func (r *Registry) PushBlobChunked(ctx context.Context, repoName string, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *Registry) PushBlobChunked(ctx context.Context, repoName string, chunkSize int) (oci.BlobWriter, error) { // TODO(mvdan): Why does the ocimem implementation allow a PATCH on an upload ID which doesn't exist? // The tests in ociserver make this assumption, so they break without this bit of code. // @@ -59,7 +59,7 @@ func (r *Registry) PushBlobChunked(ctx context.Context, repoName string, chunkSi } // PushBlobChunkedResume resumes a previously started chunked blob upload. -func (r *Registry) PushBlobChunkedResume(ctx context.Context, repoName, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (r *Registry) PushBlobChunkedResume(ctx context.Context, repoName, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { r.mu.Lock() defer r.mu.Unlock() repo, err := r.makeRepo(repoName) @@ -82,49 +82,49 @@ func (r *Registry) PushBlobChunkedResume(ctx context.Context, repoName, id strin } // MountBlob makes a blob from one repository available in another. -func (r *Registry) MountBlob(ctx context.Context, fromRepo, toRepo string, dig ociregistry.Digest) (ociregistry.Descriptor, error) { +func (r *Registry) MountBlob(ctx context.Context, fromRepo, toRepo string, dig oci.Digest) (oci.Descriptor, error) { r.mu.Lock() defer r.mu.Unlock() rto, err := r.makeRepo(toRepo) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } b, err := r.blobForDigest(fromRepo, dig) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } rto.blobs[dig] = b return b.descriptor(), nil } -var errCannotOverwriteTag = fmt.Errorf("%w: cannot overwrite tag", ociregistry.ErrDenied) +var errCannotOverwriteTag = fmt.Errorf("%w: cannot overwrite tag", oci.ErrDenied) // PushManifest pushes a manifest to the named repository, optionally tagging it. -func (r *Registry) PushManifest(ctx context.Context, repoName string, data []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { +func (r *Registry) PushManifest(ctx context.Context, repoName string, data []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { r.mu.Lock() defer r.mu.Unlock() repo, err := r.makeRepo(repoName) if err != nil { - return ociregistry.Descriptor{}, err + return oci.Descriptor{}, err } - var dig ociregistry.Digest + var dig oci.Digest if params != nil && params.Digest != "" { // Validate the provided digest against the contents. if err := params.Digest.Validate(); err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("invalid digest: %v: %w", err, ociregistry.ErrDigestInvalid) + return oci.Descriptor{}, fmt.Errorf("invalid digest: %v: %w", err, oci.ErrDigestInvalid) } verifier := params.Digest.Verifier() if _, err := verifier.Write(data); err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("cannot verify digest: %v", err) + return oci.Descriptor{}, fmt.Errorf("cannot verify digest: %v", err) } if !verifier.Verified() { - return ociregistry.Descriptor{}, fmt.Errorf("digest mismatch: %w", ociregistry.ErrDigestInvalid) + return oci.Descriptor{}, fmt.Errorf("digest mismatch: %w", oci.ErrDigestInvalid) } dig = params.Digest } else { dig = digest.FromBytes(data) } - desc := ociregistry.Descriptor{ + desc := oci.Descriptor{ Digest: dig, MediaType: mediaType, Size: int64(len(data)), @@ -135,19 +135,19 @@ func (r *Registry) PushManifest(ctx context.Context, repoName string, data []byt } for _, tag := range tags { if !ociref.IsValidTag(tag) { - return ociregistry.Descriptor{}, fmt.Errorf("invalid tag") + return oci.Descriptor{}, fmt.Errorf("invalid tag") } if r.cfg.ImmutableTags { if currDesc, ok := repo.tags[tag]; ok { if dig == currDesc.Digest { if currDesc.MediaType != mediaType { // Same digest but mismatched media type. - return ociregistry.Descriptor{}, fmt.Errorf("%w: mismatched media type", ociregistry.ErrDenied) + return oci.Descriptor{}, fmt.Errorf("%w: mismatched media type", oci.ErrDenied) } // It's got the same content already. Allow it. return currDesc, nil } - return ociregistry.Descriptor{}, errCannotOverwriteTag + return oci.Descriptor{}, errCannotOverwriteTag } } } @@ -157,18 +157,18 @@ func (r *Registry) PushManifest(ctx context.Context, repoName string, data []byt // Only check descriptor with data when using canonical digest, // since CheckDescriptor uses canonical hashing. if err := CheckDescriptor(desc, data); err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err) + return oci.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err) } } else { // For non-canonical digests, check descriptor without data verification // (we already validated the digest above). if desc.MediaType == "" { - return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: no media type in descriptor") + return oci.Descriptor{}, fmt.Errorf("invalid descriptor: no media type in descriptor") } } info, err := r.checkManifestReferences(repoName, desc.MediaType, data) if err != nil { - return ociregistry.Descriptor{}, fmt.Errorf("invalid manifest: %v", err) + return oci.Descriptor{}, fmt.Errorf("invalid manifest: %v", err) } repo.manifests[dig] = &blob{ @@ -220,7 +220,7 @@ func (r *Registry) checkManifestReferences(repoName string, mediaType string, da // returned by the given iterator, within the given repository. // TODO currently this iterates through all tagged manifests. A better // algorithm could amortise that work and be considerably more efficient. -func refersTo(repo *repository, iter descIter, digest ociregistry.Digest) (found bool, retErr error) { +func refersTo(repo *repository, iter descIter, digest oci.Digest) (found bool, retErr error) { for info := range iter { if info.desc.Digest == digest { return true, nil diff --git a/ociregistry/ociref/reference.go b/ociref/reference.go similarity index 100% rename from ociregistry/ociref/reference.go rename to ociref/reference.go diff --git a/ociregistry/ociref/reference_test.go b/ociref/reference_test.go similarity index 100% rename from ociregistry/ociref/reference_test.go rename to ociref/reference_test.go diff --git a/ociregistry/README.md b/ociregistry/README.md index d6d7c18..ea55cf6 100644 --- a/ociregistry/README.md +++ b/ociregistry/README.md @@ -1,9 +1,8 @@ -# `ociregistry` - -In the top level package (`ociregistry`) this module defines a [Go interface](./interface.go) that encapsulates the operations provided by an OCI +# `oci` +In the top level package (`oci`) this module defines a [Go interface](./interface.go) that encapsulates the operations provided by an OCI registry. -Full reference documentation can be found [here](https://pkg.go.dev/cuelabs.dev/go/oci/ociregistry). +Full reference documentation can be found [here](https://pkg.go.dev/cuelabs.dev/go/oci/oci). It also provides a lightweight in-memory implementation of that interface (`ocimem`) and an HTTP server that implements the [OCI registry protocol](https://github.com/opencontainers/distribution-spec/blob/main/spec.md) on top of it. @@ -16,3 +15,4 @@ OCI registry implementations. Although the API is fairly stable, it's still in v0 currently, so incompatible changes can't be ruled out. The code was originally derived from the [go-containerregistry](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/registry) registry, but has considerably diverged since then. + diff --git a/ociregistry/ociserver/compatibility_test.go b/ociserver/compatibility_test.go similarity index 100% rename from ociregistry/ociserver/compatibility_test.go rename to ociserver/compatibility_test.go diff --git a/ociregistry/ociserver/deleter.go b/ociserver/deleter.go similarity index 81% rename from ociregistry/ociserver/deleter.go rename to ociserver/deleter.go index ea798c2..a9dd9ee 100644 --- a/ociregistry/ociserver/deleter.go +++ b/ociserver/deleter.go @@ -18,12 +18,12 @@ import ( "context" "net/http" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/internal/ocirequest" ) func (r *registry) handleBlobDelete(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { - if err := r.backend.DeleteBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)); err != nil { + if err := r.backend.DeleteBlob(ctx, rreq.Repo, oci.Digest(rreq.Digest)); err != nil { return err } resp.WriteHeader(http.StatusAccepted) @@ -35,7 +35,7 @@ func (r *registry) handleManifestDelete(ctx context.Context, resp http.ResponseW if rreq.Tag != "" { err = r.backend.DeleteTag(ctx, rreq.Repo, rreq.Tag) } else { - err = r.backend.DeleteManifest(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + err = r.backend.DeleteManifest(ctx, rreq.Repo, oci.Digest(rreq.Digest)) } if err != nil { return err diff --git a/ociregistry/ociserver/error.go b/ociserver/error.go similarity index 79% rename from ociregistry/ociserver/error.go rename to ociserver/error.go index 69c5f75..7c9d145 100644 --- a/ociregistry/ociserver/error.go +++ b/ociserver/error.go @@ -17,13 +17,13 @@ package ociserver import ( "fmt" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) func withHTTPCode(statusCode int, err error) error { - return ociregistry.NewHTTPError(err, statusCode, nil, nil) + return oci.NewHTTPError(err, statusCode, nil, nil) } func badAPIUseError(f string, a ...any) error { - return ociregistry.NewError(fmt.Sprintf(f, a...), ociregistry.ErrUnsupported.Code(), nil) + return oci.NewError(fmt.Sprintf(f, a...), oci.ErrUnsupported.Code(), nil) } diff --git a/ociregistry/ociserver/error_test.go b/ociserver/error_test.go similarity index 81% rename from ociregistry/ociserver/error_test.go rename to ociserver/error_test.go index 8bf8ffb..d35780a 100644 --- a/ociregistry/ociserver/error_test.go +++ b/ociserver/error_test.go @@ -22,8 +22,7 @@ import ( "net/http/httptest" "testing" - "github.com/jcarter3/oci/ociregistry" - + "github.com/jcarter3/oci" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -32,10 +31,10 @@ func TestCustomErrorWriter(t *testing.T) { // Test that if an Interface method returns an HTTPError error, the // HTTP status code is derived from the OCI error code in preference // to the HTTPError status code. - r := New(&ociregistry.Funcs{}, &Options{ + r := New(&oci.Funcs{}, &Options{ WriteError: func(w http.ResponseWriter, _ *http.Request, err error) { w.Header().Set("Some-Header", "a value") - ociregistry.WriteError(w, err) + oci.WriteError(w, err) }, }) s := httptest.NewServer(r) @@ -50,9 +49,9 @@ func TestHTTPStatusOverriddenByErrorCode(t *testing.T) { // Test that if an Interface method returns an HTTPError error, the // HTTP status code is derived from the OCI error code in preference // to the HTTPError status code. - r := New(&ociregistry.Funcs{ - GetTag_: func(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { - return nil, ociregistry.NewHTTPError(ociregistry.ErrNameUnknown, http.StatusUnauthorized, nil, nil) + r := New(&oci.Funcs{ + GetTag_: func(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { + return nil, oci.NewHTTPError(oci.ErrNameUnknown, http.StatusUnauthorized, nil, nil) }, }, nil) s := httptest.NewServer(r) @@ -62,9 +61,9 @@ func TestHTTPStatusOverriddenByErrorCode(t *testing.T) { defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) require.Equal(t, http.StatusNotFound, resp.StatusCode) - expected := &ociregistry.WireErrors{ - Errors: []ociregistry.WireError{{ - Code_: ociregistry.ErrNameUnknown.Code(), + expected := &oci.WireErrors{ + Errors: []oci.WireError{{ + Code_: oci.ErrNameUnknown.Code(), Message: "401 Unauthorized: name unknown: repository name not known to registry", }}, } @@ -77,9 +76,9 @@ func TestHTTPStatusUsedForUnknownErrorCode(t *testing.T) { // Test that if an Interface method returns an HTTPError error, that // HTTP status code is used when the code isn't known to be // associated with a particular HTTP status. - r := New(&ociregistry.Funcs{ - GetTag_: func(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { - return nil, ociregistry.NewHTTPError(ociregistry.NewError("foo", "SOMECODE", nil), http.StatusTeapot, nil, nil) + r := New(&oci.Funcs{ + GetTag_: func(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { + return nil, oci.NewHTTPError(oci.NewError("foo", "SOMECODE", nil), http.StatusTeapot, nil, nil) }, }, nil) s := httptest.NewServer(r) @@ -89,8 +88,8 @@ func TestHTTPStatusUsedForUnknownErrorCode(t *testing.T) { defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) require.Equal(t, http.StatusTeapot, resp.StatusCode) - expected := &ociregistry.WireErrors{ - Errors: []ociregistry.WireError{{ + expected := &oci.WireErrors{ + Errors: []oci.WireError{{ Code_: "SOMECODE", Message: "foo", }}, diff --git a/ociregistry/ociserver/lister.go b/ociserver/lister.go similarity index 91% rename from ociregistry/ociserver/lister.go rename to ociserver/lister.go index 4a53268..a070803 100644 --- a/ociregistry/ociserver/lister.go +++ b/ociserver/lister.go @@ -25,10 +25,10 @@ import ( "net/url" "strconv" + "github.com/jcarter3/oci" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci/internal/ocirequest" ) const maxPageSize = 10000 @@ -43,7 +43,7 @@ type listTags struct { } func (r *registry) handleTagsList(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { - tags, link, err := r.nextListResults(req, rreq, r.backend.Tags(ctx, rreq.Repo, &ociregistry.TagsParameters{ + tags, link, err := r.nextListResults(req, rreq, r.backend.Tags(ctx, rreq.Repo, &oci.TagsParameters{ StartAfter: rreq.ListLast, Limit: rreq.ListN, })) @@ -102,7 +102,7 @@ func (r *registry) handleReferrersList(ctx context.Context, resp http.ResponseWr // request, linked to the next one with a Link header. However, arranging that is non-trivial // because we'd need a way to return a link value to the client that enables a fresh // call to Referrers to start where the old one left off. For now, we'll punt. - for desc, err := range r.backend.Referrers(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest), &ociregistry.ReferrersParameters{ + for desc, err := range r.backend.Referrers(ctx, rreq.Repo, oci.Digest(rreq.Digest), &oci.ReferrersParameters{ ArtifactType: artifactType, }) { if err != nil { @@ -126,7 +126,7 @@ func (r *registry) handleReferrersList(ctx context.Context, resp http.ResponseWr func (r *registry) nextListResults(req *http.Request, rreq *ocirequest.Request, itemsIter iter.Seq2[string, error]) (items []string, link string, _ error) { if r.opts.MaxListPageSize > 0 && rreq.ListN > r.opts.MaxListPageSize { - return nil, "", ociregistry.NewError(fmt.Sprintf("query parameter n is too large (n=%d, max=%d)", rreq.ListN, r.opts.MaxListPageSize), ociregistry.ErrUnsupported.Code(), nil) + return nil, "", oci.NewError(fmt.Sprintf("query parameter n is too large (n=%d, max=%d)", rreq.ListN, r.opts.MaxListPageSize), oci.ErrUnsupported.Code(), nil) } n := rreq.ListN if n <= 0 { diff --git a/ociregistry/ociserver/mediatype.go b/ociserver/mediatype.go similarity index 100% rename from ociregistry/ociserver/mediatype.go rename to ociserver/mediatype.go diff --git a/ociregistry/ociserver/proxy_test.go b/ociserver/proxy_test.go similarity index 89% rename from ociregistry/ociserver/proxy_test.go rename to ociserver/proxy_test.go index 277ab74..e1cafef 100644 --- a/ociregistry/ociserver/proxy_test.go +++ b/ociserver/proxy_test.go @@ -22,15 +22,15 @@ import ( "net/http/httptest" "testing" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/ociclient" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci/ociclient" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ociserver" ) // Test that implementing an OCI registry proxy by sitting ociserver @@ -56,14 +56,14 @@ var ( var proxyTests = []struct { name string - clientDo func(context.Context, ociregistry.Interface) error + clientDo func(context.Context, oci.Interface) error proxyRequests []string backendRequests []string }{ { name: "PushBlob_small", - clientDo: func(ctx context.Context, client ociregistry.Interface) error { + clientDo: func(ctx context.Context, client oci.Interface) error { _, err := client.PushBlob(ctx, "foo/bar", ocispec.Descriptor{ Size: int64(len(smallData)), Digest: digest.FromBytes(smallData), @@ -81,7 +81,7 @@ var proxyTests = []struct { }, { name: "PushBlob_large", - clientDo: func(ctx context.Context, client ociregistry.Interface) error { + clientDo: func(ctx context.Context, client oci.Interface) error { _, err := client.PushBlob(ctx, "foo/bar", ocispec.Descriptor{ Size: int64(len(largeData)), Digest: digest.FromBytes(largeData), @@ -99,7 +99,7 @@ var proxyTests = []struct { }, { name: "PushBlobChunked_large_oneWrite", - clientDo: func(ctx context.Context, client ociregistry.Interface) error { + clientDo: func(ctx context.Context, client oci.Interface) error { bw, err := client.PushBlobChunked(ctx, "foo/bar", 0) if err != nil { return err @@ -135,7 +135,7 @@ func recordingServer(tb testing.TB, reqs *[]string, handler http.Handler) *httpt return server } -func testClient(tb testing.TB, server *httptest.Server) ociregistry.Interface { +func testClient(tb testing.TB, server *httptest.Server) oci.Interface { client, err := ociclient.New(server.Listener.Addr().String(), &ociclient.Options{ Insecure: true, // since it's a local httptest server }) diff --git a/ociregistry/ociserver/range.go b/ociserver/range.go similarity index 100% rename from ociregistry/ociserver/range.go rename to ociserver/range.go diff --git a/ociregistry/ociserver/reader.go b/ociserver/reader.go similarity index 88% rename from ociregistry/ociserver/reader.go rename to ociserver/reader.go index 017d4cb..476b4ac 100644 --- a/ociregistry/ociserver/reader.go +++ b/ociserver/reader.go @@ -20,12 +20,12 @@ import ( "io" "net/http" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/internal/ocirequest" ) func (r *registry) handleBlobHead(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { - desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, oci.Digest(rreq.Digest)) if err != nil { return err } @@ -43,7 +43,7 @@ func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter, // what to pass back, so resolve the blob first so we don't // stimulate the backend to start sending the whole stream // only to abandon it. - desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + desc, err := r.backend.ResolveBlob(ctx, rreq.Repo, oci.Digest(rreq.Digest)) if err != nil { // TODO this might not be the best response because ResolveBlob is // often implemented with a HEAD request that can't return an error @@ -68,7 +68,7 @@ func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter, } switch len(ranges) { case 0: - blob, err := r.backend.GetBlob(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + blob, err := r.backend.GetBlob(ctx, rreq.Repo, oci.Digest(rreq.Digest)) if err != nil { return err } @@ -83,7 +83,7 @@ func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter, return nil case 1: rng := ranges[0] - blob, err := r.backend.GetBlobRange(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest), rng.start, rng.end) + blob, err := r.backend.GetBlobRange(ctx, rreq.Repo, oci.Digest(rreq.Digest), rng.start, rng.end) if err != nil { // TODO fall back to using GetBlob if err is ErrUnsupported? return err @@ -115,12 +115,12 @@ func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter, func (r *registry) handleManifestGet(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { // TODO we could do a redirect here too if we thought it was worthwhile. - var mr ociregistry.BlobReader + var mr oci.BlobReader var err error if rreq.Tag != "" { mr, err = r.backend.GetTag(ctx, rreq.Repo, rreq.Tag) } else { - mr, err = r.backend.GetManifest(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + mr, err = r.backend.GetManifest(ctx, rreq.Repo, oci.Digest(rreq.Digest)) } if err != nil { return err @@ -137,12 +137,12 @@ func (r *registry) handleManifestGet(ctx context.Context, resp http.ResponseWrit } func (r *registry) handleManifestHead(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { - var desc ociregistry.Descriptor + var desc oci.Descriptor var err error if rreq.Tag != "" { desc, err = r.backend.ResolveTag(ctx, rreq.Repo, rreq.Tag) } else { - desc, err = r.backend.ResolveManifest(ctx, rreq.Repo, ociregistry.Digest(rreq.Digest)) + desc, err = r.backend.ResolveManifest(ctx, rreq.Repo, oci.Digest(rreq.Digest)) } if err != nil { return err diff --git a/ociregistry/ociserver/registry.go b/ociserver/registry.go similarity index 93% rename from ociregistry/ociserver/registry.go rename to ociserver/registry.go index 487b986..615196a 100644 --- a/ociregistry/ociserver/registry.go +++ b/ociserver/registry.go @@ -26,8 +26,8 @@ import ( "net/http" "sync/atomic" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci" + "github.com/jcarter3/oci/internal/ocirequest" ocispecroot "github.com/opencontainers/image-spec/specs-go" ) @@ -44,7 +44,7 @@ type Options struct { // writer to write the error response to, the request that // the error is in response to, and the error itself. // - // If WriteError is nil, [ociregistry.WriteError] will + // If WriteError is nil, [oci.WriteError] will // be used and any error discarded. WriteError func(w http.ResponseWriter, req *http.Request, err error) @@ -110,7 +110,7 @@ type Options struct { // // TODO perhaps the redirect behavior described above // isn't always what is wanted? - LocationsForDescriptor func(isManifest bool, desc ociregistry.Descriptor) ([]string, error) + LocationsForDescriptor func(isManifest bool, desc oci.Descriptor) ([]string, error) DebugID string } @@ -128,13 +128,13 @@ var debugID int32 // // All HTTP responses will be JSON, formatted according to the // OCI spec. If an error returned from backend conforms to -// [ociregistry.Error], the associated code and detail will be used. +// [oci.Error], the associated code and detail will be used. // // The HTTP response code will be determined from the error // code when possible. If it can't be determined and the -// error implements [ociregistry.HTTPError], the code returned +// error implements [oci.HTTPError], the code returned // by StatusCode will be used as the HTTP response code. -func New(backend ociregistry.Interface, opts *Options) http.Handler { +func New(backend oci.Interface, opts *Options) http.Handler { if opts == nil { opts = new(Options) } @@ -147,7 +147,7 @@ func New(backend ociregistry.Interface, opts *Options) http.Handler { } if r.opts.WriteError == nil { r.opts.WriteError = func(w http.ResponseWriter, _ *http.Request, err error) { - ociregistry.WriteError(w, err) + oci.WriteError(w, err) } } return r @@ -159,7 +159,7 @@ func (r *registry) logf(f string, a ...any) { type registry struct { opts Options - backend ociregistry.Interface + backend oci.Interface } var handlers = []func(r *registry, ctx context.Context, w http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error{ @@ -217,7 +217,7 @@ func (r *registry) handlePing(ctx context.Context, resp http.ResponseWriter, req return nil } -func (r *registry) setLocationHeader(resp http.ResponseWriter, isManifest bool, desc ociregistry.Descriptor, defaultLocation string) error { +func (r *registry) setLocationHeader(resp http.ResponseWriter, isManifest bool, desc oci.Descriptor, defaultLocation string) error { loc := defaultLocation if r.opts.LocationsForDescriptor != nil { locs, err := r.opts.LocationsForDescriptor(isManifest, desc) diff --git a/ociregistry/ociserver/registry_test.go b/ociserver/registry_test.go similarity index 99% rename from ociregistry/ociserver/registry_test.go rename to ociserver/registry_test.go index e7286f4..19aaf09 100644 --- a/ociregistry/ociserver/registry_test.go +++ b/ociserver/registry_test.go @@ -22,8 +22,8 @@ import ( "strings" "testing" - "github.com/jcarter3/oci/ociregistry/ocimem" - "github.com/jcarter3/oci/ociregistry/ociserver" + "github.com/jcarter3/oci/ocimem" + "github.com/jcarter3/oci/ociserver" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/require" ) diff --git a/ociregistry/ociserver/writer.go b/ociserver/writer.go similarity index 93% rename from ociregistry/ociserver/writer.go rename to ociserver/writer.go index da9e2b2..0c7c431 100644 --- a/ociregistry/ociserver/writer.go +++ b/ociserver/writer.go @@ -22,11 +22,11 @@ import ( "net/http" "strconv" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/jcarter3/oci/ociregistry" - "github.com/jcarter3/oci/ociregistry/internal/ocirequest" + "github.com/jcarter3/oci/internal/ocirequest" ) func (r *registry) handleBlobUploadBlob(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { @@ -36,10 +36,10 @@ func (r *registry) handleBlobUploadBlob(ctx context.Context, resp http.ResponseW // TODO check that Content-Type is application/octet-stream? mediaType := mediaTypeOctetStream - desc, err := r.backend.PushBlob(req.Context(), rreq.Repo, ociregistry.Descriptor{ + desc, err := r.backend.PushBlob(req.Context(), rreq.Repo, oci.Descriptor{ MediaType: mediaType, Size: req.ContentLength, - Digest: ociregistry.Digest(rreq.Digest), + Digest: oci.Digest(rreq.Digest), }, req.Body) if err != nil { return err @@ -140,7 +140,7 @@ func (r *registry) handleBlobCompleteUpload(ctx context.Context, resp http.Respo if _, err := io.Copy(w, req.Body); err != nil { return fmt.Errorf("failed to copy data to %T: %v", w, err) } - desc, err := w.Commit(ociregistry.Digest(rreq.Digest)) + desc, err := w.Commit(oci.Digest(rreq.Digest)) if err != nil { return err } @@ -152,7 +152,7 @@ func (r *registry) handleBlobCompleteUpload(ctx context.Context, resp http.Respo } func (r *registry) handleBlobMount(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error { - desc, err := r.backend.MountBlob(ctx, rreq.FromRepo, rreq.Repo, ociregistry.Digest(rreq.Digest)) + desc, err := r.backend.MountBlob(ctx, rreq.FromRepo, rreq.Repo, oci.Digest(rreq.Digest)) if err != nil { return err } @@ -175,12 +175,12 @@ func (r *registry) handleManifestPut(ctx context.Context, resp http.ResponseWrit return fmt.Errorf("cannot read content: %v", err) } dig := digest.FromBytes(data) - params := &ociregistry.PushManifestParameters{} + params := &oci.PushManifestParameters{} if rreq.Tag != "" { params.Tags = []string{rreq.Tag} } else { - if ociregistry.Digest(rreq.Digest) != dig { - return ociregistry.ErrDigestInvalid + if oci.Digest(rreq.Digest) != dig { + return oci.ErrDigestInvalid } params.Tags = rreq.Tags } @@ -203,7 +203,7 @@ func (r *registry) handleManifestPut(ctx context.Context, resp http.ResponseWrit return nil } -func subjectFromManifest(contentType string, data []byte) (*ociregistry.Descriptor, error) { +func subjectFromManifest(contentType string, data []byte) (*oci.Descriptor, error) { switch contentType { case ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: @@ -213,7 +213,7 @@ func subjectFromManifest(contentType string, data []byte) (*ociregistry.Descript return nil, nil } var m struct { - Subject *ociregistry.Descriptor `json:"subject"` + Subject *oci.Descriptor `json:"subject"` } if err := json.Unmarshal(data, &m); err != nil { return nil, err diff --git a/ociregistry/ocitest/ocitest.go b/ocitest/ocitest.go similarity index 84% rename from ociregistry/ocitest/ocitest.go rename to ocitest/ocitest.go index 0dde9aa..91cb366 100644 --- a/ociregistry/ocitest/ocitest.go +++ b/ocitest/ocitest.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package ocitest provides some helper types for writing ociregistry-related +// Package ocitest provides some helper types for writing oci-related // tests. It's designed to be used alongside [stretchr/testify]. // // [stretchr/testify]: https://pkg.go.dev/github.com/stretchr/testify @@ -28,17 +28,16 @@ import ( "strings" "testing" + "github.com/jcarter3/oci" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/require" - - "github.com/jcarter3/oci/ociregistry" ) -// Registry wraps an [ociregistry.Interface] with convenience methods for +// Registry wraps an [oci.Interface] with convenience methods for // pushing and verifying content in tests. type Registry struct { T *testing.T - R ociregistry.Interface + R oci.Interface } // NewRegistry returns a Registry instance that wraps r, providing @@ -46,7 +45,7 @@ type Registry struct { // inside the given test instance. // // When a Must* method fails, it will fail using t. -func NewRegistry(t *testing.T, r ociregistry.Interface) Registry { +func NewRegistry(t *testing.T, r oci.Interface) Registry { return Registry{t, r} } @@ -66,7 +65,7 @@ type RegistryContent map[string]RepoContent type RepoContent struct { // Manifests maps from manifest identifier to the contents of the manifest. // TODO support manifest indexes too. - Manifests map[string]ociregistry.Manifest + Manifests map[string]oci.Manifest // Blobs maps from blob identifer to the contents of the blob. Blobs map[string]string @@ -81,21 +80,21 @@ type RepoContent struct { type PushedRepoContent struct { // Manifests holds an entry for each manifest identifier // with the descriptor for that manifest. - Manifests map[string]ociregistry.Descriptor + Manifests map[string]oci.Descriptor // ManifestData holds the actually pushed data for each manifest. ManifestData map[string][]byte // Blobs holds an entry for each blob identifier // with the descriptor for that manifest. - Blobs map[string]ociregistry.Descriptor + Blobs map[string]oci.Descriptor } // PushContent pushes all the content in rc to r. // // It returns a map mapping repository name to the descriptors // describing the content that has actually been pushed. -func PushContent(r ociregistry.Interface, rc RegistryContent) (map[string]PushedRepoContent, error) { +func PushContent(r oci.Interface, rc RegistryContent) (map[string]PushedRepoContent, error) { regContent := make(map[string]PushedRepoContent) for repo, repoc := range rc { prc, err := PushRepoContent(r, repo, repoc) @@ -108,16 +107,16 @@ func PushContent(r ociregistry.Interface, rc RegistryContent) (map[string]Pushed } // PushRepoContent pushes the content for a single repository. -func PushRepoContent(r ociregistry.Interface, repo string, repoc RepoContent) (PushedRepoContent, error) { +func PushRepoContent(r oci.Interface, repo string, repoc RepoContent) (PushedRepoContent, error) { ctx := context.Background() prc := PushedRepoContent{ - Manifests: make(map[string]ociregistry.Descriptor), + Manifests: make(map[string]oci.Descriptor), ManifestData: make(map[string][]byte), - Blobs: make(map[string]ociregistry.Descriptor), + Blobs: make(map[string]oci.Descriptor), } for id, blob := range repoc.Blobs { - prc.Blobs[id] = ociregistry.Descriptor{ + prc.Blobs[id] = oci.Descriptor{ Digest: digest.FromString(blob), Size: int64(len(blob)), MediaType: "application/binary", @@ -151,7 +150,7 @@ func PushRepoContent(r ociregistry.Interface, repo string, repoc RepoContent) (P if !ok { return PushedRepoContent{}, fmt.Errorf("tag %q refers to unknown manifest id %q", tag, id) } - _, err := r.PushManifest(ctx, repo, mc.data, mc.desc.MediaType, &ociregistry.PushManifestParameters{ + _, err := r.PushManifest(ctx, repo, mc.data, mc.desc.MediaType, &oci.PushManifestParameters{ Tags: []string{tag}, }) if err != nil { @@ -174,13 +173,13 @@ func (r Registry) MustPushContent(rc RegistryContent) map[string]PushedRepoConte type manifestContent struct { id string data []byte - desc ociregistry.Descriptor + desc oci.Descriptor } // completedManifests calculates the content of all the manifests and returns // them all, keyed by id, and a partially ordered sequence suitable // for pushing to a registry in bottom-up order. -func completedManifests(repoc RepoContent, blobs map[string]ociregistry.Descriptor) (map[string]manifestContent, []manifestContent, error) { +func completedManifests(repoc RepoContent, blobs map[string]oci.Descriptor) (map[string]manifestContent, []manifestContent, error) { manifests := make(map[string]manifestContent) manifestSeq := make([]manifestContent, 0, len(repoc.Manifests)) // subject relationships can be arbitrarily deep, so continue iterating until @@ -190,7 +189,7 @@ func completedManifests(repoc RepoContent, blobs map[string]ociregistry.Descript for { madeProgress := false needMore := false - need := func(digest ociregistry.Digest) { + need := func(digest oci.Digest) { needMore = true if !required[string(digest)] { required[string(digest)] = true @@ -220,7 +219,7 @@ func completedManifests(repoc RepoContent, blobs map[string]ociregistry.Descript mc := manifestContent{ id: id, data: data, - desc: ociregistry.Descriptor{ + desc: oci.Descriptor{ Digest: digest.FromBytes(data), Size: int64(len(data)), MediaType: m.MediaType, @@ -244,7 +243,7 @@ func completedManifests(repoc RepoContent, blobs map[string]ociregistry.Descript } } -func fillManifestDescriptors(m ociregistry.Manifest, blobs map[string]ociregistry.Descriptor) ociregistry.Manifest { +func fillManifestDescriptors(m oci.Manifest, blobs map[string]oci.Descriptor) oci.Manifest { m.Config = fillBlobDescriptor(m.Config, blobs) m.Layers = slices.Clone(m.Layers) for i, desc := range m.Layers { @@ -253,7 +252,7 @@ func fillManifestDescriptors(m ociregistry.Manifest, blobs map[string]ociregistr return m } -func fillBlobDescriptor(d ociregistry.Descriptor, blobs map[string]ociregistry.Descriptor) ociregistry.Descriptor { +func fillBlobDescriptor(d oci.Descriptor, blobs map[string]oci.Descriptor) oci.Descriptor { blobDesc, ok := blobs[string(d.Digest)] if !ok { panic(fmt.Errorf("no blob found with id %q", d.Digest)) @@ -267,8 +266,8 @@ func fillBlobDescriptor(d ociregistry.Descriptor, blobs map[string]ociregistry.D } // MustPushBlob pushes a blob to the given repository and fails the test if it encounters an error. -func (r Registry) MustPushBlob(repo string, data []byte) ociregistry.Descriptor { - desc := ociregistry.Descriptor{ +func (r Registry) MustPushBlob(repo string, data []byte) oci.Descriptor { + desc := oci.Descriptor{ Digest: digest.FromBytes(data), Size: int64(len(data)), MediaType: "application/octet-stream", @@ -280,7 +279,7 @@ func (r Registry) MustPushBlob(repo string, data []byte) ociregistry.Descriptor // MustPushManifest marshals jsonObject as a manifest, pushes it to the given repository // with the given tag, and fails the test if it encounters an error. -func (r Registry) MustPushManifest(repo string, jsonObject any, tag string) ([]byte, ociregistry.Descriptor) { +func (r Registry) MustPushManifest(repo string, jsonObject any, tag string) ([]byte, oci.Descriptor) { data, err := json.Marshal(jsonObject) require.NoError(r.T, err) var mt struct { @@ -289,14 +288,14 @@ func (r Registry) MustPushManifest(repo string, jsonObject any, tag string) ([]b err = json.Unmarshal(data, &mt) require.NoError(r.T, err) require.NotEmpty(r.T, mt.MediaType) - desc := ociregistry.Descriptor{ + desc := oci.Descriptor{ Digest: digest.FromBytes(data), Size: int64(len(data)), MediaType: mt.MediaType, } - var params *ociregistry.PushManifestParameters + var params *oci.PushManifestParameters if tag != "" { - params = &ociregistry.PushManifestParameters{ + params = &oci.PushManifestParameters{ Tags: []string{tag}, } } @@ -313,13 +312,13 @@ func (r Registry) MustPushManifest(repo string, jsonObject any, tag string) ([]b type Repo struct { T *testing.T Name string - R ociregistry.Interface + R oci.Interface } // AssertBlobContent checks that r matches the expected data and has the // expected content type. If wantMediaType is empty, "application/octet-stream" // will be expected. -func AssertBlobContent(t *testing.T, r ociregistry.BlobReader, wantData []byte, wantMediaType string) { +func AssertBlobContent(t *testing.T, r oci.BlobReader, wantData []byte, wantMediaType string) { t.Helper() if wantMediaType == "" { wantMediaType = "application/octet-stream" diff --git a/ociregistry/ociunify/deleter.go b/ociunify/deleter.go similarity index 78% rename from ociregistry/ociunify/deleter.go rename to ociunify/deleter.go index c465dcb..d9c3d1e 100644 --- a/ociregistry/ociunify/deleter.go +++ b/ociunify/deleter.go @@ -17,7 +17,7 @@ package ociunify import ( "context" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // Deleter methods @@ -25,20 +25,20 @@ import ( // TODO all these methods should not raise an error if deleting succeeds in one // registry but fails due to a not-found error in the other. -func (u unifier) DeleteBlob(ctx context.Context, repo string, digest ociregistry.Digest) error { - return bothResults(both(u, func(r ociregistry.Interface, _ int) t1 { +func (u unifier) DeleteBlob(ctx context.Context, repo string, digest oci.Digest) error { + return bothResults(both(u, func(r oci.Interface, _ int) t1 { return mk1(r.DeleteBlob(ctx, repo, digest)) })).err } -func (u unifier) DeleteManifest(ctx context.Context, repo string, digest ociregistry.Digest) error { - return bothResults(both(u, func(r ociregistry.Interface, _ int) t1 { +func (u unifier) DeleteManifest(ctx context.Context, repo string, digest oci.Digest) error { + return bothResults(both(u, func(r oci.Interface, _ int) t1 { return mk1(r.DeleteManifest(ctx, repo, digest)) })).err } func (u unifier) DeleteTag(ctx context.Context, repo string, name string) error { - return bothResults(both(u, func(r ociregistry.Interface, _ int) t1 { + return bothResults(both(u, func(r oci.Interface, _ int) t1 { return mk1(r.DeleteTag(ctx, repo, name)) })).err } diff --git a/ociregistry/ociunify/iter_test.go b/ociunify/iter_test.go similarity index 77% rename from ociregistry/ociunify/iter_test.go rename to ociunify/iter_test.go index 7c8afd4..8da1965 100644 --- a/ociregistry/ociunify/iter_test.go +++ b/ociunify/iter_test.go @@ -19,7 +19,7 @@ import ( "iter" "testing" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" "github.com/stretchr/testify/require" ) @@ -30,18 +30,18 @@ var mergeIterTests = []struct { wantErr error }{{ testName: "IdenticalContents", - it0: ociregistry.SliceSeq([]int{1, 2, 3}), - it1: ociregistry.SliceSeq([]int{1, 2, 3}), + it0: oci.SliceSeq([]int{1, 2, 3}), + it1: oci.SliceSeq([]int{1, 2, 3}), want: []int{1, 2, 3}, }, { testName: "DifferentContents", - it0: ociregistry.SliceSeq([]int{0, 1, 2, 3}), - it1: ociregistry.SliceSeq([]int{1, 2, 3, 5}), + it0: oci.SliceSeq([]int{0, 1, 2, 3}), + it1: oci.SliceSeq([]int{1, 2, 3, 5}), want: []int{0, 1, 2, 3, 5}, }, { testName: "NoItems", - it0: ociregistry.SliceSeq[int](nil), - it1: ociregistry.SliceSeq[int](nil), + it0: oci.SliceSeq[int](nil), + it1: oci.SliceSeq[int](nil), want: []int{}, }} @@ -49,7 +49,7 @@ func TestMergeIter(t *testing.T) { for _, test := range mergeIterTests { t.Run(test.testName, func(t *testing.T) { it := mergeIter(test.it0, test.it1, cmp.Compare) - xs, err := ociregistry.All(it) + xs, err := oci.All(it) require.Equal(t, test.want, xs) require.Equal(t, test.wantErr, err) }) diff --git a/ociregistry/ociunify/lister.go b/ociunify/lister.go similarity index 70% rename from ociregistry/ociunify/lister.go rename to ociunify/lister.go index 3aca7a2..02fc760 100644 --- a/ociregistry/ociunify/lister.go +++ b/ociunify/lister.go @@ -21,18 +21,18 @@ import ( "slices" "strings" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) func (u unifier) Repositories(ctx context.Context, startAfter string) iter.Seq2[string, error] { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) iter.Seq2[string, error] { + r0, r1 := both(u, func(r oci.Interface, _ int) iter.Seq2[string, error] { return r.Repositories(ctx, startAfter) }) return mergeIter(r0, r1, strings.Compare) } -func (u unifier) Tags(ctx context.Context, repo string, params *ociregistry.TagsParameters) iter.Seq2[string, error] { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) iter.Seq2[string, error] { +func (u unifier) Tags(ctx context.Context, repo string, params *oci.TagsParameters) iter.Seq2[string, error] { + r0, r1 := both(u, func(r oci.Interface, _ int) iter.Seq2[string, error] { return r.Tags(ctx, repo, params) }) it := mergeIter(r0, r1, strings.Compare) @@ -41,31 +41,31 @@ func (u unifier) Tags(ctx context.Context, repo string, params *ociregistry.Tags limit = params.Limit } if limit > 0 { - return ociregistry.LimitIter(it, limit) + return oci.LimitIter(it, limit) } return it } -func (u unifier) Referrers(ctx context.Context, repo string, digest ociregistry.Digest, params *ociregistry.ReferrersParameters) iter.Seq2[ociregistry.Descriptor, error] { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) iter.Seq2[ociregistry.Descriptor, error] { +func (u unifier) Referrers(ctx context.Context, repo string, digest oci.Digest, params *oci.ReferrersParameters) iter.Seq2[oci.Descriptor, error] { + r0, r1 := both(u, func(r oci.Interface, _ int) iter.Seq2[oci.Descriptor, error] { return r.Referrers(ctx, repo, digest, params) }) return mergeIter(r0, r1, compareDescriptor) } -func compareDescriptor(d0, d1 ociregistry.Descriptor) int { +func compareDescriptor(d0, d1 oci.Descriptor) int { return strings.Compare(string(d0.Digest), string(d1.Digest)) } func mergeIter[T any](it0, it1 iter.Seq2[T, error], cmp func(T, T) int) iter.Seq2[T, error] { // TODO streaming merge sort - xs0, err0 := ociregistry.All(it0) - xs1, err1 := ociregistry.All(it1) + xs0, err0 := oci.All(it0) + xs1, err1 := oci.All(it1) if err0 != nil || err1 != nil { - notFound0 := errors.Is(err0, ociregistry.ErrNameUnknown) - notFound1 := errors.Is(err1, ociregistry.ErrNameUnknown) + notFound0 := errors.Is(err0, oci.ErrNameUnknown) + notFound1 := errors.Is(err1, oci.ErrNameUnknown) if notFound0 && notFound1 { - return ociregistry.ErrorSeq[T](err0) + return oci.ErrorSeq[T](err0) } if notFound0 { err0 = nil @@ -87,7 +87,7 @@ func mergeIter[T any](it0, it1 iter.Seq2[T, error], cmp func(T, T) int) iter.Seq err = err1 } if err == nil { - return ociregistry.SliceSeq(xs) + return oci.SliceSeq(xs) } return func(yield func(T, error) bool) { for _, x := range xs { diff --git a/ociregistry/ociunify/reader.go b/ociunify/reader.go similarity index 67% rename from ociregistry/ociunify/reader.go rename to ociunify/reader.go index 70357e2..f833249 100644 --- a/ociregistry/ociunify/reader.go +++ b/ociunify/reader.go @@ -18,35 +18,35 @@ import ( "context" "fmt" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // Reader methods. -func (u unifier) GetBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { - return runReadBlobReader(ctx, u, func(ctx context.Context, r ociregistry.Interface, i int) t2[ociregistry.BlobReader] { +func (u unifier) GetBlob(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { + return runReadBlobReader(ctx, u, func(ctx context.Context, r oci.Interface, i int) t2[oci.BlobReader] { return mk2(r.GetBlob(ctx, repo, digest)) }) } -func (u unifier) GetBlobRange(ctx context.Context, repo string, digest ociregistry.Digest, o0, o1 int64) (ociregistry.BlobReader, error) { +func (u unifier) GetBlobRange(ctx context.Context, repo string, digest oci.Digest, o0, o1 int64) (oci.BlobReader, error) { return runReadBlobReader(ctx, u, - func(ctx context.Context, r ociregistry.Interface, i int) t2[ociregistry.BlobReader] { + func(ctx context.Context, r oci.Interface, i int) t2[oci.BlobReader] { return mk2(r.GetBlobRange(ctx, repo, digest, o0, o1)) }, ) } -func (u unifier) GetManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.BlobReader, error) { +func (u unifier) GetManifest(ctx context.Context, repo string, digest oci.Digest) (oci.BlobReader, error) { return runReadBlobReader(ctx, u, - func(ctx context.Context, r ociregistry.Interface, i int) t2[ociregistry.BlobReader] { + func(ctx context.Context, r oci.Interface, i int) t2[oci.BlobReader] { return mk2(r.GetManifest(ctx, repo, digest)) }, ) } type blobReader struct { - ociregistry.BlobReader + oci.BlobReader cancel func() } @@ -55,8 +55,8 @@ func (r blobReader) Close() error { return r.BlobReader.Close() } -func (u unifier) GetTag(ctx context.Context, repo string, tagName string) (ociregistry.BlobReader, error) { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) t2[ociregistry.BlobReader] { +func (u unifier) GetTag(ctx context.Context, repo string, tagName string) (oci.BlobReader, error) { + r0, r1 := both(u, func(r oci.Interface, _ int) t2[oci.BlobReader] { return mk2(r.GetTag(ctx, repo, tagName)) }) switch { @@ -78,20 +78,20 @@ func (u unifier) GetTag(ctx context.Context, repo string, tagName string) (ocire panic("unreachable") } -func (u unifier) ResolveBlob(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { - return runRead(ctx, u, func(ctx context.Context, r ociregistry.Interface, _ int) t2[ociregistry.Descriptor] { +func (u unifier) ResolveBlob(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { + return runRead(ctx, u, func(ctx context.Context, r oci.Interface, _ int) t2[oci.Descriptor] { return mk2(r.ResolveBlob(ctx, repo, digest)) }).get() } -func (u unifier) ResolveManifest(ctx context.Context, repo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { - return runRead(ctx, u, func(ctx context.Context, r ociregistry.Interface, _ int) t2[ociregistry.Descriptor] { +func (u unifier) ResolveManifest(ctx context.Context, repo string, digest oci.Digest) (oci.Descriptor, error) { + return runRead(ctx, u, func(ctx context.Context, r oci.Interface, _ int) t2[oci.Descriptor] { return mk2(r.ResolveManifest(ctx, repo, digest)) }).get() } -func (u unifier) ResolveTag(ctx context.Context, repo string, tagName string) (ociregistry.Descriptor, error) { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) t2[ociregistry.Descriptor] { +func (u unifier) ResolveTag(ctx context.Context, repo string, tagName string) (oci.Descriptor, error) { + r0, r1 := both(u, func(r oci.Interface, _ int) t2[oci.Descriptor] { return mk2(r.ResolveTag(ctx, repo, tagName)) }) switch { @@ -99,7 +99,7 @@ func (u unifier) ResolveTag(ctx context.Context, repo string, tagName string) (o if r0.x.Digest == r1.x.Digest { return r0.get() } - return ociregistry.Descriptor{}, fmt.Errorf("conflicting results for tag") + return oci.Descriptor{}, fmt.Errorf("conflicting results for tag") case r0.err != nil && r1.err != nil: return r0.get() case r0.err == nil: @@ -110,7 +110,7 @@ func (u unifier) ResolveTag(ctx context.Context, repo string, tagName string) (o panic("unreachable") } -func runReadBlobReader(ctx context.Context, u unifier, f func(ctx context.Context, r ociregistry.Interface, i int) t2[ociregistry.BlobReader]) (ociregistry.BlobReader, error) { +func runReadBlobReader(ctx context.Context, u unifier, f func(ctx context.Context, r oci.Interface, i int) t2[oci.BlobReader]) (oci.BlobReader, error) { rv, cancel := runReadWithCancel(ctx, u, f) r, err := rv.get() if err != nil { diff --git a/ociregistry/ociunify/unify.go b/ociunify/unify.go similarity index 87% rename from ociregistry/ociunify/unify.go rename to ociunify/unify.go index 9d96057..28ae5d0 100644 --- a/ociregistry/ociunify/unify.go +++ b/ociunify/unify.go @@ -20,7 +20,7 @@ import ( "fmt" "io" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) // Options holds configuration for the unified registry. @@ -46,7 +46,7 @@ const ( // // Writes write to both repositories. Reads of immutable data // come from either. -func New(r0, r1 ociregistry.Interface, opts *Options) ociregistry.Interface { +func New(r0, r1 oci.Interface, opts *Options) oci.Interface { if opts == nil { opts = new(Options) } @@ -58,9 +58,9 @@ func New(r0, r1 ociregistry.Interface, opts *Options) ociregistry.Interface { } type unifier struct { - r0, r1 ociregistry.Interface + r0, r1 oci.Interface opts Options - *ociregistry.Funcs + *oci.Funcs } func bothResults[T result[T]](r0, r1 T) T { @@ -84,7 +84,7 @@ type result[T any] interface { } // both returns the results from calling f on both registries concurrently. -func both[T any](u unifier, f func(r ociregistry.Interface, i int) T) (T, T) { +func both[T any](u unifier, f func(r oci.Interface, i int) T) (T, T) { c0, c1 := make(chan T), make(chan T) go func() { c0 <- f(u.r0, 0) @@ -98,7 +98,7 @@ func both[T any](u unifier, f func(r ociregistry.Interface, i int) T) (T, T) { // runRead calls f concurrently on each registry. // It returns the result from the first one that returns without error. // This should not be used if the return value is affected by cancelling the context. -func runRead[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r ociregistry.Interface, i int) T) T { +func runRead[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r oci.Interface, i int) T) T { r, cancel := runReadWithCancel(ctx, u, f) cancel() return r @@ -107,7 +107,7 @@ func runRead[T result[T]](ctx context.Context, u unifier, f func(ctx context.Con // runReadWithCancel calls f concurrently on each registry. // It returns the result from the first one that returns without error // and a cancel function that should be called when the returned value is done with. -func runReadWithCancel[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r ociregistry.Interface, i int) T) (T, func()) { +func runReadWithCancel[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r oci.Interface, i int) T) (T, func()) { switch u.opts.ReadPolicy { case ReadConcurrent: return runReadConcurrent(ctx, u, f) @@ -118,7 +118,7 @@ func runReadWithCancel[T result[T]](ctx context.Context, u unifier, f func(ctx c } } -func runReadSequential[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r ociregistry.Interface, i int) T) T { +func runReadSequential[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r oci.Interface, i int) T) T { r := f(ctx, u.r0, 0) if err := r.error(); err == nil { return r @@ -126,7 +126,7 @@ func runReadSequential[T result[T]](ctx context.Context, u unifier, f func(ctx c return f(ctx, u.r1, 1) } -func runReadConcurrent[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r ociregistry.Interface, i int) T) (T, func()) { +func runReadConcurrent[T result[T]](ctx context.Context, u unifier, f func(ctx context.Context, r oci.Interface, i int) T) (T, func()) { done := make(chan struct{}) defer close(done) type result struct { @@ -134,7 +134,7 @@ func runReadConcurrent[T result[T]](ctx context.Context, u unifier, f func(ctx c cancel func() } c := make(chan result) - sender := func(f func(context.Context, ociregistry.Interface, int) T, reg ociregistry.Interface, i int) { + sender := func(f func(context.Context, oci.Interface, int) T, reg oci.Interface, i int) { ctx, cancel := context.WithCancel(ctx) r := f(ctx, reg, i) select { diff --git a/ociregistry/ociunify/writer.go b/ociunify/writer.go similarity index 68% rename from ociregistry/ociunify/writer.go rename to ociunify/writer.go index 899c779..e4acd27 100644 --- a/ociregistry/ociunify/writer.go +++ b/ociunify/writer.go @@ -21,15 +21,15 @@ import ( "fmt" "io" - "github.com/jcarter3/oci/ociregistry" + "github.com/jcarter3/oci" ) -func (u unifier) PushBlob(ctx context.Context, repo string, desc ociregistry.Descriptor, r io.Reader) (ociregistry.Descriptor, error) { - resultc := make(chan t2[ociregistry.Descriptor]) - onePush := func(ri ociregistry.Interface, r *io.PipeReader) { +func (u unifier) PushBlob(ctx context.Context, repo string, desc oci.Descriptor, r io.Reader) (oci.Descriptor, error) { + resultc := make(chan t2[oci.Descriptor]) + onePush := func(ri oci.Interface, r *io.PipeReader) { desc, err := ri.PushBlob(ctx, repo, desc, r) r.CloseWithError(err) - resultc <- t2[ociregistry.Descriptor]{desc, err} + resultc <- t2[oci.Descriptor]{desc, err} } pr0, pw0 := io.Pipe() pr1, pw1 := io.Pipe() @@ -45,21 +45,21 @@ func (u unifier) PushBlob(ctx context.Context, repo string, desc ociregistry.Des if (r0.err == nil) == (r1.err == nil) { return r0.get() } - return ociregistry.Descriptor{}, fmt.Errorf("one push succeeded where the other failed (TODO better error)") + return oci.Descriptor{}, fmt.Errorf("one push succeeded where the other failed (TODO better error)") } -func (u unifier) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *ociregistry.PushManifestParameters) (ociregistry.Descriptor, error) { - r0, r1 := both(u, func(r ociregistry.Interface, _ int) t2[ociregistry.Descriptor] { +func (u unifier) PushManifest(ctx context.Context, repo string, contents []byte, mediaType string, params *oci.PushManifestParameters) (oci.Descriptor, error) { + r0, r1 := both(u, func(r oci.Interface, _ int) t2[oci.Descriptor] { return mk2(r.PushManifest(ctx, repo, contents, mediaType, params)) }) if (r0.err == nil) == (r1.err == nil) { return r0.get() } - return ociregistry.Descriptor{}, fmt.Errorf("one push succeeded where the other failed (TODO better error)") + return oci.Descriptor{}, fmt.Errorf("one push succeeded where the other failed (TODO better error)") } -func (u unifier) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (ociregistry.BlobWriter, error) { - r0, r1 := both(u, func(r ociregistry.Interface, i int) t2[ociregistry.BlobWriter] { +func (u unifier) PushBlobChunked(ctx context.Context, repo string, chunkSize int) (oci.BlobWriter, error) { + r0, r1 := both(u, func(r oci.Interface, i int) t2[oci.BlobWriter] { return mk2(r.PushBlobChunked(ctx, repo, chunkSize)) }) if r0.err != nil || r1.err != nil { @@ -70,12 +70,12 @@ func (u unifier) PushBlobChunked(ctx context.Context, repo string, chunkSize int w0, w1 := r0.x, r1.x size := w0.Size() // assumed to agree with w1.Size return &unifiedBlobWriter{ - w: [2]ociregistry.BlobWriter{w0, w1}, + w: [2]oci.BlobWriter{w0, w1}, size: size, }, nil } -func (u unifier) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) { +func (u unifier) PushBlobChunkedResume(ctx context.Context, repo, id string, offset int64, chunkSize int) (oci.BlobWriter, error) { data, err := base64.RawURLEncoding.DecodeString(id) if err != nil { return nil, fmt.Errorf("malformed ID: %v", err) @@ -87,7 +87,7 @@ func (u unifier) PushBlobChunkedResume(ctx context.Context, repo, id string, off if len(ids) != 2 { return nil, fmt.Errorf("malformed ID %q (expected two elements)", id) } - r0, r1 := both(u, func(r ociregistry.Interface, i int) t2[ociregistry.BlobWriter] { + r0, r1 := both(u, func(r oci.Interface, i int) t2[oci.BlobWriter] { return mk2(r.PushBlobChunkedResume(ctx, repo, ids[i], offset, chunkSize)) }) if r0.err != nil || r1.err != nil { @@ -103,14 +103,14 @@ func (u unifier) PushBlobChunkedResume(ctx context.Context, repo, id string, off return nil, fmt.Errorf("registries do not agree on upload size; please start upload again") } return &unifiedBlobWriter{ - w: [2]ociregistry.BlobWriter{w0, w1}, + w: [2]oci.BlobWriter{w0, w1}, size: size, }, nil } -func (u unifier) MountBlob(ctx context.Context, fromRepo, toRepo string, digest ociregistry.Digest) (ociregistry.Descriptor, error) { +func (u unifier) MountBlob(ctx context.Context, fromRepo, toRepo string, digest oci.Digest) (oci.Descriptor, error) { return bothResults(both(u, - func(r ociregistry.Interface, _ int) t2[ociregistry.Descriptor] { + func(r oci.Interface, _ int) t2[oci.Descriptor] { return mk2(r.MountBlob(ctx, fromRepo, toRepo, digest)) }, )).get() @@ -118,12 +118,12 @@ func (u unifier) MountBlob(ctx context.Context, fromRepo, toRepo string, digest type unifiedBlobWriter struct { u unifier - w [2]ociregistry.BlobWriter + w [2]oci.BlobWriter size int64 } func (w *unifiedBlobWriter) Write(buf []byte) (int, error) { - r := bothResults(both(w.u, func(_ ociregistry.Interface, i int) t2[int] { + r := bothResults(both(w.u, func(_ oci.Interface, i int) t2[int] { return mk2(w.w[i].Write(buf)) })) if r.err != nil { @@ -134,13 +134,13 @@ func (w *unifiedBlobWriter) Write(buf []byte) (int, error) { } func (w *unifiedBlobWriter) Close() error { - return bothResults(both(w.u, func(_ ociregistry.Interface, i int) t1 { + return bothResults(both(w.u, func(_ oci.Interface, i int) t1 { return mk1(w.w[i].Close()) })).err } func (w *unifiedBlobWriter) Cancel() error { - return bothResults(both(w.u, func(_ ociregistry.Interface, i int) t1 { + return bothResults(both(w.u, func(_ oci.Interface, i int) t1 { return mk1(w.w[i].Cancel()) })).err } @@ -160,8 +160,8 @@ func (w *unifiedBlobWriter) ID() string { return base64.RawURLEncoding.EncodeToString(data) } -func (w *unifiedBlobWriter) Commit(digest ociregistry.Digest) (ociregistry.Descriptor, error) { - return bothResults(both(w.u, func(_ ociregistry.Interface, i int) t2[ociregistry.Descriptor] { +func (w *unifiedBlobWriter) Commit(digest oci.Digest) (oci.Descriptor, error) { + return bothResults(both(w.u, func(_ oci.Interface, i int) t2[oci.Descriptor] { return mk2(w.w[i].Commit(digest)) })).get() } diff --git a/ociregistry/valid.go b/valid.go similarity index 90% rename from ociregistry/valid.go rename to valid.go index c1f691b..671330e 100644 --- a/ociregistry/valid.go +++ b/valid.go @@ -1,7 +1,7 @@ -package ociregistry +package oci import ( - "github.com/jcarter3/oci/ociregistry/ociref" + "github.com/jcarter3/oci/ociref" ) // IsValidRepoName reports whether the given repository