Bump github.com/containers/image@efae299
Update the vendored copy of github.com/containers/image to revision efae29995d4846ffa6163eb4d466fd61bda43aae. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
7c551964c0
commit
3d15bc571b
26 changed files with 2095 additions and 571 deletions
284
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
Normal file
284
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
Normal file
|
@ -0,0 +1,284 @@
|
|||
package ostree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type blobToImport struct {
|
||||
Size int64
|
||||
Digest digest.Digest
|
||||
BlobPath string
|
||||
}
|
||||
|
||||
type descriptor struct {
|
||||
Size int64 `json:"size"`
|
||||
Digest digest.Digest `json:"digest"`
|
||||
}
|
||||
|
||||
type manifestSchema struct {
|
||||
ConfigDescriptor descriptor `json:"config"`
|
||||
LayersDescriptors []descriptor `json:"layers"`
|
||||
}
|
||||
|
||||
type ostreeImageDestination struct {
|
||||
ref ostreeReference
|
||||
manifest string
|
||||
schema manifestSchema
|
||||
tmpDirPath string
|
||||
blobs map[string]*blobToImport
|
||||
}
|
||||
|
||||
// newImageDestination returns an ImageDestination for writing to an existing ostree.
|
||||
func newImageDestination(ref ostreeReference, tmpDirPath string) (types.ImageDestination, error) {
|
||||
tmpDirPath = filepath.Join(tmpDirPath, ref.branchName)
|
||||
if err := ensureDirectoryExists(tmpDirPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ostreeImageDestination{ref, "", manifestSchema{}, tmpDirPath, map[string]*blobToImport{}}, nil
|
||||
}
|
||||
|
||||
// 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 *ostreeImageDestination) Reference() types.ImageReference {
|
||||
return d.ref
|
||||
}
|
||||
|
||||
// Close removes resources associated with an initialized ImageDestination, if any.
|
||||
func (d *ostreeImageDestination) Close() error {
|
||||
return os.RemoveAll(d.tmpDirPath)
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) SupportedManifestMIMETypes() []string {
|
||||
return []string{
|
||||
manifest.DockerV2Schema2MediaType,
|
||||
}
|
||||
}
|
||||
|
||||
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
|
||||
func (d *ostreeImageDestination) SupportsSignatures() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination.
|
||||
func (d *ostreeImageDestination) ShouldCompressLayers() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
|
||||
// uploaded to the image destination, true otherwise.
|
||||
func (d *ostreeImageDestination) AcceptsForeignLayerURLs() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
|
||||
tmpDir, err := ioutil.TempDir(d.tmpDirPath, "blob")
|
||||
if err != nil {
|
||||
return types.BlobInfo{}, err
|
||||
}
|
||||
|
||||
blobPath := filepath.Join(tmpDir, "content")
|
||||
blobFile, err := os.Create(blobPath)
|
||||
if err != nil {
|
||||
return types.BlobInfo{}, err
|
||||
}
|
||||
defer blobFile.Close()
|
||||
|
||||
digester := digest.Canonical.Digester()
|
||||
tee := io.TeeReader(stream, digester.Hash())
|
||||
|
||||
size, err := io.Copy(blobFile, tee)
|
||||
if err != nil {
|
||||
return types.BlobInfo{}, err
|
||||
}
|
||||
computedDigest := digester.Digest()
|
||||
if inputInfo.Size != -1 && size != inputInfo.Size {
|
||||
return types.BlobInfo{}, errors.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, inputInfo.Size, size)
|
||||
}
|
||||
if err := blobFile.Sync(); err != nil {
|
||||
return types.BlobInfo{}, err
|
||||
}
|
||||
|
||||
hash := computedDigest.Hex()
|
||||
d.blobs[hash] = &blobToImport{Size: size, Digest: computedDigest, BlobPath: blobPath}
|
||||
return types.BlobInfo{Digest: computedDigest, Size: size}, nil
|
||||
}
|
||||
|
||||
func fixUsermodeFiles(dir string) error {
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, info := range entries {
|
||||
fullpath := filepath.Join(dir, info.Name())
|
||||
if info.IsDir() {
|
||||
if err := os.Chmod(dir, info.Mode()|0700); err != nil {
|
||||
return err
|
||||
}
|
||||
err = fixUsermodeFiles(fullpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if info.Mode().IsRegular() {
|
||||
if err := os.Chmod(fullpath, info.Mode()|0600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) importBlob(blob *blobToImport) error {
|
||||
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
||||
destinationPath := filepath.Join(d.tmpDirPath, blob.Digest.Hex(), "root")
|
||||
if err := ensureDirectoryExists(destinationPath); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
os.Remove(blob.BlobPath)
|
||||
os.RemoveAll(destinationPath)
|
||||
}()
|
||||
|
||||
if os.Getuid() == 0 {
|
||||
if err := archive.UntarPath(blob.BlobPath, destinationPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
os.MkdirAll(destinationPath, 0755)
|
||||
if err := exec.Command("tar", "-C", destinationPath, "--no-same-owner", "--no-same-permissions", "--delay-directory-restore", "-xf", blob.BlobPath).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fixUsermodeFiles(destinationPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return exec.Command("ostree", "commit",
|
||||
"--repo", d.ref.repo,
|
||||
fmt.Sprintf("--add-metadata-string=docker.size=%d", blob.Size),
|
||||
"--branch", ostreeBranch,
|
||||
fmt.Sprintf("--tree=dir=%s", destinationPath)).Run()
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) importConfig(blob *blobToImport) error {
|
||||
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
||||
|
||||
return exec.Command("ostree", "commit",
|
||||
"--repo", d.ref.repo,
|
||||
fmt.Sprintf("--add-metadata-string=docker.size=%d", blob.Size),
|
||||
"--branch", ostreeBranch, filepath.Dir(blob.BlobPath)).Run()
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) {
|
||||
branch := fmt.Sprintf("ociimage/%s", info.Digest.Hex())
|
||||
output, err := exec.Command("ostree", "show", "--repo", d.ref.repo, "--print-metadata-key=docker.size", branch).CombinedOutput()
|
||||
if err != nil {
|
||||
if bytes.Index(output, []byte("not found")) >= 0 || bytes.Index(output, []byte("No such")) >= 0 {
|
||||
return false, -1, nil
|
||||
}
|
||||
return false, -1, err
|
||||
}
|
||||
size, err := strconv.ParseInt(strings.Trim(string(output), "'\n"), 10, 64)
|
||||
if err != nil {
|
||||
return false, -1, err
|
||||
}
|
||||
|
||||
return true, size, nil
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) PutManifest(manifest []byte) error {
|
||||
d.manifest = string(manifest)
|
||||
|
||||
if err := json.Unmarshal(manifest, &d.schema); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(d.tmpDirPath, d.ref.manifestPath())
|
||||
if err := ensureParentDirectoryExists(manifestPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(manifestPath, manifest, 0644)
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) PutSignatures(signatures [][]byte) error {
|
||||
path := filepath.Join(d.tmpDirPath, d.ref.signaturePath(0))
|
||||
if err := ensureParentDirectoryExists(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, sig := range signatures {
|
||||
signaturePath := filepath.Join(d.tmpDirPath, d.ref.signaturePath(i))
|
||||
if err := ioutil.WriteFile(signaturePath, sig, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ostreeImageDestination) Commit() error {
|
||||
for _, layer := range d.schema.LayersDescriptors {
|
||||
hash := layer.Digest.Hex()
|
||||
blob := d.blobs[hash]
|
||||
// if the blob is not present in d.blobs then it is already stored in OSTree,
|
||||
// and we don't need to import it.
|
||||
if blob == nil {
|
||||
continue
|
||||
}
|
||||
err := d.importBlob(blob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hash := d.schema.ConfigDescriptor.Digest.Hex()
|
||||
blob := d.blobs[hash]
|
||||
if blob != nil {
|
||||
err := d.importConfig(blob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(d.tmpDirPath, "manifest")
|
||||
err := exec.Command("ostree", "commit",
|
||||
"--repo", d.ref.repo,
|
||||
fmt.Sprintf("--add-metadata-string=docker.manifest=%s", string(d.manifest)),
|
||||
fmt.Sprintf("--branch=ociimage/%s", d.ref.branchName),
|
||||
manifestPath).Run()
|
||||
return err
|
||||
}
|
||||
|
||||
func ensureDirectoryExists(path string) error {
|
||||
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureParentDirectoryExists(path string) error {
|
||||
return ensureDirectoryExists(filepath.Dir(path))
|
||||
}
|
235
vendor/github.com/containers/image/ostree/ostree_transport.go
generated
vendored
Normal file
235
vendor/github.com/containers/image/ostree/ostree_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,235 @@
|
|||
package ostree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/containers/image/directory/explicitfilepath"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/transports"
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
const defaultOSTreeRepo = "/ostree/repo"
|
||||
|
||||
// Transport is an ImageTransport for ostree paths.
|
||||
var Transport = ostreeTransport{}
|
||||
|
||||
type ostreeTransport struct{}
|
||||
|
||||
func (t ostreeTransport) Name() string {
|
||||
return "ostree"
|
||||
}
|
||||
|
||||
func init() {
|
||||
transports.Register(Transport)
|
||||
}
|
||||
|
||||
// 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 ostreeTransport) ValidatePolicyConfigurationScope(scope string) error {
|
||||
sep := strings.Index(scope, ":")
|
||||
if sep < 0 {
|
||||
return errors.Errorf("Invalid ostree: scope %s: Must include a repo", scope)
|
||||
}
|
||||
repo := scope[:sep]
|
||||
|
||||
if !strings.HasPrefix(repo, "/") {
|
||||
return errors.Errorf("Invalid ostree: scope %s: repository must be an absolute path", scope)
|
||||
}
|
||||
cleaned := filepath.Clean(repo)
|
||||
if cleaned != repo {
|
||||
return errors.Errorf(`Invalid ostree: scope %s: Uses non-canonical path format, perhaps try with path %s`, scope, cleaned)
|
||||
}
|
||||
|
||||
// FIXME? In the namespaces within a repo,
|
||||
// we could be verifying the various character set and length restrictions
|
||||
// from docker/distribution/reference.regexp.go, but other than that there
|
||||
// are few semantically invalid strings.
|
||||
return nil
|
||||
}
|
||||
|
||||
// ostreeReference is an ImageReference for ostree paths.
|
||||
type ostreeReference struct {
|
||||
image string
|
||||
branchName string
|
||||
repo string
|
||||
}
|
||||
|
||||
func (t ostreeTransport) ParseReference(ref string) (types.ImageReference, error) {
|
||||
var repo = ""
|
||||
var image = ""
|
||||
s := strings.SplitN(ref, "@/", 2)
|
||||
if len(s) == 1 {
|
||||
image, repo = s[0], defaultOSTreeRepo
|
||||
} else {
|
||||
image, repo = s[0], "/"+s[1]
|
||||
}
|
||||
|
||||
return NewReference(image, repo)
|
||||
}
|
||||
|
||||
// NewReference returns an OSTree reference for a specified repo and image.
|
||||
func NewReference(image string, repo string) (types.ImageReference, error) {
|
||||
// image is not _really_ in a containers/image/docker/reference format;
|
||||
// as far as the libOSTree ociimage/* namespace is concerned, it is more or
|
||||
// less an arbitrary string with an implied tag.
|
||||
// We use the reference.* parsers basically for the default tag name in
|
||||
// reference.TagNameOnly, and incidentally for some character set and length
|
||||
// restrictions.
|
||||
var ostreeImage reference.Named
|
||||
s := strings.SplitN(image, ":", 2)
|
||||
|
||||
named, err := reference.WithName(s[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(s) == 1 {
|
||||
ostreeImage = reference.TagNameOnly(named)
|
||||
} else {
|
||||
ostreeImage, err = reference.WithTag(named, s[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resolved, err := explicitfilepath.ResolvePathToFullyExplicit(repo)
|
||||
if err != nil {
|
||||
// With os.IsNotExist(err), the parent directory of repo is also not existent;
|
||||
// that should ordinarily not happen, but it would be a bit weird to reject
|
||||
// references which do not specify a repo just because the implicit defaultOSTreeRepo
|
||||
// does not exist.
|
||||
if os.IsNotExist(err) && repo == defaultOSTreeRepo {
|
||||
resolved = repo
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// This is necessary to prevent directory paths returned by PolicyConfigurationNamespaces
|
||||
// from being ambiguous with values of PolicyConfigurationIdentity.
|
||||
if strings.Contains(resolved, ":") {
|
||||
return nil, errors.Errorf("Invalid OSTreeCI reference %s@%s: path %s contains a colon", image, repo, resolved)
|
||||
}
|
||||
|
||||
return ostreeReference{
|
||||
image: ostreeImage.String(),
|
||||
branchName: encodeOStreeRef(ostreeImage.String()),
|
||||
repo: resolved,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ref ostreeReference) 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.
|
||||
func (ref ostreeReference) StringWithinTransport() string {
|
||||
return fmt.Sprintf("%s@%s", ref.image, ref.repo)
|
||||
}
|
||||
|
||||
// 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 ostreeReference) DockerReference() reference.Named {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ref ostreeReference) PolicyConfigurationIdentity() string {
|
||||
return fmt.Sprintf("%s:%s", ref.repo, ref.image)
|
||||
}
|
||||
|
||||
// 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 ostreeReference) PolicyConfigurationNamespaces() []string {
|
||||
s := strings.SplitN(ref.image, ":", 2)
|
||||
if len(s) != 2 { // Coverage: Should never happen, NewReference above ensures ref.image has a :tag.
|
||||
panic(fmt.Sprintf("Internal inconsistency: ref.image value %q does not have a :tag", ref.image))
|
||||
}
|
||||
name := s[0]
|
||||
res := []string{}
|
||||
for {
|
||||
res = append(res, fmt.Sprintf("%s:%s", ref.repo, name))
|
||||
|
||||
lastSlash := strings.LastIndex(name, "/")
|
||||
if lastSlash == -1 {
|
||||
break
|
||||
}
|
||||
name = name[:lastSlash]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
|
||||
// The caller must call .Close() on the returned Image.
|
||||
// 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.
|
||||
func (ref ostreeReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||
return nil, errors.New("Reading ostree: images is currently not supported")
|
||||
}
|
||||
|
||||
// NewImageSource returns a types.ImageSource for this reference,
|
||||
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
|
||||
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||
// The caller must call .Close() on the returned ImageSource.
|
||||
func (ref ostreeReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||
return nil, errors.New("Reading ostree: images is currently not supported")
|
||||
}
|
||||
|
||||
// NewImageDestination returns a types.ImageDestination for this reference.
|
||||
// The caller must call .Close() on the returned ImageDestination.
|
||||
func (ref ostreeReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||
var tmpDir string
|
||||
if ctx == nil || ctx.OSTreeTmpDirPath == "" {
|
||||
tmpDir = os.TempDir()
|
||||
} else {
|
||||
tmpDir = ctx.OSTreeTmpDirPath
|
||||
}
|
||||
return newImageDestination(ref, tmpDir)
|
||||
}
|
||||
|
||||
// DeleteImage deletes the named image from the registry, if supported.
|
||||
func (ref ostreeReference) DeleteImage(ctx *types.SystemContext) error {
|
||||
return errors.Errorf("Deleting images not implemented for ostree: images")
|
||||
}
|
||||
|
||||
var ostreeRefRegexp = regexp.MustCompile(`^[A-Za-z0-9.-]$`)
|
||||
|
||||
func encodeOStreeRef(in string) string {
|
||||
var buffer bytes.Buffer
|
||||
for i := range in {
|
||||
sub := in[i : i+1]
|
||||
if ostreeRefRegexp.MatchString(sub) {
|
||||
buffer.WriteString(sub)
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("_%02X", sub[0]))
|
||||
}
|
||||
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// manifestPath returns a path for the manifest within a ostree using our conventions.
|
||||
func (ref ostreeReference) manifestPath() string {
|
||||
return filepath.Join("manifest", "manifest.json")
|
||||
}
|
||||
|
||||
// signaturePath returns a path for a signature within a ostree using our conventions.
|
||||
func (ref ostreeReference) signaturePath(index int) string {
|
||||
return filepath.Join("manifest", fmt.Sprintf("signature-%d", index+1))
|
||||
}
|
316
vendor/github.com/containers/image/ostree/ostree_transport_test.go
generated
vendored
Normal file
316
vendor/github.com/containers/image/ostree/ostree_transport_test.go
generated
vendored
Normal file
|
@ -0,0 +1,316 @@
|
|||
package ostree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
sha256digestHex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
sha256digest = "@sha256:" + sha256digestHex
|
||||
)
|
||||
|
||||
func TestTransportName(t *testing.T) {
|
||||
assert.Equal(t, "ostree", Transport.Name())
|
||||
}
|
||||
|
||||
// A helper to replace $TMP in a repo path with a real temporary directory
|
||||
func withTmpDir(repo string, tmpDir string) string {
|
||||
return strings.Replace(repo, "$TMP", tmpDir, -1)
|
||||
}
|
||||
|
||||
// A common list of repo suffixes to test for the various ImageReference methods.
|
||||
var repoSuffixes = []struct{ repoSuffix, resolvedRepo string }{
|
||||
{"", "/ostree/repo"},
|
||||
{"@/ostree/repo", "/ostree/repo"}, // /ostree/repo is accepted even if neither /ostree/repo nor /ostree exists, as a special case.
|
||||
{"@$TMP/at@sign@repo", "$TMP/at@sign@repo"},
|
||||
// Rejected as ambiguous: /repo:with:colons could either be an (/repo, with:colons) policy configuration identity, or a (/repo:with, colons) policy configuration namespace.
|
||||
{"@$TMP/repo:with:colons", ""},
|
||||
}
|
||||
|
||||
// A common list of cases for image name parsing and normalization
|
||||
var imageNameTestcases = []struct{ input, normalized, branchName string }{
|
||||
{"busybox:notlatest", "busybox:notlatest", "busybox_3Anotlatest"}, // Explicit tag
|
||||
{"busybox", "busybox:latest", "busybox_3Alatest"}, // Default tag
|
||||
{"docker.io/library/busybox:latest", "docker.io/library/busybox:latest", "docker.io_2Flibrary_2Fbusybox_3Alatest"}, // A hierarchical name
|
||||
{"UPPERCASEISINVALID", "", ""}, // Invalid input
|
||||
{"busybox" + sha256digest, "", ""}, // Digested references are not supported (parsed as invalid repository name)
|
||||
{"busybox:invalid+tag", "", ""}, // Invalid tag value
|
||||
{"busybox:tag:with:colons", "", ""}, // Multiple colons - treated as a tag which contains a colon, which is invalid
|
||||
{"", "", ""}, // Empty input is rejected (invalid repository.Named)
|
||||
}
|
||||
|
||||
func TestTransportParseReference(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreeParseReference")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, c := range imageNameTestcases {
|
||||
for _, suffix := range repoSuffixes {
|
||||
fullInput := c.input + withTmpDir(suffix.repoSuffix, tmpDir)
|
||||
ref, err := Transport.ParseReference(fullInput)
|
||||
if c.normalized == "" || suffix.resolvedRepo == "" {
|
||||
assert.Error(t, err, fullInput)
|
||||
} else {
|
||||
require.NoError(t, err, fullInput)
|
||||
ostreeRef, ok := ref.(ostreeReference)
|
||||
require.True(t, ok, fullInput)
|
||||
assert.Equal(t, c.normalized, ostreeRef.image, fullInput)
|
||||
assert.Equal(t, c.branchName, ostreeRef.branchName, fullInput)
|
||||
assert.Equal(t, withTmpDir(suffix.resolvedRepo, tmpDir), ostreeRef.repo, fullInput)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransportValidatePolicyConfigurationScope(t *testing.T) {
|
||||
for _, scope := range []string{
|
||||
"/etc:docker.io/library/busybox:notlatest", // This also demonstrates that two colons are interpreted as repo:name:tag.
|
||||
"/etc:docker.io/library/busybox",
|
||||
"/etc:docker.io/library",
|
||||
"/etc:docker.io",
|
||||
"/etc:repo",
|
||||
"/this/does/not/exist:notlatest",
|
||||
} {
|
||||
err := Transport.ValidatePolicyConfigurationScope(scope)
|
||||
assert.NoError(t, err, scope)
|
||||
}
|
||||
|
||||
for _, scope := range []string{
|
||||
"/colon missing as a path-reference delimiter",
|
||||
"relative/path:busybox",
|
||||
"/double//slashes:busybox",
|
||||
"/has/./dot:busybox",
|
||||
"/has/dot/../dot:busybox",
|
||||
"/trailing/slash/:busybox",
|
||||
} {
|
||||
err := Transport.ValidatePolicyConfigurationScope(scope)
|
||||
assert.Error(t, err, scope)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewReference(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreeNewReference")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, c := range imageNameTestcases {
|
||||
for _, suffix := range repoSuffixes {
|
||||
if suffix.repoSuffix == "" {
|
||||
continue
|
||||
}
|
||||
caseName := c.input + suffix.repoSuffix
|
||||
ref, err := NewReference(c.input, withTmpDir(strings.TrimPrefix(suffix.repoSuffix, "@"), tmpDir))
|
||||
if c.normalized == "" || suffix.resolvedRepo == "" {
|
||||
assert.Error(t, err, caseName)
|
||||
} else {
|
||||
require.NoError(t, err, caseName)
|
||||
ostreeRef, ok := ref.(ostreeReference)
|
||||
require.True(t, ok, caseName)
|
||||
assert.Equal(t, c.normalized, ostreeRef.image, caseName)
|
||||
assert.Equal(t, c.branchName, ostreeRef.branchName, caseName)
|
||||
assert.Equal(t, withTmpDir(suffix.resolvedRepo, tmpDir), ostreeRef.repo, caseName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range []string{
|
||||
"/",
|
||||
"/etc",
|
||||
tmpDir,
|
||||
"relativepath",
|
||||
tmpDir + "/thisdoesnotexist",
|
||||
} {
|
||||
_, err := NewReference("busybox", path)
|
||||
require.NoError(t, err, path)
|
||||
}
|
||||
|
||||
_, err = NewReference("busybox", tmpDir+"/thisparentdoesnotexist/something")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// A common list of reference formats to test for the various ImageReference methods.
|
||||
var validReferenceTestCases = []struct{ input, stringWithinTransport, policyConfigurationIdentity string }{
|
||||
{"busybox", "busybox:latest@/ostree/repo", "/ostree/repo:busybox:latest"}, // Everything implied
|
||||
{"busybox:latest@/ostree/repo", "busybox:latest@/ostree/repo", "/ostree/repo:busybox:latest"}, // All implied values explicitly specified
|
||||
{"example.com/ns/foo:bar@$TMP/non-DEFAULT", "example.com/ns/foo:bar@$TMP/non-DEFAULT", "$TMP/non-DEFAULT:example.com/ns/foo:bar"}, // All values explicitly specified, a hierarchical name
|
||||
// A non-canonical path. Testing just one, the various other cases are tested in explicitfilepath.ResolvePathToFullyExplicit.
|
||||
{"busybox@$TMP/.", "busybox:latest@$TMP", "$TMP:busybox:latest"},
|
||||
// "/" as a corner case
|
||||
{"busybox@/", "busybox:latest@/", "/:busybox:latest"},
|
||||
}
|
||||
|
||||
func TestReferenceTransport(t *testing.T) {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Transport, ref.Transport())
|
||||
}
|
||||
|
||||
func TestReferenceStringWithinTransport(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreeStringWithinTransport")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := Transport.ParseReference(withTmpDir(c.input, tmpDir))
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef := ref.StringWithinTransport()
|
||||
assert.Equal(t, withTmpDir(c.stringWithinTransport, tmpDir), stringRef, c.input)
|
||||
// Do one more round to verify that the output can be parsed, to an equal value.
|
||||
ref2, err := Transport.ParseReference(stringRef)
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef2 := ref2.StringWithinTransport()
|
||||
assert.Equal(t, stringRef, stringRef2, c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferenceDockerReference(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreeDockerReference")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := Transport.ParseReference(withTmpDir(c.input, tmpDir))
|
||||
require.NoError(t, err, c.input)
|
||||
dockerRef := ref.DockerReference()
|
||||
assert.Nil(t, dockerRef, c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationIdentity(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreePolicyConfigurationIdentity")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := Transport.ParseReference(withTmpDir(c.input, tmpDir))
|
||||
require.NoError(t, err, c.input)
|
||||
assert.Equal(t, withTmpDir(c.policyConfigurationIdentity, tmpDir), ref.PolicyConfigurationIdentity(), c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationNamespaces(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreePolicyConfigurationNamespaces")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Test both that DockerReferenceIdentity returns the expected value (fullName+suffix),
|
||||
// and that DockerReferenceNamespaces starts with the expected value (fullName), i.e. that the two functions are
|
||||
// consistent.
|
||||
for inputName, expectedNS := range map[string][]string{
|
||||
"example.com/ns/repo": {"example.com/ns/repo", "example.com/ns", "example.com"},
|
||||
"example.com/repo": {"example.com/repo", "example.com"},
|
||||
"localhost/ns/repo": {"localhost/ns/repo", "localhost/ns", "localhost"},
|
||||
"localhost/repo": {"localhost/repo", "localhost"},
|
||||
"ns/repo": {"ns/repo", "ns"},
|
||||
"repo": {"repo"},
|
||||
} {
|
||||
// Test with a known path which should exist. Test just one non-canonical
|
||||
// path, the various other cases are tested in explicitfilepath.ResolvePathToFullyExplicit.
|
||||
for _, repoInput := range []string{tmpDir, tmpDir + "/./."} {
|
||||
fullName := inputName + ":notlatest"
|
||||
ref, err := NewReference(fullName, repoInput)
|
||||
require.NoError(t, err, fullName)
|
||||
|
||||
identity := ref.PolicyConfigurationIdentity()
|
||||
assert.Equal(t, tmpDir+":"+expectedNS[0]+":notlatest", identity, fullName)
|
||||
|
||||
ns := ref.PolicyConfigurationNamespaces()
|
||||
require.NotNil(t, ns, fullName)
|
||||
require.Len(t, ns, len(expectedNS), fullName)
|
||||
moreSpecific := identity
|
||||
for i := range expectedNS {
|
||||
assert.Equal(t, tmpDir+":"+expectedNS[i], ns[i], fmt.Sprintf("%s item %d", fullName, i))
|
||||
assert.True(t, strings.HasPrefix(moreSpecific, ns[i]))
|
||||
moreSpecific = ns[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferenceNewImage(t *testing.T) {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
_, err = ref.NewImage(nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReferenceNewImageSource(t *testing.T) {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
_, err = ref.NewImageSource(nil, nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReferenceNewImageDestination(t *testing.T) {
|
||||
otherTmpDir, err := ioutil.TempDir("", "ostree-transport-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(otherTmpDir)
|
||||
|
||||
for _, c := range []struct {
|
||||
ctx *types.SystemContext
|
||||
tmpDir string
|
||||
}{
|
||||
{nil, os.TempDir()},
|
||||
{&types.SystemContext{}, os.TempDir()},
|
||||
{&types.SystemContext{OSTreeTmpDirPath: otherTmpDir}, otherTmpDir},
|
||||
} {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
dest, err := ref.NewImageDestination(c.ctx)
|
||||
require.NoError(t, err)
|
||||
ostreeDest, ok := dest.(*ostreeImageDestination)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, c.tmpDir+"/busybox_3Alatest", ostreeDest.tmpDirPath)
|
||||
defer dest.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferenceDeleteImage(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "ostreeDeleteImage")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ref, err := Transport.ParseReference(withTmpDir("busybox@$TMP/this-repo-does-not-exist", tmpDir))
|
||||
require.NoError(t, err)
|
||||
err = ref.DeleteImage(nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestEncodeOSTreeRef(t *testing.T) {
|
||||
// Just a smoke test
|
||||
assert.Equal(t, "busybox_3Alatest", encodeOStreeRef("busybox:latest"))
|
||||
}
|
||||
|
||||
func TestReferenceManifestPath(t *testing.T) {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
ostreeRef, ok := ref.(ostreeReference)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, fmt.Sprintf("manifest%cmanifest.json", filepath.Separator), ostreeRef.manifestPath())
|
||||
}
|
||||
|
||||
func TestReferenceSignaturePath(t *testing.T) {
|
||||
ref, err := Transport.ParseReference("busybox")
|
||||
require.NoError(t, err)
|
||||
ostreeRef, ok := ref.(ostreeReference)
|
||||
require.True(t, ok)
|
||||
for _, c := range []struct {
|
||||
input int
|
||||
suffix string
|
||||
}{
|
||||
{0, "-1"},
|
||||
{42, "-43"},
|
||||
} {
|
||||
assert.Equal(t, fmt.Sprintf("manifest%csignature%s", filepath.Separator, c.suffix), ostreeRef.signaturePath(c.input), string(c.input))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue