mirror of
				https://github.com/containers/podman.git
				synced 2025-10-30 17:38:27 +08:00 
			
		
		
		
	 49264c7148
			
		
	
	49264c7148
	
	
	
		
			
			Changes as of 2022-04-21: - apply-podman-deltas: minor cleanup - buildah-tests.diff: deal with: . buildah #3894 (the registry one), which affected helpers.bash in a way that resulted in conflicts here; and . buildah #3917 (etchosts), which caused offset-only diffs with no conflicts - Reevaluate the bud skip list, and reenable some tests that seems to be passing now under podman: . bud with specified context ... . two tests that require a local registry (which buildah now runs) . bud with --cgroup-parent Signed-off-by: Ed Santiago <santiago@redhat.com> Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
		
			
				
	
	
		
			468 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			468 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package util
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"syscall"
 | |
| 
 | |
| 	"github.com/containers/buildah/define"
 | |
| 	"github.com/containers/common/libimage"
 | |
| 	"github.com/containers/common/pkg/config"
 | |
| 	"github.com/containers/image/v5/docker/reference"
 | |
| 	"github.com/containers/image/v5/pkg/shortnames"
 | |
| 	"github.com/containers/image/v5/signature"
 | |
| 	"github.com/containers/image/v5/transports/alltransports"
 | |
| 	"github.com/containers/image/v5/types"
 | |
| 	"github.com/containers/storage"
 | |
| 	"github.com/docker/distribution/registry/api/errcode"
 | |
| 	"github.com/opencontainers/go-digest"
 | |
| 	specs "github.com/opencontainers/runtime-spec/specs-go"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	minimumTruncatedIDLength = 3
 | |
| 	// DefaultTransport is a prefix that we apply to an image name if we
 | |
| 	// can't find one in the local Store, in order to generate a source
 | |
| 	// reference for the image that we can then copy to the local Store.
 | |
| 	DefaultTransport = "docker://"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// RegistryDefaultPathPrefix contains a per-registry listing of default prefixes
 | |
| 	// to prepend to image names that only contain a single path component.
 | |
| 	RegistryDefaultPathPrefix = map[string]string{
 | |
| 		"index.docker.io": "library",
 | |
| 		"docker.io":       "library",
 | |
| 	}
 | |
| )
 | |
| 
 | |
| // resolveName checks if name is a valid image name, and if that name doesn't
 | |
| // include a domain portion, returns a list of the names which it might
 | |
| // correspond to in the set of configured registries, and the transport used to
 | |
| // pull the image.
 | |
| //
 | |
| // The returned image names never include a transport: prefix, and if transport != "",
 | |
| // (transport, image) should be a valid input to alltransports.ParseImageName.
 | |
| // transport == "" indicates that image that already exists in a local storage,
 | |
| // and the name is valid for store.Image() / storage.Transport.ParseStoreReference().
 | |
| //
 | |
| // NOTE: The "list of search registries is empty" check does not count blocked registries,
 | |
| // and neither the implied "localhost" nor a possible firstRegistry are counted
 | |
| func resolveName(name string, sc *types.SystemContext, store storage.Store) ([]string, string, error) {
 | |
| 	if name == "" {
 | |
| 		return nil, "", nil
 | |
| 	}
 | |
| 
 | |
| 	// Maybe it's a truncated image ID.  Don't prepend a registry name, then.
 | |
| 	if len(name) >= minimumTruncatedIDLength {
 | |
| 		if img, err := store.Image(name); err == nil && img != nil && strings.HasPrefix(img.ID, name) {
 | |
| 			// It's a truncated version of the ID of an image that's present in local storage;
 | |
| 			// we need only expand the ID.
 | |
| 			return []string{img.ID}, "", nil
 | |
| 		}
 | |
| 	}
 | |
| 	// If we're referring to an image by digest, it *must* be local and we
 | |
| 	// should not have any fall through/back logic.
 | |
| 	if strings.HasPrefix(name, "sha256:") {
 | |
| 		d, err := digest.Parse(name)
 | |
| 		if err != nil {
 | |
| 			return nil, "", err
 | |
| 		}
 | |
| 		img, err := store.Image(d.Encoded())
 | |
| 		if err != nil {
 | |
| 			return nil, "", err
 | |
| 		}
 | |
| 		return []string{img.ID}, "", nil
 | |
| 	}
 | |
| 
 | |
| 	// Transports are not supported for local image look ups.
 | |
| 	srcRef, err := alltransports.ParseImageName(name)
 | |
| 	if err == nil {
 | |
| 		return []string{srcRef.StringWithinTransport()}, srcRef.Transport().Name(), nil
 | |
| 	}
 | |
| 
 | |
| 	var candidates []string
 | |
| 	// Local short-name resolution.
 | |
| 	namedCandidates, err := shortnames.ResolveLocally(sc, name)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 	for _, named := range namedCandidates {
 | |
| 		candidates = append(candidates, named.String())
 | |
| 	}
 | |
| 
 | |
| 	return candidates, DefaultTransport, nil
 | |
| }
 | |
| 
 | |
| // ExpandNames takes unqualified names, parses them as image names, and returns
 | |
| // the fully expanded result, including a tag.  Names which don't include a registry
 | |
| // name will be marked for the most-preferred registry (i.e., the first one in our
 | |
| // configuration).
 | |
| func ExpandNames(names []string, systemContext *types.SystemContext, store storage.Store) ([]string, error) {
 | |
| 	expanded := make([]string, 0, len(names))
 | |
| 	for _, n := range names {
 | |
| 		var name reference.Named
 | |
| 		nameList, _, err := resolveName(n, systemContext, store)
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Wrapf(err, "error parsing name %q", n)
 | |
| 		}
 | |
| 		if len(nameList) == 0 {
 | |
| 			named, err := reference.ParseNormalizedNamed(n)
 | |
| 			if err != nil {
 | |
| 				return nil, errors.Wrapf(err, "error parsing name %q", n)
 | |
| 			}
 | |
| 			name = named
 | |
| 		} else {
 | |
| 			named, err := reference.ParseNormalizedNamed(nameList[0])
 | |
| 			if err != nil {
 | |
| 				return nil, errors.Wrapf(err, "error parsing name %q", nameList[0])
 | |
| 			}
 | |
| 			name = named
 | |
| 		}
 | |
| 		name = reference.TagNameOnly(name)
 | |
| 		expanded = append(expanded, name.String())
 | |
| 	}
 | |
| 	return expanded, nil
 | |
| }
 | |
| 
 | |
| // FindImage locates the locally-stored image which corresponds to a given name.
 | |
| // Please note that the `firstRegistry` argument has been deprecated and has no
 | |
| // effect anymore.
 | |
| func FindImage(store storage.Store, firstRegistry string, systemContext *types.SystemContext, image string) (types.ImageReference, *storage.Image, error) {
 | |
| 	runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	localImage, _, err := runtime.LookupImage(image, nil)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	ref, err := localImage.StorageReference()
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	return ref, localImage.StorageImage(), nil
 | |
| }
 | |
| 
 | |
| // resolveNameToReferences tries to create a list of possible references
 | |
| // (including their transports) from the provided image name.
 | |
| func ResolveNameToReferences(
 | |
| 	store storage.Store,
 | |
| 	systemContext *types.SystemContext,
 | |
| 	image string,
 | |
| ) (refs []types.ImageReference, err error) {
 | |
| 	names, transport, err := resolveName(image, systemContext, store)
 | |
| 	if err != nil {
 | |
| 		return nil, errors.Wrapf(err, "error parsing name %q", image)
 | |
| 	}
 | |
| 
 | |
| 	if transport != DefaultTransport {
 | |
| 		transport += ":"
 | |
| 	}
 | |
| 
 | |
| 	for _, name := range names {
 | |
| 		ref, err := alltransports.ParseImageName(transport + name)
 | |
| 		if err != nil {
 | |
| 			logrus.Debugf("error parsing reference to image %q: %v", name, err)
 | |
| 			continue
 | |
| 		}
 | |
| 		refs = append(refs, ref)
 | |
| 	}
 | |
| 	if len(refs) == 0 {
 | |
| 		return nil, errors.Errorf("error locating images with names %v", names)
 | |
| 	}
 | |
| 	return refs, nil
 | |
| }
 | |
| 
 | |
| // AddImageNames adds the specified names to the specified image.  Please note
 | |
| // that the `firstRegistry` argument has been deprecated and has no effect
 | |
| // anymore.
 | |
| func AddImageNames(store storage.Store, firstRegistry string, systemContext *types.SystemContext, image *storage.Image, addNames []string) error {
 | |
| 	runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	localImage, _, err := runtime.LookupImage(image.ID, nil)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	for _, tag := range addNames {
 | |
| 		if err := localImage.Tag(tag); err != nil {
 | |
| 			return errors.Wrapf(err, "error tagging image %s", image.ID)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GetFailureCause checks the type of the error "err" and returns a new
 | |
| // error message that reflects the reason of the failure.
 | |
| // In case err type is not a familiar one the error "defaultError" is returned.
 | |
| func GetFailureCause(err, defaultError error) error {
 | |
| 	switch nErr := errors.Cause(err).(type) {
 | |
| 	case errcode.Errors:
 | |
| 		return err
 | |
| 	case errcode.Error, *url.Error:
 | |
| 		return nErr
 | |
| 	default:
 | |
| 		return defaultError
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WriteError writes `lastError` into `w` if not nil and return the next error `err`
 | |
| func WriteError(w io.Writer, err error, lastError error) error {
 | |
| 	if lastError != nil {
 | |
| 		fmt.Fprintln(w, lastError)
 | |
| 	}
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Runtime is the default command to use to run the container.
 | |
| func Runtime() string {
 | |
| 	runtime := os.Getenv("BUILDAH_RUNTIME")
 | |
| 	if runtime != "" {
 | |
| 		return runtime
 | |
| 	}
 | |
| 
 | |
| 	conf, err := config.Default()
 | |
| 	if err != nil {
 | |
| 		logrus.Warnf("Error loading container config when searching for local runtime: %v", err)
 | |
| 		return define.DefaultRuntime
 | |
| 	}
 | |
| 	return conf.Engine.OCIRuntime
 | |
| }
 | |
| 
 | |
| // StringInSlice returns a boolean indicating if the exact value s is present
 | |
| // in the slice slice.
 | |
| func StringInSlice(s string, slice []string) bool {
 | |
| 	for _, v := range slice {
 | |
| 		if v == s {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // GetContainerIDs uses ID mappings to compute the container-level IDs that will
 | |
| // correspond to a UID/GID pair on the host.
 | |
| func GetContainerIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32, uint32, error) {
 | |
| 	uidMapped := true
 | |
| 	for _, m := range uidmap {
 | |
| 		uidMapped = false
 | |
| 		if uid >= m.HostID && uid < m.HostID+m.Size {
 | |
| 			uid = (uid - m.HostID) + m.ContainerID
 | |
| 			uidMapped = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !uidMapped {
 | |
| 		return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map UID %d", uidmap, uid)
 | |
| 	}
 | |
| 	gidMapped := true
 | |
| 	for _, m := range gidmap {
 | |
| 		gidMapped = false
 | |
| 		if gid >= m.HostID && gid < m.HostID+m.Size {
 | |
| 			gid = (gid - m.HostID) + m.ContainerID
 | |
| 			gidMapped = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !gidMapped {
 | |
| 		return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map GID %d", gidmap, gid)
 | |
| 	}
 | |
| 	return uid, gid, nil
 | |
| }
 | |
| 
 | |
| // GetHostIDs uses ID mappings to compute the host-level IDs that will
 | |
| // correspond to a UID/GID pair in the container.
 | |
| func GetHostIDs(uidmap, gidmap []specs.LinuxIDMapping, uid, gid uint32) (uint32, uint32, error) {
 | |
| 	uidMapped := true
 | |
| 	for _, m := range uidmap {
 | |
| 		uidMapped = false
 | |
| 		if uid >= m.ContainerID && uid < m.ContainerID+m.Size {
 | |
| 			uid = (uid - m.ContainerID) + m.HostID
 | |
| 			uidMapped = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !uidMapped {
 | |
| 		return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map UID %d", uidmap, uid)
 | |
| 	}
 | |
| 	gidMapped := true
 | |
| 	for _, m := range gidmap {
 | |
| 		gidMapped = false
 | |
| 		if gid >= m.ContainerID && gid < m.ContainerID+m.Size {
 | |
| 			gid = (gid - m.ContainerID) + m.HostID
 | |
| 			gidMapped = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if !gidMapped {
 | |
| 		return 0, 0, errors.Errorf("container uses ID mappings (%#v), but doesn't map GID %d", gidmap, gid)
 | |
| 	}
 | |
| 	return uid, gid, nil
 | |
| }
 | |
| 
 | |
| // GetHostRootIDs uses ID mappings in spec to compute the host-level IDs that will
 | |
| // correspond to UID/GID 0/0 in the container.
 | |
| func GetHostRootIDs(spec *specs.Spec) (uint32, uint32, error) {
 | |
| 	if spec == nil || spec.Linux == nil {
 | |
| 		return 0, 0, nil
 | |
| 	}
 | |
| 	return GetHostIDs(spec.Linux.UIDMappings, spec.Linux.GIDMappings, 0, 0)
 | |
| }
 | |
| 
 | |
| // GetPolicyContext sets up, initializes and returns a new context for the specified policy
 | |
| func GetPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error) {
 | |
| 	policy, err := signature.DefaultPolicy(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	policyContext, err := signature.NewPolicyContext(policy)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return policyContext, nil
 | |
| }
 | |
| 
 | |
| // logIfNotErrno logs the error message unless err is either nil or one of the
 | |
| // listed syscall.Errno values.  It returns true if it logged an error.
 | |
| func logIfNotErrno(err error, what string, ignores ...syscall.Errno) (logged bool) {
 | |
| 	if err == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	if errno, isErrno := err.(syscall.Errno); isErrno {
 | |
| 		for _, ignore := range ignores {
 | |
| 			if errno == ignore {
 | |
| 				return false
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	logrus.Error(what)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // LogIfNotRetryable logs "what" if err is set and is not an EINTR or EAGAIN
 | |
| // syscall.Errno.  Returns "true" if we can continue.
 | |
| func LogIfNotRetryable(err error, what string) (retry bool) {
 | |
| 	return !logIfNotErrno(err, what, syscall.EINTR, syscall.EAGAIN)
 | |
| }
 | |
| 
 | |
| // LogIfUnexpectedWhileDraining logs "what" if err is set and is not an EINTR
 | |
| // or EAGAIN or EIO syscall.Errno.
 | |
| func LogIfUnexpectedWhileDraining(err error, what string) {
 | |
| 	logIfNotErrno(err, what, syscall.EINTR, syscall.EAGAIN, syscall.EIO)
 | |
| }
 | |
| 
 | |
| // TruncateString trims the given string to the provided maximum amount of
 | |
| // characters and shortens it with `...`.
 | |
| func TruncateString(str string, to int) string {
 | |
| 	newStr := str
 | |
| 	if len(str) > to {
 | |
| 		const tr = "..."
 | |
| 		if to > len(tr) {
 | |
| 			to -= len(tr)
 | |
| 		}
 | |
| 		newStr = str[0:to] + tr
 | |
| 	}
 | |
| 	return newStr
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	isUnifiedOnce sync.Once
 | |
| 	isUnified     bool
 | |
| 	isUnifiedErr  error
 | |
| )
 | |
| 
 | |
| // fileExistsAndNotADir - Check to see if a file exists
 | |
| // and that it is not a directory.
 | |
| func fileExistsAndNotADir(path string) bool {
 | |
| 	file, err := os.Stat(path)
 | |
| 
 | |
| 	if file == nil || err != nil || os.IsNotExist(err) {
 | |
| 		return false
 | |
| 	}
 | |
| 	return !file.IsDir()
 | |
| }
 | |
| 
 | |
| // FindLocalRuntime find the local runtime of the
 | |
| // system searching through the config file for
 | |
| // possible locations.
 | |
| func FindLocalRuntime(runtime string) string {
 | |
| 	var localRuntime string
 | |
| 	conf, err := config.Default()
 | |
| 	if err != nil {
 | |
| 		logrus.Debugf("Error loading container config when searching for local runtime.")
 | |
| 		return localRuntime
 | |
| 	}
 | |
| 	for _, val := range conf.Engine.OCIRuntimes[runtime] {
 | |
| 		if fileExistsAndNotADir(val) {
 | |
| 			localRuntime = val
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return localRuntime
 | |
| }
 | |
| 
 | |
| // MergeEnv merges two lists of environment variables, avoiding duplicates.
 | |
| func MergeEnv(defaults, overrides []string) []string {
 | |
| 	s := make([]string, 0, len(defaults)+len(overrides))
 | |
| 	index := make(map[string]int)
 | |
| 	for _, envSpec := range append(defaults, overrides...) {
 | |
| 		envVar := strings.SplitN(envSpec, "=", 2)
 | |
| 		if i, ok := index[envVar[0]]; ok {
 | |
| 			s[i] = envSpec
 | |
| 			continue
 | |
| 		}
 | |
| 		s = append(s, envSpec)
 | |
| 		index[envVar[0]] = len(s) - 1
 | |
| 	}
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| type byDestination []specs.Mount
 | |
| 
 | |
| func (m byDestination) Len() int {
 | |
| 	return len(m)
 | |
| }
 | |
| 
 | |
| func (m byDestination) Less(i, j int) bool {
 | |
| 	return m.parts(i) < m.parts(j)
 | |
| }
 | |
| 
 | |
| func (m byDestination) Swap(i, j int) {
 | |
| 	m[i], m[j] = m[j], m[i]
 | |
| }
 | |
| 
 | |
| func (m byDestination) parts(i int) int {
 | |
| 	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
 | |
| }
 | |
| 
 | |
| func SortMounts(m []specs.Mount) []specs.Mount {
 | |
| 	sort.Sort(byDestination(m))
 | |
| 	return m
 | |
| }
 | |
| 
 | |
| func VerifyTagName(imageSpec string) (types.ImageReference, error) {
 | |
| 	ref, err := alltransports.ParseImageName(imageSpec)
 | |
| 	if err != nil {
 | |
| 		if ref, err = alltransports.ParseImageName(DefaultTransport + imageSpec); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	return ref, nil
 | |
| }
 |