migrate Podman to containers/common/libimage

Migrate the Podman code base over to `common/libimage` which replaces
`libpod/image` and a lot of glue code entirely.

Note that I tried to leave bread crumbs for changed tests.

Miscellaneous changes:

 * Some errors yield different messages which required to alter some
   tests.

 * I fixed some pre-existing issues in the code.  Others were marked as
   `//TODO`s to prevent the PR from exploding.

 * The `NamesHistory` of an image is returned as is from the storage.
   Previously, we did some filtering which I think is undesirable.
   Instead we should return the data as stored in the storage.

 * Touched handlers use the ABI interfaces where possible.

 * Local image resolution: previously Podman would match "foo" on
   "myfoo".  This behaviour has been changed and Podman will now
   only match on repository boundaries such that "foo" would match
   "my/foo" but not "myfoo".  I consider the old behaviour to be a
   bug, at the very least an exotic corner case.

 * Futhermore, "foo:none" does *not* resolve to a local image "foo"
   without tag anymore.  It's a hill I am (almost) willing to die on.

 * `image prune` prints the IDs of pruned images.  Previously, in some
   cases, the names were printed instead.  The API clearly states ID,
   so we should stick to it.

 * Compat endpoint image removal with _force_ deletes the entire not
   only the specified tag.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2021-04-22 08:01:12 +02:00
parent 8eefca5a25
commit 0f7d54b026
190 changed files with 8669 additions and 7743 deletions

View File

@@ -246,10 +246,17 @@ rootless=%d
bindFiles["/run/.containerenv"] = containerenvPath
}
err = b.setupMounts(mountPoint, spec, path, options.Mounts, bindFiles, volumes, b.CommonBuildOpts.Volumes, b.CommonBuildOpts.ShmSize, namespaceOptions)
runMountTargets, err := b.setupMounts(mountPoint, spec, path, options.Mounts, bindFiles, volumes, b.CommonBuildOpts.Volumes, b.CommonBuildOpts.ShmSize, namespaceOptions, options.Secrets, options.RunMounts)
if err != nil {
return errors.Wrapf(err, "error resolving mountpoints for container %q", b.ContainerID)
}
defer func() {
if err := cleanupRunMounts(runMountTargets, mountPoint); err != nil {
logrus.Errorf("unabe to cleanup run mounts %v", err)
}
}()
defer b.cleanupTempVolumes()
if options.CNIConfigDir == "" {
@@ -341,16 +348,16 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin
// Add temporary copies of the contents of volume locations at the
// volume locations, unless we already have something there.
for _, volume := range builtinVolumes {
subdir := digest.Canonical.FromString(volume).Hex()
volumePath := filepath.Join(containerDir, "buildah-volumes", subdir)
srcPath := filepath.Join(mountPoint, volume)
volumePath := filepath.Join(containerDir, "buildah-volumes", digest.Canonical.FromString(volume).Hex())
initializeVolume := false
// If we need to, initialize the volume path's initial contents.
// If we need to, create the directory that we'll use to hold
// the volume contents. If we do need to create it, then we'll
// need to populate it, too, so make a note of that.
if _, err := os.Stat(volumePath); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
logrus.Debugf("setting up built-in volume at %q", volumePath)
logrus.Debugf("setting up built-in volume path at %q for %q", volumePath, volume)
if err = os.MkdirAll(volumePath, 0755); err != nil {
return nil, err
}
@@ -359,28 +366,25 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin
}
initializeVolume = true
}
// Check if srcPath is a symlink
stat, err := os.Lstat(srcPath)
// If srcPath is a symlink, follow the link and ensure the destination exists
if err == nil && stat != nil && (stat.Mode()&os.ModeSymlink != 0) {
srcPath, err = copier.Eval(mountPoint, volume, copier.EvalOptions{})
if err != nil {
return nil, errors.Wrapf(err, "evaluating symlink %q", srcPath)
}
// Stat the destination of the evaluated symlink
stat, err = os.Stat(srcPath)
}
// Make sure the volume exists in the rootfs and read its attributes.
createDirPerms := os.FileMode(0755)
err := copier.Mkdir(mountPoint, filepath.Join(mountPoint, volume), copier.MkdirOptions{
ChownNew: &hostOwner,
ChmodNew: &createDirPerms,
})
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
if err = idtools.MkdirAllAndChownNew(srcPath, 0755, hostOwner); err != nil {
return nil, err
}
if stat, err = os.Stat(srcPath); err != nil {
return nil, err
}
return nil, errors.Wrapf(err, "ensuring volume path %q", filepath.Join(mountPoint, volume))
}
srcPath, err := copier.Eval(mountPoint, filepath.Join(mountPoint, volume), copier.EvalOptions{})
if err != nil {
return nil, errors.Wrapf(err, "evaluating path %q", srcPath)
}
stat, err := os.Stat(srcPath)
if err != nil && !os.IsNotExist(err) {
return nil, err
}
// If we need to populate the mounted volume's contents with
// content from the rootfs, set it up now.
if initializeVolume {
if err = os.Chmod(volumePath, stat.Mode().Perm()); err != nil {
return nil, err
@@ -388,6 +392,7 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin
if err = os.Chown(volumePath, int(stat.Sys().(*syscall.Stat_t).Uid), int(stat.Sys().(*syscall.Stat_t).Gid)); err != nil {
return nil, err
}
logrus.Debugf("populating directory %q for volume %q using contents of %q", volumePath, volume, srcPath)
if err = extractWithTar(mountPoint, srcPath, volumePath); err != nil && !os.IsNotExist(errors.Cause(err)) {
return nil, errors.Wrapf(err, "error populating directory %q for volume %q using contents of %q", volumePath, volume, srcPath)
}
@@ -403,7 +408,7 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin
return mounts, nil
}
func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, shmSize string, namespaceOptions define.NamespaceOptions) error {
func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, shmSize string, namespaceOptions define.NamespaceOptions, secrets map[string]string, runFileMounts []string) (runMountTargets []string, err error) {
// Start building a new list of mounts.
var mounts []specs.Mount
haveMount := func(destination string) bool {
@@ -497,39 +502,45 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
// After this point we need to know the per-container persistent storage directory.
cdir, err := b.store.ContainerDirectory(b.ContainerID)
if err != nil {
return errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID)
return nil, errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID)
}
// Figure out which UID and GID to tell the subscriptions package to use
// for files that it creates.
rootUID, rootGID, err := util.GetHostRootIDs(spec)
if err != nil {
return err
return nil, err
}
// Get the list of subscriptions mounts.
secretMounts := subscriptions.MountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false)
subscriptionMounts := subscriptions.MountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false)
// Get the list of mounts that are just for this Run() call.
runMounts, runTargets, err := runSetupRunMounts(runFileMounts, secrets, b.MountLabel, cdir, spec.Linux.UIDMappings, spec.Linux.GIDMappings)
if err != nil {
return nil, err
}
// Add temporary copies of the contents of volume locations at the
// volume locations, unless we already have something there.
builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, builtinVolumes, int(rootUID), int(rootGID))
if err != nil {
return err
return nil, err
}
// Get host UID and GID of the container process.
processUID, processGID, err := util.GetHostIDs(spec.Linux.UIDMappings, spec.Linux.GIDMappings, spec.Process.User.UID, spec.Process.User.GID)
if err != nil {
return err
return nil, err
}
// Get the list of explicitly-specified volume mounts.
volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID), int(processUID), int(processGID))
if err != nil {
return err
return nil, err
}
allMounts := util.SortMounts(append(append(append(append(append(volumes, builtins...), secretMounts...), bindFileMounts...), specMounts...), sysfsMount...))
allMounts := util.SortMounts(append(append(append(append(append(append(volumes, builtins...), runMounts...), subscriptionMounts...), bindFileMounts...), specMounts...), sysfsMount...))
// Add them all, in the preferred order, except where they conflict with something that was previously added.
for _, mount := range allMounts {
if haveMount(mount.Destination) {
@@ -542,7 +553,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
// Set the list in the spec.
spec.Mounts = mounts
return nil
return runTargets, nil
}
// addNetworkConfig copies files from host and sets them up to bind mount into container
@@ -818,7 +829,7 @@ func runUsingRuntime(isolation define.Isolation, options RunOptions, configureNe
logrus.Debugf("Running %q", create.Args)
err = create.Run()
if err != nil {
return 1, errors.Wrapf(err, "error creating container for %v: %s", pargs, runCollectOutput(errorFds, closeBeforeReadingErrorFds))
return 1, errors.Wrapf(err, "error from %s creating container for %v: %s", runtime, pargs, runCollectOutput(errorFds, closeBeforeReadingErrorFds))
}
defer func() {
err2 := del.Run()
@@ -826,7 +837,7 @@ func runUsingRuntime(isolation define.Isolation, options RunOptions, configureNe
if err == nil {
err = errors.Wrapf(err2, "error deleting container")
} else {
logrus.Infof("error deleting container: %v", err2)
logrus.Infof("error from %s deleting container: %v", runtime, err2)
}
}
}()
@@ -879,13 +890,13 @@ func runUsingRuntime(isolation define.Isolation, options RunOptions, configureNe
logrus.Debugf("Running %q", start.Args)
err = start.Run()
if err != nil {
return 1, errors.Wrapf(err, "error starting container")
return 1, errors.Wrapf(err, "error from %s starting container", runtime)
}
stopped := false
defer func() {
if !stopped {
if err2 := kill.Run(); err2 != nil {
logrus.Infof("error stopping container: %v", err2)
logrus.Infof("error from %s stopping container: %v", runtime, err2)
}
}
}()
@@ -900,10 +911,10 @@ func runUsingRuntime(isolation define.Isolation, options RunOptions, configureNe
stat.Stderr = os.Stderr
stateOutput, err := stat.Output()
if err != nil {
return 1, errors.Wrapf(err, "error reading container state (got output: %q)", string(stateOutput))
return 1, errors.Wrapf(err, "error reading container state from %s (got output: %q)", runtime, string(stateOutput))
}
if err = json.Unmarshal(stateOutput, &state); err != nil {
return 1, errors.Wrapf(err, "error parsing container state %q", string(stateOutput))
return 1, errors.Wrapf(err, "error parsing container state %q from %s", string(stateOutput), runtime)
}
switch state.Status {
case "running":
@@ -2248,3 +2259,149 @@ type runUsingRuntimeSubprocOptions struct {
func init() {
reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain)
}
// runSetupRunMounts sets up mounts that exist only in this RUN, not in subsequent runs
func runSetupRunMounts(mounts []string, secrets map[string]string, mountlabel string, containerWorkingDir string, uidmap []spec.LinuxIDMapping, gidmap []spec.LinuxIDMapping) ([]spec.Mount, []string, error) {
mountTargets := make([]string, 0, 10)
finalMounts := make([]specs.Mount, 0, len(mounts))
for _, mount := range mounts {
arr := strings.SplitN(mount, ",", 2)
if len(arr) < 2 {
return nil, nil, errors.New("invalid mount syntax")
}
kv := strings.Split(arr[0], "=")
if len(kv) != 2 || kv[0] != "type" {
return nil, nil, errors.New("invalid mount type")
}
tokens := strings.Split(arr[1], ",")
// For now, we only support type secret.
switch kv[1] {
case "secret":
mount, err := getSecretMount(tokens, secrets, mountlabel, containerWorkingDir, uidmap, gidmap)
if err != nil {
return nil, nil, err
}
if mount != nil {
finalMounts = append(finalMounts, *mount)
mountTargets = append(mountTargets, mount.Destination)
}
default:
return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
}
}
return finalMounts, mountTargets, nil
}
func getSecretMount(tokens []string, secrets map[string]string, mountlabel string, containerWorkingDir string, uidmap []spec.LinuxIDMapping, gidmap []spec.LinuxIDMapping) (*spec.Mount, error) {
errInvalidSyntax := errors.New("secret should have syntax id=id[,target=path,required=bool,mode=uint,uid=uint,gid=uint")
var err error
var id, target string
var required bool
var uid, gid uint32
var mode uint32 = 400
for _, val := range tokens {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "id":
id = kv[1]
case "target":
target = kv[1]
case "required":
required, err = strconv.ParseBool(kv[1])
if err != nil {
return nil, errInvalidSyntax
}
case "mode":
mode64, err := strconv.ParseUint(kv[1], 8, 32)
if err != nil {
return nil, errInvalidSyntax
}
mode = uint32(mode64)
case "uid":
uid64, err := strconv.ParseUint(kv[1], 10, 32)
if err != nil {
return nil, errInvalidSyntax
}
uid = uint32(uid64)
case "gid":
gid64, err := strconv.ParseUint(kv[1], 10, 32)
if err != nil {
return nil, errInvalidSyntax
}
gid = uint32(gid64)
default:
return nil, errInvalidSyntax
}
}
if id == "" {
return nil, errInvalidSyntax
}
// Default location for secretis is /run/secrets/id
if target == "" {
target = "/run/secrets/" + id
}
src, ok := secrets[id]
if !ok {
if required {
return nil, errors.Errorf("secret required but no secret with id %s found", id)
}
return nil, nil
}
// Copy secrets to container working dir, since we need to chmod, chown and relabel it
// for the container user and we don't want to mess with the original file
ctrFileOnHost := filepath.Join(containerWorkingDir, "secrets", id)
_, err = os.Stat(ctrFileOnHost)
if os.IsNotExist(err) {
data, err := ioutil.ReadFile(src)
if err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Dir(ctrFileOnHost), 0644); err != nil {
return nil, err
}
if err := ioutil.WriteFile(ctrFileOnHost, data, 0644); err != nil {
return nil, err
}
}
if err := label.Relabel(ctrFileOnHost, mountlabel, false); err != nil {
return nil, err
}
hostUID, hostGID, err := util.GetHostIDs(uidmap, gidmap, uid, gid)
if err != nil {
return nil, err
}
if err := os.Lchown(ctrFileOnHost, int(hostUID), int(hostGID)); err != nil {
return nil, err
}
if err := os.Chmod(ctrFileOnHost, os.FileMode(mode)); err != nil {
return nil, err
}
newMount := specs.Mount{
Destination: target,
Type: "bind",
Source: ctrFileOnHost,
Options: []string{"bind", "rprivate", "ro"},
}
return &newMount, nil
}
func cleanupRunMounts(paths []string, mountpoint string) error {
opts := copier.RemoveOptions{
All: true,
}
for _, path := range paths {
err := copier.Remove(mountpoint, path, opts)
if err != nil {
return err
}
}
return nil
}