Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Image mount #4209

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/podman/cliconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ type MountValues struct {
Format string
NoTrunc bool
Latest bool
Image bool
}

type NetworkCreateValues struct {
Expand Down Expand Up @@ -569,6 +570,7 @@ type UmountValues struct {
All bool
Force bool
Latest bool
Image bool
}

type UnpauseValues struct {
Expand Down
30 changes: 29 additions & 1 deletion cmd/podman/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
js "encoding/json"
"fmt"
"os"
"path/filepath"

of "github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -49,6 +51,7 @@ func init() {
flags.StringVar(&mountCommand.Format, "format", "", "Change the output format to Go template")
flags.BoolVarP(&mountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&mountCommand.NoTrunc, "notruncate", false, "Do not truncate output")
flags.BoolVar(&mountCommand.Image, "image", false, "mount image to folder")

markFlagHiddenForRemoteClient("latest", flags)
}
Expand All @@ -66,7 +69,17 @@ func mountCmd(c *cliconfig.MountValues) error {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.DeferredShutdown(false)

if c.Image {
if len(c.InputArgs) > 1 {
fmt.Println("only one image can be mounted at a time")
}
path, err := mountImage(runtime, c.InputArgs[0])
if err != nil {
return err
}
fmt.Println(path)
return nil
}
if os.Geteuid() != 0 {
rtc, err := runtime.GetConfig()
if err != nil {
Expand Down Expand Up @@ -167,3 +180,18 @@ func mountCmd(c *cliconfig.MountValues) error {
}
return nil
}

func mountImage(runtime *libpod.Runtime, img string) (string, error) {
image, err := runtime.ImageRuntime().NewFromLocal(img)
if err != nil {
return "", fmt.Errorf("no image found : %s", img)
}

mountPoint, err := runtime.ImageRuntime().MountImage(image.ID())
if err != nil {
return "", fmt.Errorf("no image found : %s", img)
}
mountPoint, err = filepath.EvalSymlinks(mountPoint)

return mountPoint, nil
}
18 changes: 16 additions & 2 deletions cmd/podman/umount.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ var (
},
Example: `podman umount ctrID
podman umount ctrID1 ctrID2 ctrID3
podman umount --all`,
podman umount --all
podman umount --image imageID`,
}
)

Expand All @@ -44,17 +45,30 @@ func init() {
flags.BoolVarP(&umountCommand.All, "all", "a", false, "Umount all of the currently mounted containers")
flags.BoolVarP(&umountCommand.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers")
flags.BoolVarP(&umountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVarP(&umountCommand.Image, "image", "i", false, "umount image")
markFlagHiddenForRemoteClient("latest", flags)
}

func umountCmd(c *cliconfig.UmountValues) error {

var (
ok []string
failures map[string]error
err error
)

runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating runtime")
}
defer runtime.DeferredShutdown(false)

ok, failures, err := runtime.UmountRootFilesystems(getContext(), c)
if c.Image {
ok, failures, err = runtime.UmountImage(c)
} else {
ok, failures, err = runtime.UmountRootFilesystems(getContext(), c)
}

if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/podman/varlink/io.podman.varlink
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,8 @@ method ImagesPrune(all: bool) -> (pruned: []string)
# This function is not implemented yet.
# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)

method UnmountImage(name: string, force: bool) -> (notimplemented: NotImplemented)

# GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
# and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
method GenerateKube(name: string, service: bool) -> (pod: KubePodService)
Expand Down
38 changes: 38 additions & 0 deletions libpod/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,42 @@ func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
return &image
}

// MountImage mounts the image to tempfolder and returns.
func (ir *Runtime) MountImage(idOrName string) (string, error) {
image, err := ir.store.Image(idOrName)
if err != nil {
return "", err
}
//get image top layer id
mountPoint, err := ir.store.MountImage(image.TopLayer, []string{"ro"})
if err != nil {
logrus.Debugf("failed to mount image %q: %v", image.ID, err)
return "", err
}
logrus.Debugf("mounted image %q at %q", image.ID, mountPoint)
return mountPoint, nil
}

// UnmountImage unmounts image
func (ir *Runtime) UnmountImage(idOrName string, force bool) (bool, error) {
image, err := ir.store.Image(idOrName)
if err != nil {
return false, err
}
// pass top layer of image for umount
mounted, err := ir.store.Unmount(image.TopLayer, force)
if err != nil {
return false, err
}
logrus.Debugf("unmounted image %q ", image.ID)
return mounted, nil
}

// NewFromLocal creates a new image object that is intended
// to only deal with local images already in the store (or
// its aliases)
func (ir *Runtime) NewFromLocal(name string) (*Image, error) {
logrus.Info("ImageFromLocal: ", ir)
image := Image{
InputName: name,
imageruntime: ir,
Expand Down Expand Up @@ -216,9 +248,12 @@ func (i *Image) reloadImage() error {

// stringSha256 strips sha256 from user input
func stripSha256(name string) string {
logrus.Info("stripSha256 : ", name)
if strings.HasPrefix(name, "sha256:") && len(name) > 7 {
logrus.Info("stripSha256 : returning inside.. ", name)
return name[7:]
}
logrus.Info("stripSha256 : returning outside.. ", name)
return name
}

Expand Down Expand Up @@ -404,11 +439,14 @@ func (i *Image) GetManifest() error {
// If no matching image can be found, an error is returned
func (ir *Runtime) getImage(image string) (*Image, error) {
var img *storage.Image
logrus.Info("getImage...")
logrus.Info(ir)
ref, err := is.Transport.ParseStoreReference(ir.store, image)
if err == nil {
img, err = is.Transport.GetStoreImage(ir.store, ref)
}
if err != nil {
logrus.Info("not nil ", ir.store, image)
img2, err2 := ir.store.Image(image)
if err2 != nil {
if ref == nil {
Expand Down
31 changes: 31 additions & 0 deletions libpod/image/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/containers/libpod/libpod/events"
Expand Down Expand Up @@ -292,3 +293,33 @@ func TestNormalizedTag(t *testing.T) {
}
}
}

func TestImageMount(t *testing.T) {
if os.Geteuid() != 0 { // containers/storage requires root access
t.Skipf("Test not running as root")
}

//Set up
workdir, err := mkWorkDir()
t.Log(workdir)
assert.NoError(t, err)

so := storage.StoreOptions{
RunRoot: workdir,
GraphRoot: workdir,
}
ir, err := NewImageRuntimeFromOptions(so)
assert.NoError(t, err)
ir.Eventer = events.NewNullEventer()
newImage, err := ir.New(context.Background(), "docker.io/library/ubuntu:latest", "", "", os.Stdout, nil, SigningOptions{}, nil, util.PullImageMissing)
assert.NoError(t, err)

mountPoint, err := ir.MountImage(newImage.ID())
mountPoint, err = filepath.EvalSymlinks(mountPoint)
assert.NoError(t, err)

_, err = ir.UnmountImage(newImage.ID(), false)
assert.NoError(t, err)
cleanup(workdir, ir)

}
2 changes: 1 addition & 1 deletion libpod/runtime_img.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/containers/image/v4/directory"
dockerarchive "github.com/containers/image/v4/docker/archive"
ociarchive "github.com/containers/image/v4/oci/archive"
"github.com/opencontainers/image-spec/specs-go/v1"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)

// Runtime API
Expand Down
32 changes: 31 additions & 1 deletion libpod/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/containers/image/v4/types"
"github.com/containers/libpod/libpod/define"
"github.com/containers/storage"
"github.com/opencontainers/image-spec/specs-go/v1"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -211,6 +211,36 @@ func (r *storageService) GetContainerMetadata(idOrName string) (RuntimeContainer
return metadata, nil
}

// MountImage mounts the image to tempfolder and returns.
func (r *storageService) MountImage(idOrName string) (string, error) {
image, err := r.store.Image(idOrName)
if err != nil {
return "", err
}
mountPoint, err := r.store.MountImage(image.ID, []string{"ro"})
if err != nil {
logrus.Debugf("failed to mount image %q: %v", image.ID, err)
return "", err
}
logrus.Debugf("mounted image %q at %q", image.ID, mountPoint)
return mountPoint, nil
}

func (r *storageService) UnmountImage(idOrName string, force bool) (bool, error) {
image, err := r.store.Image(idOrName)
if err != nil {
return false, err
}
mounted, err := r.store.Unmount(image.TopLayer, force)
if err != nil {
logrus.Debugf("failed to unmount image %q: %v", image.ID, err)
return false, err
}
logrus.Debugf("unmounted image %q", image.ID)
return mounted, nil

}

func (r *storageService) MountContainerImage(idOrName string) (string, error) {
container, err := r.store.Container(idOrName)
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions pkg/adapter/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package adapter

import (
"fmt"

"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
Expand Down Expand Up @@ -32,3 +34,18 @@ func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[stri
}
return imageInfo, layerInfoMap, img, nil
}

// UmountImage removes container(s) based on CLI inputs.
func (r *LocalRuntime) UmountImage(cli *cliconfig.UmountValues) ([]string, map[string]error, error) {
var (
ok = []string{}
failures = map[string]error{}
)
fmt.Println("unmounting... ", cli.InputArgs[0])
_, err := r.Runtime.ImageRuntime().UnmountImage(cli.InputArgs[0], cli.Force)
if err != nil {
return ok, failures, err
}
ok = append(ok, cli.InputArgs[0])
return ok, failures, nil
}
15 changes: 15 additions & 0 deletions pkg/adapter/images_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,18 @@ func (r *LocalRuntime) Tree(c *cliconfig.TreeValues) (*image.InfoImage, map[stri

return imageInfo, layerInfoMap, img, nil
}

// UmountImage removes container(s) based on CLI inputs.
func (r *LocalRuntime) UmountImage(cli *cliconfig.UmountValues) ([]string, map[string]error, error) {
var (
ok = []string{}
failures = map[string]error{}
)

_, err := iopodman.UnmountImage().Call(r.Conn, cli.InputArgs[0], cli.Force)
if err != nil {
return ok, failures, err
}
ok = append(ok, cli.InputArgs[1])
return ok, failures, nil
}
7 changes: 6 additions & 1 deletion pkg/adapter/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
)

// LocalRuntime describes a typical libpod runtime
Expand Down Expand Up @@ -454,3 +454,8 @@ func (r *LocalRuntime) GetVersion() (define.Version, error) {
func (r *LocalRuntime) RemoteEndpoint() (*Endpoint, error) {
return nil, errors.New("RemoteEndpoint() not implemented for local connection")
}

// GetLocalRuntime returns local liblod.Runtime
func (r *LocalRuntime) GetLocalRuntime() *libpod.Runtime {
return r.Runtime
}