// Source: https://github.com/distribution/distribution // Copyright 2014 https://github.com/distribution/distribution Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package storage import ( "context" "errors" "fmt" "io" "github.com/harness/gitness/registry/app/manifest" "github.com/distribution/reference" "github.com/opencontainers/go-digest" ) var ( // ErrBlobExists returned when blob already exists. ErrBlobExists = errors.New("blob exists") // ErrBlobDigestUnsupported when blob digest is an unsupported version. ErrBlobDigestUnsupported = errors.New("unsupported blob digest") // ErrBlobUnknown when blob is not found. ErrBlobUnknown = errors.New("unknown blob") // ErrBlobUploadUnknown returned when upload is not found. ErrBlobUploadUnknown = errors.New("blob upload unknown") // ErrBlobInvalidLength returned when the blob has an expected length on // commit, meaning mismatched with the descriptor or an invalid value. ErrBlobInvalidLength = errors.New("blob invalid length") ) // BlobInvalidDigestError returned when digest check fails. type BlobInvalidDigestError struct { Digest digest.Digest Reason error } func (err BlobInvalidDigestError) Error() string { return fmt.Sprintf( "invalid digest for referenced layer: %v, %v", err.Digest, err.Reason, ) } // BlobMountedError returned when a blob is mounted from another repository // instead of initiating an upload session. type BlobMountedError struct { From reference.Canonical Descriptor manifest.Descriptor } func (err BlobMountedError) Error() string { return fmt.Sprintf( "blob mounted from: %v to: %v", err.From, err.Descriptor, ) } // BlobWriter provides a handle for inserting data into a blob store. // Instances should be obtained from BlobWriteService.Writer and // BlobWriteService.Resume. If supported by the store, a writer can be // recovered with the id. type BlobWriter interface { io.WriteCloser // Size returns the number of bytes written to this blob. Size() int64 // ID returns the identifier for this writer. The ID can be used with the // Blob service to later resume the write. ID() string // Commit completes the blob writer process. The content is verified // against the provided provisional descriptor, which may result in an // error. Depending on the implementation, written data may be validated // against the provisional descriptor fields. If MediaType is not present, // the implementation may reject the commit or assign "application/octet- // stream" to the blob. The returned descriptor may have a different // digest depending on the blob store, referred to as the canonical // descriptor. Commit(ctx context.Context, pathPrefix string, provisional manifest.Descriptor) ( canonical manifest.Descriptor, err error, ) // Cancel ends the blob write without storing any data and frees any // associated resources. Any data written thus far will be lost. Cancel // implementations should allow multiple calls even after a commit that // result in a no-op. This allows use of Cancel in a defer statement, // increasing the assurance that it is correctly called. Cancel(ctx context.Context) error } // OciBlobStore represent the entire suite of blob related operations. Such an // implementation can access, read, write, delete and serve blobs. type OciBlobStore interface { // ServeBlobInternal attempts to serve the blob, identified by dgst, via http. The // service may decide to redirect the client elsewhere or serve the data // directly. // // This handler only issues successful responses, such as 2xx or 3xx, // meaning it serves data or issues a redirect. If the blob is not // available, an error will be returned and the caller may still issue a // response. // // The implementation may serve the same blob from a different digest // domain. The appropriate headers will be set for the blob, unless they // have already been set by the caller. ServeBlobInternal( ctx context.Context, pathPrefix string, dgst digest.Digest, headers map[string]string, method string, ) (*FileReader, string, int64, error) Delete(ctx context.Context, pathPrefix string, dgst digest.Digest) error // Stat provides metadata about a blob identified by the digest. If the // blob is unknown to the describer, ErrBlobUnknown will be returned. Stat(ctx context.Context, pathPrefix string, dgst digest.Digest) (manifest.Descriptor, error) // Get returns the entire blob identified by digest along with the descriptor. Get(ctx context.Context, pathPrefix string, dgst digest.Digest) ([]byte, error) // Open provides an [io.ReadSeekCloser] to the blob identified by the provided // descriptor. If the blob is not known to the service, an error is returned. Open(ctx context.Context, pathPrefix string, dgst digest.Digest) (io.ReadSeekCloser, error) // Put inserts the content p into the blob service, returning a descriptor // or an error. Put(ctx context.Context, pathPrefix string, p []byte) (manifest.Descriptor, error) // Create allocates a new blob writer to add a blob to this service. The // returned handle can be written to and later resumed using an opaque // identifier. With this approach, one can Close and Resume a BlobWriter // multiple times until the BlobWriter is committed or cancelled. Create(ctx context.Context) (BlobWriter, error) // Resume attempts to resume a write to a blob, identified by an id. Resume(ctx context.Context, id string) (BlobWriter, error) Path() string }