package archive import ( "context" "io" "github.com/containers/image/v5/docker/internal/tarfile" "github.com/containers/image/v5/types" "github.com/pkg/errors" ) type archiveImageDestination struct { *tarfile.Destination // Implements most of types.ImageDestination ref archiveReference archive *tarfile.Writer // Should only be closed if writer != nil writer io.Closer // May be nil if the archive is shared } func newImageDestination(sys *types.SystemContext, ref archiveReference) (types.ImageDestination, error) { if ref.sourceIndex != -1 { return nil, errors.Errorf("Destination reference must not contain a manifest index @%d", ref.sourceIndex) } var archive *tarfile.Writer var writer io.Closer if ref.archiveWriter != nil { archive = ref.archiveWriter writer = nil } else { fh, err := openArchiveForWriting(ref.path) if err != nil { return nil, err } archive = tarfile.NewWriter(fh) writer = fh } tarDest := tarfile.NewDestination(sys, archive, ref.ref) if sys != nil && sys.DockerArchiveAdditionalTags != nil { tarDest.AddRepoTags(sys.DockerArchiveAdditionalTags) } return &archiveImageDestination{ Destination: tarDest, ref: ref, archive: archive, writer: writer, }, nil } // DesiredLayerCompression indicates if layers must be compressed, decompressed or preserved func (d *archiveImageDestination) DesiredLayerCompression() types.LayerCompression { return types.Decompress } // Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent, // e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects. func (d *archiveImageDestination) Reference() types.ImageReference { return d.ref } // Close removes resources associated with an initialized ImageDestination, if any. func (d *archiveImageDestination) Close() error { if d.writer != nil { return d.writer.Close() } return nil } // Commit marks the process of storing the image as successful and asks for the image to be persisted. // unparsedToplevel contains data about the top-level manifest of the source (which may be a single-arch image or a manifest list // if PutManifest was only called for the single-arch image with instanceDigest == nil), primarily to allow lookups by the // original manifest list digest, if desired. // WARNING: This does not have any transactional semantics: // - Uploaded data MAY be visible to others before Commit() is called // - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed) func (d *archiveImageDestination) Commit(ctx context.Context, unparsedToplevel types.UnparsedImage) error { if d.writer != nil { return d.archive.Close() } return nil }