mirror of
https://github.com/containers/podman.git
synced 2025-09-28 17:25:31 +08:00
Bump github.com/containers/image/v5 from 5.18.0 to 5.19.0
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.18.0 to 5.19.0. - [Release notes](https://github.com/containers/image/releases) - [Commits](https://github.com/containers/image/compare/v5.18.0...v5.19.0) --- updated-dependencies: - dependency-name: github.com/containers/image/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
5
vendor/github.com/containers/image/v5/copy/copy.go
generated
vendored
5
vendor/github.com/containers/image/v5/copy/copy.go
generated
vendored
@ -124,6 +124,7 @@ type ImageListSelection int
|
||||
type Options struct {
|
||||
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
|
||||
SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(),
|
||||
SignPassphrase string // Passphare to use when signing with the key ID from `SignBy`.
|
||||
ReportWriter io.Writer
|
||||
SourceCtx *types.SystemContext
|
||||
DestinationCtx *types.SystemContext
|
||||
@ -569,7 +570,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
|
||||
|
||||
// Sign the manifest list.
|
||||
if options.SignBy != "" {
|
||||
newSig, err := c.createSignature(manifestList, options.SignBy)
|
||||
newSig, err := c.createSignature(manifestList, options.SignBy, options.SignPassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -791,7 +792,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
|
||||
}
|
||||
|
||||
if options.SignBy != "" {
|
||||
newSig, err := c.createSignature(manifestBytes, options.SignBy)
|
||||
newSig, err := c.createSignature(manifestBytes, options.SignBy, options.SignPassphrase)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
4
vendor/github.com/containers/image/v5/copy/sign.go
generated
vendored
4
vendor/github.com/containers/image/v5/copy/sign.go
generated
vendored
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// createSignature creates a new signature of manifest using keyIdentity.
|
||||
func (c *copier) createSignature(manifest []byte, keyIdentity string) ([]byte, error) {
|
||||
func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string) ([]byte, error) {
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "initializing GPG")
|
||||
@ -23,7 +23,7 @@ func (c *copier) createSignature(manifest []byte, keyIdentity string) ([]byte, e
|
||||
}
|
||||
|
||||
c.Printf("Signing manifest\n")
|
||||
newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, keyIdentity)
|
||||
newSig, err := signature.SignDockerManifestWithOptions(manifest, dockerReference.String(), mech, keyIdentity, &signature.SignOptions{Passphrase: passphrase})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating signature")
|
||||
}
|
||||
|
4
vendor/github.com/containers/image/v5/manifest/common.go
generated
vendored
4
vendor/github.com/containers/image/v5/manifest/common.go
generated
vendored
@ -51,7 +51,7 @@ const (
|
||||
// other than the ones the caller specifically allows.
|
||||
// expectedMIMEType is used only for diagnostics.
|
||||
// NOTE: The caller should do the non-heuristic validations (e.g. check for any specified format
|
||||
// identification/version, or other “magic numbers”) before calling this, to cleanly reject unambigous
|
||||
// identification/version, or other “magic numbers”) before calling this, to cleanly reject unambiguous
|
||||
// data that just isn’t what was expected, as opposed to actually ambiguous data.
|
||||
func validateUnambiguousManifestFormat(manifest []byte, expectedMIMEType string,
|
||||
allowed allowedManifestFields) error {
|
||||
@ -71,7 +71,7 @@ func validateUnambiguousManifestFormat(manifest []byte, expectedMIMEType string,
|
||||
Manifests interface{} `json:"manifests"`
|
||||
}{}
|
||||
if err := json.Unmarshal(manifest, &detectedFields); err != nil {
|
||||
// The caller was supposed to already validate version numbers, so this shold not happen;
|
||||
// The caller was supposed to already validate version numbers, so this should not happen;
|
||||
// let’s not bother with making this error “nice”.
|
||||
return err
|
||||
}
|
||||
|
4
vendor/github.com/containers/image/v5/pkg/docker/config/config.go
generated
vendored
4
vendor/github.com/containers/image/v5/pkg/docker/config/config.go
generated
vendored
@ -667,6 +667,7 @@ func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types
|
||||
// This intentionally uses "registry", not "key"; we don't support namespaced
|
||||
// credentials in helpers.
|
||||
if ch, exists := auths.CredHelpers[registry]; exists {
|
||||
logrus.Debugf("Looking up in credential helper %s based on credHelpers entry in %s", ch, path)
|
||||
return getAuthFromCredHelper(ch, registry)
|
||||
}
|
||||
|
||||
@ -703,6 +704,9 @@ func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types
|
||||
}
|
||||
}
|
||||
|
||||
// Only log this if we found nothing; getCredentialsWithHomeDir logs the
|
||||
// source of found data.
|
||||
logrus.Debugf("No credentials matching %s found in %s", key, path)
|
||||
return types.DockerAuthConfig{}, nil
|
||||
}
|
||||
|
||||
|
211
vendor/github.com/containers/image/v5/sif/load.go
generated
vendored
Normal file
211
vendor/github.com/containers/image/v5/sif/load.go
generated
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
package sif
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sylabs/sif/v2/pkg/sif"
|
||||
)
|
||||
|
||||
// injectedScriptTargetPath is the path injectedScript should be written to in the created image.
|
||||
const injectedScriptTargetPath = "/podman/runscript"
|
||||
|
||||
// parseDefFile parses a SIF definition file from reader,
|
||||
// and returns non-trivial contents of the %environment and %runscript sections.
|
||||
func parseDefFile(reader io.Reader) ([]string, []string, error) {
|
||||
type parserState int
|
||||
const (
|
||||
parsingOther parserState = iota
|
||||
parsingEnvironment
|
||||
parsingRunscript
|
||||
)
|
||||
|
||||
environment := []string{}
|
||||
runscript := []string{}
|
||||
|
||||
state := parsingOther
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
switch {
|
||||
case s == `%environment`:
|
||||
state = parsingEnvironment
|
||||
case s == `%runscript`:
|
||||
state = parsingRunscript
|
||||
case strings.HasPrefix(s, "%"):
|
||||
state = parsingOther
|
||||
case state == parsingEnvironment:
|
||||
if s != "" && !strings.HasPrefix(s, "#") {
|
||||
environment = append(environment, s)
|
||||
}
|
||||
case state == parsingRunscript:
|
||||
runscript = append(runscript, s)
|
||||
default: // parsingOther: ignore the line
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, nil, fmt.Errorf("reading lines from SIF definition file object: %w", err)
|
||||
}
|
||||
return environment, runscript, nil
|
||||
}
|
||||
|
||||
// generateInjectedScript generates a shell script based on
|
||||
// SIF definition file %environment and %runscript data, and returns it.
|
||||
func generateInjectedScript(environment []string, runscript []string) []byte {
|
||||
script := fmt.Sprintf("#!/bin/bash\n"+
|
||||
"%s\n"+
|
||||
"%s\n", strings.Join(environment, "\n"), strings.Join(runscript, "\n"))
|
||||
return []byte(script)
|
||||
}
|
||||
|
||||
// processDefFile finds sif.DataDeffile in sifImage, if any,
|
||||
// and returns:
|
||||
// - the command to run
|
||||
// - contents of a script to inject as injectedScriptTargetPath, or nil
|
||||
func processDefFile(sifImage *sif.FileImage) (string, []byte, error) {
|
||||
var environment, runscript []string
|
||||
|
||||
desc, err := sifImage.GetDescriptor(sif.WithDataType(sif.DataDeffile))
|
||||
if err == nil {
|
||||
environment, runscript, err = parseDefFile(desc.GetReader())
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var command string
|
||||
var injectedScript []byte
|
||||
if len(environment) == 0 && len(runscript) == 0 {
|
||||
command = "bash"
|
||||
injectedScript = nil
|
||||
} else {
|
||||
injectedScript = generateInjectedScript(environment, runscript)
|
||||
command = injectedScriptTargetPath
|
||||
}
|
||||
|
||||
return command, injectedScript, nil
|
||||
}
|
||||
|
||||
func writeInjectedScript(extractedRootPath string, injectedScript []byte) error {
|
||||
if injectedScript == nil {
|
||||
return nil
|
||||
}
|
||||
filePath := filepath.Join(extractedRootPath, injectedScriptTargetPath)
|
||||
parentDirPath := filepath.Dir(filePath)
|
||||
if err := os.MkdirAll(parentDirPath, 0755); err != nil {
|
||||
return fmt.Errorf("creating %s: %w", parentDirPath, err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filePath, injectedScript, 0755); err != nil {
|
||||
return fmt.Errorf("writing %s to %s: %w", injectedScriptTargetPath, filePath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTarFromSIFInputs creates a tar file at tarPath, using a squashfs image at squashFSPath.
|
||||
// It can also use extractedRootPath and scriptPath, which are allocated for its exclusive use,
|
||||
// if necessary.
|
||||
func createTarFromSIFInputs(ctx context.Context, tarPath, squashFSPath string, injectedScript []byte, extractedRootPath, scriptPath string) error {
|
||||
// It's safe for the Remove calls to happen even before we create the files, because tempDir is exclusive
|
||||
// for our use.
|
||||
defer os.RemoveAll(extractedRootPath)
|
||||
|
||||
// Almost everything in extractedRootPath comes from squashFSPath.
|
||||
conversionCommand := fmt.Sprintf("unsquashfs -d %s -f %s && tar --acls --xattrs -C %s -cpf %s ./",
|
||||
extractedRootPath, squashFSPath, extractedRootPath, tarPath)
|
||||
script := "#!/bin/sh\n" + conversionCommand + "\n"
|
||||
if err := ioutil.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(scriptPath)
|
||||
|
||||
// On top of squashFSPath, we only add injectedScript, if necessary.
|
||||
if err := writeInjectedScript(extractedRootPath, injectedScript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("Converting squashfs to tar, command: %s ...", conversionCommand)
|
||||
cmd := exec.CommandContext(ctx, "fakeroot", "--", scriptPath)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting image: %w, output: %s", err, string(output))
|
||||
}
|
||||
logrus.Debugf("... finished converting squashfs to tar")
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertSIFToElements processes sifImage and creates/returns
|
||||
// the relevant elements for constructing an OCI-like image:
|
||||
// - A path to a tar file containing a root filesystem,
|
||||
// - A command to run.
|
||||
// The returned tar file path is inside tempDir, which can be assumed to be empty
|
||||
// at start, and is exclusively used by the current process (i.e. it is safe
|
||||
// to use hard-coded relative paths within it).
|
||||
func convertSIFToElements(ctx context.Context, sifImage *sif.FileImage, tempDir string) (string, []string, error) {
|
||||
// We could allocate unique names for all of these using ioutil.Temp*, but tempDir is exclusive,
|
||||
// so we can just hard-code a set of unique values here.
|
||||
// We create and/or manage cleanup of these two paths.
|
||||
squashFSPath := filepath.Join(tempDir, "rootfs.squashfs")
|
||||
tarPath := filepath.Join(tempDir, "rootfs.tar")
|
||||
// We only allocate these paths, the user is responsible for cleaning them up.
|
||||
extractedRootPath := filepath.Join(tempDir, "rootfs")
|
||||
scriptPath := filepath.Join(tempDir, "script")
|
||||
|
||||
succeeded := false
|
||||
// It's safe for the Remove calls to happen even before we create the files, because tempDir is exclusive
|
||||
// for our use.
|
||||
// Ideally we would remove squashFSPath immediately after creating extractedRootPath, but we need
|
||||
// to run both creation and consumption of extractedRootPath in the same fakeroot context.
|
||||
// So, overall, this process requires at least 2 compressed copies (SIF and squashFSPath) and 2
|
||||
// uncompressed copies (extractedRootPath and tarPath) of the data, all using up space at the same time.
|
||||
// That's rather unsatisfactory, ideally we would be streaming the data directly from a squashfs parser
|
||||
// reading from the SIF file to a tarball, for 1 compressed and 1 uncompressed copy.
|
||||
defer os.Remove(squashFSPath)
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
os.Remove(tarPath)
|
||||
}
|
||||
}()
|
||||
|
||||
command, injectedScript, err := processDefFile(sifImage)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
rootFS, err := sifImage.GetDescriptor(sif.WithPartitionType(sif.PartPrimSys))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("looking up rootfs from SIF file: %w", err)
|
||||
}
|
||||
// TODO: We'd prefer not to make a full copy of the file here; unsquashfs ≥ 4.4
|
||||
// has an -o option that allows extracting a squashfs from the SIF file directly,
|
||||
// but that version is not currently available in RHEL 8.
|
||||
logrus.Debugf("Creating a temporary squashfs image %s ...", squashFSPath)
|
||||
if err := func() error { // A scope for defer
|
||||
f, err := os.Create(squashFSPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// TODO: This can take quite some time, and should ideally be cancellable using ctx.Done().
|
||||
if _, err := io.CopyN(f, rootFS.GetReader(), rootFS.Size()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
logrus.Debugf("... finished creating a temporary squashfs image")
|
||||
|
||||
if err := createTarFromSIFInputs(ctx, tarPath, squashFSPath, injectedScript, extractedRootPath, scriptPath); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
succeeded = true
|
||||
return tarPath, []string{command}, nil
|
||||
}
|
217
vendor/github.com/containers/image/v5/sif/src.go
generated
vendored
Normal file
217
vendor/github.com/containers/image/v5/sif/src.go
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
package sif
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/v5/internal/tmpdir"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
imgspecs "github.com/opencontainers/image-spec/specs-go"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sylabs/sif/v2/pkg/sif"
|
||||
)
|
||||
|
||||
type sifImageSource struct {
|
||||
ref sifReference
|
||||
workDir string
|
||||
layerDigest digest.Digest
|
||||
layerSize int64
|
||||
layerFile string
|
||||
config []byte
|
||||
configDigest digest.Digest
|
||||
manifest []byte
|
||||
}
|
||||
|
||||
// getBlobInfo returns the digest, and size of the provided file.
|
||||
func getBlobInfo(path string) (digest.Digest, int64, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", -1, fmt.Errorf("opening %q for reading: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// TODO: Instead of writing the tar file to disk, and reading
|
||||
// it here again, stream the tar file to a pipe and
|
||||
// compute the digest while writing it to disk.
|
||||
logrus.Debugf("Computing a digest of the SIF conversion output...")
|
||||
digester := digest.Canonical.Digester()
|
||||
// TODO: This can take quite some time, and should ideally be cancellable using ctx.Done().
|
||||
size, err := io.Copy(digester.Hash(), f)
|
||||
if err != nil {
|
||||
return "", -1, fmt.Errorf("reading %q: %w", path, err)
|
||||
}
|
||||
digest := digester.Digest()
|
||||
logrus.Debugf("... finished computing the digest of the SIF conversion output")
|
||||
|
||||
return digest, size, nil
|
||||
}
|
||||
|
||||
// newImageSource returns an ImageSource for reading from an existing directory.
|
||||
// newImageSource extracts SIF objects and saves them in a temp directory.
|
||||
func newImageSource(ctx context.Context, sys *types.SystemContext, ref sifReference) (types.ImageSource, error) {
|
||||
sifImg, err := sif.LoadContainerFromPath(ref.file, sif.OptLoadWithFlag(os.O_RDONLY))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading SIF file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = sifImg.UnloadContainer()
|
||||
}()
|
||||
|
||||
workDir, err := ioutil.TempDir(tmpdir.TemporaryDirectoryForBigFiles(sys), "sif")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating temp directory: %w", err)
|
||||
}
|
||||
succeeded := false
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
os.RemoveAll(workDir)
|
||||
}
|
||||
}()
|
||||
|
||||
layerPath, commandLine, err := convertSIFToElements(ctx, sifImg, workDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting rootfs from SquashFS to Tarball: %w", err)
|
||||
}
|
||||
|
||||
layerDigest, layerSize, err := getBlobInfo(layerPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gathering blob information: %w", err)
|
||||
}
|
||||
|
||||
created := sifImg.ModifiedAt()
|
||||
config := imgspecv1.Image{
|
||||
Created: &created,
|
||||
Architecture: sifImg.PrimaryArch(),
|
||||
OS: "linux",
|
||||
Config: imgspecv1.ImageConfig{
|
||||
Cmd: commandLine,
|
||||
},
|
||||
RootFS: imgspecv1.RootFS{
|
||||
Type: "layers",
|
||||
DiffIDs: []digest.Digest{layerDigest},
|
||||
},
|
||||
History: []imgspecv1.History{
|
||||
{
|
||||
Created: &created,
|
||||
CreatedBy: fmt.Sprintf("/bin/sh -c #(nop) ADD file:%s in %c", layerDigest.Hex(), os.PathSeparator),
|
||||
Comment: "imported from SIF, uuid: " + sifImg.ID(),
|
||||
},
|
||||
{
|
||||
Created: &created,
|
||||
CreatedBy: "/bin/sh -c #(nop) CMD [\"bash\"]",
|
||||
EmptyLayer: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
configBytes, err := json.Marshal(&config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generating configuration blob for %q: %w", ref.resolvedFile, err)
|
||||
}
|
||||
configDigest := digest.Canonical.FromBytes(configBytes)
|
||||
|
||||
manifest := imgspecv1.Manifest{
|
||||
Versioned: imgspecs.Versioned{SchemaVersion: 2},
|
||||
MediaType: imgspecv1.MediaTypeImageManifest,
|
||||
Config: imgspecv1.Descriptor{
|
||||
Digest: configDigest,
|
||||
Size: int64(len(configBytes)),
|
||||
MediaType: imgspecv1.MediaTypeImageConfig,
|
||||
},
|
||||
Layers: []imgspecv1.Descriptor{{
|
||||
Digest: layerDigest,
|
||||
Size: layerSize,
|
||||
MediaType: imgspecv1.MediaTypeImageLayer,
|
||||
}},
|
||||
}
|
||||
manifestBytes, err := json.Marshal(&manifest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generating manifest for %q: %w", ref.resolvedFile, err)
|
||||
}
|
||||
|
||||
succeeded = true
|
||||
return &sifImageSource{
|
||||
ref: ref,
|
||||
workDir: workDir,
|
||||
layerDigest: layerDigest,
|
||||
layerSize: layerSize,
|
||||
layerFile: layerPath,
|
||||
config: configBytes,
|
||||
configDigest: configDigest,
|
||||
manifest: manifestBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Reference returns the reference used to set up this source.
|
||||
func (s *sifImageSource) Reference() types.ImageReference {
|
||||
return s.ref
|
||||
}
|
||||
|
||||
// Close removes resources associated with an initialized ImageSource, if any.
|
||||
func (s *sifImageSource) Close() error {
|
||||
return os.RemoveAll(s.workDir)
|
||||
}
|
||||
|
||||
// HasThreadSafeGetBlob indicates whether GetBlob can be executed concurrently.
|
||||
func (s *sifImageSource) HasThreadSafeGetBlob() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
|
||||
// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
|
||||
// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location.
|
||||
func (s *sifImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) {
|
||||
switch info.Digest {
|
||||
case s.configDigest:
|
||||
return ioutil.NopCloser(bytes.NewBuffer(s.config)), int64(len(s.config)), nil
|
||||
case s.layerDigest:
|
||||
reader, err := os.Open(s.layerFile)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("opening %q: %w", s.layerFile, err)
|
||||
}
|
||||
return reader, s.layerSize, nil
|
||||
default:
|
||||
return nil, -1, fmt.Errorf("no blob with digest %q found", info.Digest.String())
|
||||
}
|
||||
}
|
||||
|
||||
// GetManifest returns the image's manifest along with its MIME type (which may be empty when it can't be determined but the manifest is available).
|
||||
// It may use a remote (= slow) service.
|
||||
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
|
||||
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
|
||||
func (s *sifImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
|
||||
if instanceDigest != nil {
|
||||
return nil, "", errors.New("manifest lists are not supported by the sif transport")
|
||||
}
|
||||
return s.manifest, imgspecv1.MediaTypeImageManifest, nil
|
||||
}
|
||||
|
||||
// GetSignatures returns the image's signatures. It may use a remote (= slow) service.
|
||||
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve signatures for
|
||||
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||
// (e.g. if the source never returns manifest lists).
|
||||
func (s *sifImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
|
||||
if instanceDigest != nil {
|
||||
return nil, errors.New("manifest lists are not supported by the sif transport")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer
|
||||
// blobsums that are listed in the image's manifest. If values are returned, they should be used when using GetBlob()
|
||||
// to read the image's layers.
|
||||
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve BlobInfos for
|
||||
// (when the primary manifest is a manifest list); this never happens if the primary manifest is not a manifest list
|
||||
// (e.g. if the source never returns manifest lists).
|
||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||
func (s *sifImageSource) LayerInfosForCopy(ctx context.Context, instanceDigest *digest.Digest) ([]types.BlobInfo, error) {
|
||||
return nil, nil
|
||||
}
|
164
vendor/github.com/containers/image/v5/sif/transport.go
generated
vendored
Normal file
164
vendor/github.com/containers/image/v5/sif/transport.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
package sif
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/directory/explicitfilepath"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/image"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
transports.Register(Transport)
|
||||
}
|
||||
|
||||
// Transport is an ImageTransport for SIF images.
|
||||
var Transport = sifTransport{}
|
||||
|
||||
type sifTransport struct{}
|
||||
|
||||
func (t sifTransport) Name() string {
|
||||
return "sif"
|
||||
}
|
||||
|
||||
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
|
||||
func (t sifTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||
return NewReference(reference)
|
||||
}
|
||||
|
||||
// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
|
||||
// (i.e. a valid PolicyConfigurationIdentity() or PolicyConfigurationNamespaces() return value).
|
||||
// It is acceptable to allow an invalid value which will never be matched, it can "only" cause user confusion.
|
||||
// scope passed to this function will not be "", that value is always allowed.
|
||||
func (t sifTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||
if !strings.HasPrefix(scope, "/") {
|
||||
return fmt.Errorf("Invalid scope %s: Must be an absolute path", scope)
|
||||
}
|
||||
// Refuse also "/", otherwise "/" and "" would have the same semantics,
|
||||
// and "" could be unexpectedly shadowed by the "/" entry.
|
||||
if scope == "/" {
|
||||
return errors.New(`Invalid scope "/": Use the generic default scope ""`)
|
||||
}
|
||||
cleaned := filepath.Clean(scope)
|
||||
if cleaned != scope {
|
||||
return fmt.Errorf(`Invalid scope %s: Uses non-canonical format, perhaps try %s`, scope, cleaned)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sifReference is an ImageReference for SIF images.
|
||||
type sifReference struct {
|
||||
// Note that the interpretation of paths below depends on the underlying filesystem state, which may change under us at any time!
|
||||
// Either of the paths may point to a different, or no, inode over time. resolvedFile may contain symbolic links, and so on.
|
||||
|
||||
// Generally we follow the intent of the user, and use the "file" member for filesystem operations (e.g. the user can use a relative path to avoid
|
||||
// being exposed to symlinks and renames in the parent directories to the working directory).
|
||||
// (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.)
|
||||
file string // As specified by the user. May be relative, contain symlinks, etc.
|
||||
resolvedFile string // Absolute file path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces.
|
||||
}
|
||||
|
||||
// There is no sif.ParseReference because it is rather pointless.
|
||||
// Callers who need a transport-independent interface will go through
|
||||
// sifTransport.ParseReference; callers who intentionally deal with SIF files
|
||||
// can use sif.NewReference.
|
||||
|
||||
// NewReference returns an image file reference for a specified path.
|
||||
func NewReference(file string) (types.ImageReference, error) {
|
||||
// We do not expose an API supplying the resolvedFile; we could, but recomputing it
|
||||
// is generally cheap enough that we prefer being confident about the properties of resolvedFile.
|
||||
resolved, err := explicitfilepath.ResolvePathToFullyExplicit(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sifReference{file: file, resolvedFile: resolved}, nil
|
||||
}
|
||||
|
||||
func (ref sifReference) Transport() types.ImageTransport {
|
||||
return Transport
|
||||
}
|
||||
|
||||
// StringWithinTransport returns a string representation of the reference, which MUST be such that
|
||||
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
|
||||
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
|
||||
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
|
||||
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix;
|
||||
// instead, see transports.ImageName().
|
||||
func (ref sifReference) StringWithinTransport() string {
|
||||
return ref.file
|
||||
}
|
||||
|
||||
// DockerReference returns a Docker reference associated with this reference
|
||||
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
|
||||
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
|
||||
func (ref sifReference) DockerReference() reference.Named {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
|
||||
// This MUST reflect user intent, not e.g. after processing of third-party redirects or aliases;
|
||||
// The value SHOULD be fully explicit about its semantics, with no hidden defaults, AND canonical
|
||||
// (i.e. various references with exactly the same semantics should return the same configuration identity)
|
||||
// It is fine for the return value to be equal to StringWithinTransport(), and it is desirable but
|
||||
// not required/guaranteed that it will be a valid input to Transport().ParseReference().
|
||||
// Returns "" if configuration identities for these references are not supported.
|
||||
func (ref sifReference) PolicyConfigurationIdentity() string {
|
||||
return ref.resolvedFile
|
||||
}
|
||||
|
||||
// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
|
||||
// for if explicit configuration for PolicyConfigurationIdentity() is not set. The list will be processed
|
||||
// in order, terminating on first match, and an implicit "" is always checked at the end.
|
||||
// It is STRONGLY recommended for the first element, if any, to be a prefix of PolicyConfigurationIdentity(),
|
||||
// and each following element to be a prefix of the element preceding it.
|
||||
func (ref sifReference) PolicyConfigurationNamespaces() []string {
|
||||
res := []string{}
|
||||
path := ref.resolvedFile
|
||||
for {
|
||||
lastSlash := strings.LastIndex(path, "/")
|
||||
if lastSlash == -1 || lastSlash == 0 {
|
||||
break
|
||||
}
|
||||
path = path[:lastSlash]
|
||||
res = append(res, path)
|
||||
}
|
||||
// Note that we do not include "/"; it is redundant with the default "" global default,
|
||||
// and rejected by sifTransport.ValidatePolicyConfigurationScope above.
|
||||
return res
|
||||
}
|
||||
|
||||
// NewImage returns a types.ImageCloser for this reference, possibly specialized for this ImageTransport.
|
||||
// The caller must call .Close() on the returned ImageCloser.
|
||||
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
|
||||
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
|
||||
// WARNING: This may not do the right thing for a manifest list, see image.FromSource for details.
|
||||
func (ref sifReference) NewImage(ctx context.Context, sys *types.SystemContext) (types.ImageCloser, error) {
|
||||
src, err := newImageSource(ctx, sys, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return image.FromSource(ctx, sys, src)
|
||||
}
|
||||
|
||||
// NewImageSource returns a types.ImageSource for this reference.
|
||||
// The caller must call .Close() on the returned ImageSource.
|
||||
func (ref sifReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) {
|
||||
return newImageSource(ctx, sys, ref)
|
||||
}
|
||||
|
||||
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||
// The caller must call .Close() on the returned ImageDestination.
|
||||
func (ref sifReference) NewImageDestination(ctx context.Context, sys *types.SystemContext) (types.ImageDestination, error) {
|
||||
return nil, errors.New(`"sif:" locations can only be read from, not written to`)
|
||||
}
|
||||
|
||||
// DeleteImage deletes the named image from the registry, if supported.
|
||||
func (ref sifReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error {
|
||||
return errors.New("Deleting images not implemented for sif: images")
|
||||
}
|
30
vendor/github.com/containers/image/v5/signature/docker.go
generated
vendored
30
vendor/github.com/containers/image/v5/signature/docker.go
generated
vendored
@ -3,22 +3,46 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// SignOptions includes optional parameters for signing container images.
|
||||
type SignOptions struct {
|
||||
// Passphare to use when signing with the key identity.
|
||||
Passphrase string
|
||||
}
|
||||
|
||||
// SignDockerManifest returns a signature for manifest as the specified dockerReference,
|
||||
// using mech and keyIdentity.
|
||||
func SignDockerManifest(m []byte, dockerReference string, mech SigningMechanism, keyIdentity string) ([]byte, error) {
|
||||
// using mech and keyIdentity, and the specified options.
|
||||
func SignDockerManifestWithOptions(m []byte, dockerReference string, mech SigningMechanism, keyIdentity string, options *SignOptions) ([]byte, error) {
|
||||
manifestDigest, err := manifest.Digest(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig := newUntrustedSignature(manifestDigest, dockerReference)
|
||||
return sig.sign(mech, keyIdentity)
|
||||
|
||||
var passphrase string
|
||||
if options != nil {
|
||||
passphrase = options.Passphrase
|
||||
// The gpgme implementation can’t use passphrase with \n; reject it here for consistent behavior.
|
||||
if strings.Contains(passphrase, "\n") {
|
||||
return nil, errors.New("invalid passphrase: must not contain a line break")
|
||||
}
|
||||
}
|
||||
|
||||
return sig.sign(mech, keyIdentity, passphrase)
|
||||
}
|
||||
|
||||
// SignDockerManifest returns a signature for manifest as the specified dockerReference,
|
||||
// using mech and keyIdentity.
|
||||
func SignDockerManifest(m []byte, dockerReference string, mech SigningMechanism, keyIdentity string) ([]byte, error) {
|
||||
return SignDockerManifestWithOptions(m, dockerReference, mech, keyIdentity, nil)
|
||||
}
|
||||
|
||||
// VerifyDockerManifestSignature checks that unverifiedSignature uses expectedKeyIdentity to sign unverifiedManifest as expectedDockerReference,
|
||||
|
11
vendor/github.com/containers/image/v5/signature/mechanism.go
generated
vendored
11
vendor/github.com/containers/image/v5/signature/mechanism.go
generated
vendored
@ -18,8 +18,6 @@ import (
|
||||
|
||||
// SigningMechanism abstracts a way to sign binary blobs and verify their signatures.
|
||||
// Each mechanism should eventually be closed by calling Close().
|
||||
// FIXME: Eventually expand on keyIdentity (namespace them between mechanisms to
|
||||
// eliminate ambiguities, support CA signatures and perhaps other key properties)
|
||||
type SigningMechanism interface {
|
||||
// Close removes resources associated with the mechanism, if any.
|
||||
Close() error
|
||||
@ -38,6 +36,15 @@ type SigningMechanism interface {
|
||||
UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error)
|
||||
}
|
||||
|
||||
// signingMechanismWithPassphrase is an internal extension of SigningMechanism.
|
||||
type signingMechanismWithPassphrase interface {
|
||||
SigningMechanism
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity and passphrase.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
SignWithPassphrase(input []byte, keyIdentity string, passphrase string) ([]byte, error)
|
||||
}
|
||||
|
||||
// SigningNotSupportedError is returned when trying to sign using a mechanism which does not support that.
|
||||
type SigningNotSupportedError string
|
||||
|
||||
|
37
vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go
generated
vendored
37
vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go
generated
vendored
@ -5,11 +5,12 @@ package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/mtrmac/gpgme"
|
||||
"github.com/proglottis/gpgme"
|
||||
)
|
||||
|
||||
// A GPG/OpenPGP signing mechanism, implemented using gpgme.
|
||||
@ -20,7 +21,7 @@ type gpgmeSigningMechanism struct {
|
||||
|
||||
// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
|
||||
// The caller must call .Close() on the returned SigningMechanism.
|
||||
func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, error) {
|
||||
func newGPGSigningMechanismInDirectory(optionalDir string) (signingMechanismWithPassphrase, error) {
|
||||
ctx, err := newGPGMEContext(optionalDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -35,7 +36,7 @@ func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, er
|
||||
// recognizes _only_ public keys from the supplied blob, and returns the identities
|
||||
// of these keys.
|
||||
// The caller must call .Close() on the returned SigningMechanism.
|
||||
func newEphemeralGPGSigningMechanism(blob []byte) (SigningMechanism, []string, error) {
|
||||
func newEphemeralGPGSigningMechanism(blob []byte) (signingMechanismWithPassphrase, []string, error) {
|
||||
dir, err := ioutil.TempDir("", "containers-ephemeral-gpg-")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -117,9 +118,9 @@ func (m *gpgmeSigningMechanism) SupportsSigning() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity.
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity and passphrase.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
func (m *gpgmeSigningMechanism) SignWithPassphrase(input []byte, keyIdentity string, passphrase string) ([]byte, error) {
|
||||
key, err := m.ctx.GetKey(keyIdentity, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -133,12 +134,38 @@ func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if passphrase != "" {
|
||||
// Callback to write the passphrase to the specified file descriptor.
|
||||
callback := func(uidHint string, prevWasBad bool, gpgmeFD *os.File) error {
|
||||
if prevWasBad {
|
||||
return errors.New("bad passphrase")
|
||||
}
|
||||
_, err := gpgmeFD.WriteString(passphrase + "\n")
|
||||
return err
|
||||
}
|
||||
if err := m.ctx.SetCallback(callback); err != nil {
|
||||
return nil, fmt.Errorf("setting gpgme passphrase callback: %w", err)
|
||||
}
|
||||
|
||||
// Loopback mode will use the callback instead of prompting the user.
|
||||
if err := m.ctx.SetPinEntryMode(gpgme.PinEntryLoopback); err != nil {
|
||||
return nil, fmt.Errorf("setting gpgme pinentry mode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = m.ctx.Sign([]*gpgme.Key{key}, inputData, sigData, gpgme.SigModeNormal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sigBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
return m.SignWithPassphrase(input, keyIdentity, "")
|
||||
}
|
||||
|
||||
// Verify parses unverifiedSignature and returns the content and the signer's identity
|
||||
func (m *gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
|
||||
signedBuffer := bytes.Buffer{}
|
||||
|
12
vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go
generated
vendored
12
vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go
generated
vendored
@ -30,7 +30,7 @@ type openpgpSigningMechanism struct {
|
||||
|
||||
// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
|
||||
// The caller must call .Close() on the returned SigningMechanism.
|
||||
func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, error) {
|
||||
func newGPGSigningMechanismInDirectory(optionalDir string) (signingMechanismWithPassphrase, error) {
|
||||
m := &openpgpSigningMechanism{
|
||||
keyring: openpgp.EntityList{},
|
||||
}
|
||||
@ -61,7 +61,7 @@ func newGPGSigningMechanismInDirectory(optionalDir string) (SigningMechanism, er
|
||||
// recognizes _only_ public keys from the supplied blob, and returns the identities
|
||||
// of these keys.
|
||||
// The caller must call .Close() on the returned SigningMechanism.
|
||||
func newEphemeralGPGSigningMechanism(blob []byte) (SigningMechanism, []string, error) {
|
||||
func newEphemeralGPGSigningMechanism(blob []byte) (signingMechanismWithPassphrase, []string, error) {
|
||||
m := &openpgpSigningMechanism{
|
||||
keyring: openpgp.EntityList{},
|
||||
}
|
||||
@ -110,10 +110,16 @@ func (m *openpgpSigningMechanism) SupportsSigning() error {
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *openpgpSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
func (m *openpgpSigningMechanism) SignWithPassphrase(input []byte, keyIdentity string, passphrase string) ([]byte, error) {
|
||||
return nil, SigningNotSupportedError("signing is not supported in github.com/containers/image built with the containers_image_openpgp build tag")
|
||||
}
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *openpgpSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
return m.SignWithPassphrase(input, keyIdentity, "")
|
||||
}
|
||||
|
||||
// Verify parses unverifiedSignature and returns the content and the signer's identity
|
||||
func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
|
||||
md, err := openpgp.ReadMessage(bytes.NewReader(unverifiedSignature), m.keyring, nil, nil)
|
||||
|
10
vendor/github.com/containers/image/v5/signature/signature.go
generated
vendored
10
vendor/github.com/containers/image/v5/signature/signature.go
generated
vendored
@ -190,12 +190,20 @@ func (s *untrustedSignature) strictUnmarshalJSON(data []byte) error {
|
||||
// of the system just because it is a private key — actually the presence of a private key
|
||||
// on the system increases the likelihood of an a successful attack on that private key
|
||||
// on that particular system.)
|
||||
func (s untrustedSignature) sign(mech SigningMechanism, keyIdentity string) ([]byte, error) {
|
||||
func (s untrustedSignature) sign(mech SigningMechanism, keyIdentity string, passphrase string) ([]byte, error) {
|
||||
json, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if newMech, ok := mech.(signingMechanismWithPassphrase); ok {
|
||||
return newMech.SignWithPassphrase(json, keyIdentity, passphrase)
|
||||
}
|
||||
|
||||
if passphrase != "" {
|
||||
return nil, errors.New("signing mechanism does not support passphrases")
|
||||
}
|
||||
|
||||
return mech.Sign(json, keyIdentity)
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/containers/image/v5/transports/alltransports/alltransports.go
generated
vendored
2
vendor/github.com/containers/image/v5/transports/alltransports/alltransports.go
generated
vendored
@ -12,7 +12,9 @@ import (
|
||||
_ "github.com/containers/image/v5/oci/archive"
|
||||
_ "github.com/containers/image/v5/oci/layout"
|
||||
_ "github.com/containers/image/v5/openshift"
|
||||
_ "github.com/containers/image/v5/sif"
|
||||
_ "github.com/containers/image/v5/tarball"
|
||||
|
||||
// The ostree transport is registered by ostree*.go
|
||||
// The storage transport is registered by storage*.go
|
||||
"github.com/containers/image/v5/transports"
|
||||
|
2
vendor/github.com/containers/image/v5/version/version.go
generated
vendored
2
vendor/github.com/containers/image/v5/version/version.go
generated
vendored
@ -6,7 +6,7 @@ const (
|
||||
// VersionMajor is for an API incompatible changes
|
||||
VersionMajor = 5
|
||||
// VersionMinor is for functionality in a backwards-compatible manner
|
||||
VersionMinor = 18
|
||||
VersionMinor = 19
|
||||
// VersionPatch is for backwards-compatible bug fixes
|
||||
VersionPatch = 0
|
||||
|
||||
|
Reference in New Issue
Block a user