mirror of
https://github.com/containers/podman.git
synced 2025-07-02 08:47:43 +08:00
podman: accept option --rootfs to use exploded images
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com> Closes: #871 Approved by: mheon
This commit is contained in:

committed by
Atomic Bot

parent
4b4de5dc21
commit
7bdfb4f9b3
@ -322,6 +322,10 @@ var createFlags = []cli.Flag{
|
|||||||
Name: "rm",
|
Name: "rm",
|
||||||
Usage: "Remove container (and pod if created) after exit",
|
Usage: "Remove container (and pod if created) after exit",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "rootfs",
|
||||||
|
Usage: "The first argument is not an image but the rootfs to the exploded container",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "security-opt",
|
Name: "security-opt",
|
||||||
Usage: "Security Options (default [])",
|
Usage: "Security Options (default [])",
|
||||||
|
@ -72,6 +72,11 @@ func createCmd(c *cli.Context) error {
|
|||||||
return errors.Errorf("image name or ID is required")
|
return errors.Errorf("image name or ID is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootfs := ""
|
||||||
|
if c.Bool("rootfs") {
|
||||||
|
rootfs = c.Args()[0]
|
||||||
|
}
|
||||||
|
|
||||||
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
|
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -89,12 +94,17 @@ func createCmd(c *cli.Context) error {
|
|||||||
rtc := runtime.GetConfig()
|
rtc := runtime.GetConfig()
|
||||||
ctx := getContext()
|
ctx := getContext()
|
||||||
|
|
||||||
newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
|
imageName := ""
|
||||||
if err != nil {
|
var data *inspect.ImageData = nil
|
||||||
return err
|
if rootfs == "" {
|
||||||
|
newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, err = newImage.Inspect(ctx)
|
||||||
|
imageName = newImage.Names()[0]
|
||||||
}
|
}
|
||||||
data, err := newImage.Inspect(ctx)
|
createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
|
||||||
createConfig, err := parseCreateOpts(ctx, c, runtime, newImage.Names()[0], data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -118,6 +128,9 @@ func createCmd(c *cli.Context) error {
|
|||||||
options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
|
options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
|
||||||
options = append(options, libpod.WithGroups(createConfig.GroupAdd))
|
options = append(options, libpod.WithGroups(createConfig.GroupAdd))
|
||||||
options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
|
options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
|
||||||
|
if createConfig.Rootfs != "" {
|
||||||
|
options = append(options, libpod.WithRootFS(createConfig.Rootfs))
|
||||||
|
}
|
||||||
ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
|
ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -246,10 +259,16 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
imageID := data.ID
|
imageID := ""
|
||||||
|
|
||||||
if len(c.Args()) > 1 {
|
inputCommand = c.Args()[1:]
|
||||||
inputCommand = c.Args()[1:]
|
if data != nil {
|
||||||
|
imageID = data.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs := ""
|
||||||
|
if c.Bool("rootfs") {
|
||||||
|
rootfs = c.Args()[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
sysctl, err := validateSysctl(c.StringSlice("sysctl"))
|
sysctl, err := validateSysctl(c.StringSlice("sysctl"))
|
||||||
@ -337,12 +356,19 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
// USER
|
// USER
|
||||||
user := c.String("user")
|
user := c.String("user")
|
||||||
if user == "" {
|
if user == "" {
|
||||||
user = data.ContainerConfig.User
|
if data == nil {
|
||||||
|
user = "0"
|
||||||
|
} else {
|
||||||
|
user = data.ContainerConfig.User
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// STOP SIGNAL
|
// STOP SIGNAL
|
||||||
stopSignal := syscall.SIGTERM
|
stopSignal := syscall.SIGTERM
|
||||||
signalString := data.ContainerConfig.StopSignal
|
signalString := "SIGTERM"
|
||||||
|
if data != nil {
|
||||||
|
signalString = data.ContainerConfig.StopSignal
|
||||||
|
}
|
||||||
if c.IsSet("stop-signal") {
|
if c.IsSet("stop-signal") {
|
||||||
signalString = c.String("stop-signal")
|
signalString = c.String("stop-signal")
|
||||||
}
|
}
|
||||||
@ -355,12 +381,14 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
|
|
||||||
// ENVIRONMENT VARIABLES
|
// ENVIRONMENT VARIABLES
|
||||||
env := defaultEnvVariables
|
env := defaultEnvVariables
|
||||||
for _, e := range data.ContainerConfig.Env {
|
if data != nil {
|
||||||
split := strings.SplitN(e, "=", 2)
|
for _, e := range data.ContainerConfig.Env {
|
||||||
if len(split) > 1 {
|
split := strings.SplitN(e, "=", 2)
|
||||||
env[split[0]] = split[1]
|
if len(split) > 1 {
|
||||||
} else {
|
env[split[0]] = split[1]
|
||||||
env[split[0]] = ""
|
} else {
|
||||||
|
env[split[0]] = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := readKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil {
|
if err := readKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil {
|
||||||
@ -372,9 +400,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "unable to process labels")
|
return nil, errors.Wrapf(err, "unable to process labels")
|
||||||
}
|
}
|
||||||
for key, val := range data.ContainerConfig.Labels {
|
if data != nil {
|
||||||
if _, ok := labels[key]; !ok {
|
for key, val := range data.ContainerConfig.Labels {
|
||||||
labels[key] = val
|
if _, ok := labels[key]; !ok {
|
||||||
|
labels[key] = val
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,9 +416,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
if tty {
|
if tty {
|
||||||
annotations[ann.TTY] = "true"
|
annotations[ann.TTY] = "true"
|
||||||
}
|
}
|
||||||
// Next, add annotations from the image
|
if data != nil {
|
||||||
for key, value := range data.Annotations {
|
// Next, add annotations from the image
|
||||||
annotations[key] = value
|
for key, value := range data.Annotations {
|
||||||
|
annotations[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Last, add user annotations
|
// Last, add user annotations
|
||||||
for _, annotation := range c.StringSlice("annotation") {
|
for _, annotation := range c.StringSlice("annotation") {
|
||||||
@ -403,14 +435,14 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
workDir := "/"
|
workDir := "/"
|
||||||
if c.IsSet("workdir") || c.IsSet("w") {
|
if c.IsSet("workdir") || c.IsSet("w") {
|
||||||
workDir = c.String("workdir")
|
workDir = c.String("workdir")
|
||||||
} else if data.ContainerConfig.WorkingDir != "" {
|
} else if data != nil && data.ContainerConfig.WorkingDir != "" {
|
||||||
workDir = data.ContainerConfig.WorkingDir
|
workDir = data.ContainerConfig.WorkingDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// ENTRYPOINT
|
// ENTRYPOINT
|
||||||
// User input entrypoint takes priority over image entrypoint
|
// User input entrypoint takes priority over image entrypoint
|
||||||
entrypoint := c.StringSlice("entrypoint")
|
entrypoint := c.StringSlice("entrypoint")
|
||||||
if len(entrypoint) == 0 {
|
if len(entrypoint) == 0 && data != nil {
|
||||||
entrypoint = data.ContainerConfig.Entrypoint
|
entrypoint = data.ContainerConfig.Entrypoint
|
||||||
}
|
}
|
||||||
// if entrypoint=, we need to clear the entrypoint
|
// if entrypoint=, we need to clear the entrypoint
|
||||||
@ -425,7 +457,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
if len(inputCommand) > 0 {
|
if len(inputCommand) > 0 {
|
||||||
// User command overrides data CMD
|
// User command overrides data CMD
|
||||||
command = append(command, inputCommand...)
|
command = append(command, inputCommand...)
|
||||||
} else if len(data.ContainerConfig.Cmd) > 0 && !c.IsSet("entrypoint") {
|
} else if data != nil && len(data.ContainerConfig.Cmd) > 0 && !c.IsSet("entrypoint") {
|
||||||
// If not user command, add CMD
|
// If not user command, add CMD
|
||||||
command = append(command, data.ContainerConfig.Cmd...)
|
command = append(command, data.ContainerConfig.Cmd...)
|
||||||
}
|
}
|
||||||
@ -435,9 +467,12 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EXPOSED PORTS
|
// EXPOSED PORTS
|
||||||
portBindings, err := cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
|
var portBindings map[nat.Port][]nat.PortBinding
|
||||||
if err != nil {
|
if data != nil {
|
||||||
return nil, err
|
portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SHM Size
|
// SHM Size
|
||||||
@ -472,8 +507,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageVolumes := data.ContainerConfig.Volumes
|
|
||||||
|
|
||||||
|
var ImageVolumes map[string]struct{}
|
||||||
|
if data != nil {
|
||||||
|
ImageVolumes = data.ContainerConfig.Volumes
|
||||||
|
}
|
||||||
var imageVolType = map[string]string{
|
var imageVolType = map[string]string{
|
||||||
"bind": "",
|
"bind": "",
|
||||||
"tmpfs": "",
|
"tmpfs": "",
|
||||||
@ -567,6 +605,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
UsernsMode: usernsMode,
|
UsernsMode: usernsMode,
|
||||||
Volumes: c.StringSlice("volume"),
|
Volumes: c.StringSlice("volume"),
|
||||||
WorkDir: workDir,
|
WorkDir: workDir,
|
||||||
|
Rootfs: rootfs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.Privileged {
|
if !config.Privileged {
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||||
"github.com/projectatomic/libpod/libpod"
|
"github.com/projectatomic/libpod/libpod"
|
||||||
"github.com/projectatomic/libpod/libpod/image"
|
"github.com/projectatomic/libpod/libpod/image"
|
||||||
|
"github.com/projectatomic/libpod/pkg/inspect"
|
||||||
cc "github.com/projectatomic/libpod/pkg/spec"
|
cc "github.com/projectatomic/libpod/pkg/spec"
|
||||||
"github.com/projectatomic/libpod/pkg/util"
|
"github.com/projectatomic/libpod/pkg/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -66,25 +67,36 @@ func runCmd(c *cli.Context) error {
|
|||||||
return errors.Wrapf(err, "error creating libpod runtime")
|
return errors.Wrapf(err, "error creating libpod runtime")
|
||||||
}
|
}
|
||||||
defer runtime.Shutdown(false)
|
defer runtime.Shutdown(false)
|
||||||
|
|
||||||
if len(c.Args()) < 1 {
|
if len(c.Args()) < 1 {
|
||||||
return errors.Errorf("image name or ID is required")
|
return errors.Errorf("image name or ID is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := getContext()
|
rootfs := ""
|
||||||
rtc := runtime.GetConfig()
|
if c.Bool("rootfs") {
|
||||||
newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
|
rootfs = c.Args()[0]
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to find image")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := newImage.Inspect(ctx)
|
ctx := getContext()
|
||||||
if err != nil {
|
rtc := runtime.GetConfig()
|
||||||
return err
|
|
||||||
}
|
var newImage *image.Image = nil
|
||||||
if len(newImage.Names()) < 1 {
|
var data *inspect.ImageData = nil
|
||||||
imageName = newImage.ID()
|
if rootfs == "" {
|
||||||
} else {
|
newImage, err = runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false)
|
||||||
imageName = newImage.Names()[0]
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to find image")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err = newImage.Inspect(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(newImage.Names()) < 1 {
|
||||||
|
imageName = newImage.ID()
|
||||||
|
} else {
|
||||||
|
imageName = newImage.Names()[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
|
createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -112,6 +124,9 @@ func runCmd(c *cli.Context) error {
|
|||||||
options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
|
options = append(options, libpod.WithShmSize(createConfig.Resources.ShmSize))
|
||||||
options = append(options, libpod.WithGroups(createConfig.GroupAdd))
|
options = append(options, libpod.WithGroups(createConfig.GroupAdd))
|
||||||
options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
|
options = append(options, libpod.WithIDMappings(*createConfig.IDMappings))
|
||||||
|
if createConfig.Rootfs != "" {
|
||||||
|
options = append(options, libpod.WithRootFS(createConfig.Rootfs))
|
||||||
|
}
|
||||||
|
|
||||||
// Default used if not overridden on command line
|
// Default used if not overridden on command line
|
||||||
|
|
||||||
|
@ -1431,6 +1431,7 @@ _podman_container_run() {
|
|||||||
--pids-limit
|
--pids-limit
|
||||||
--publish -p
|
--publish -p
|
||||||
--runtime
|
--runtime
|
||||||
|
--rootfs
|
||||||
--security-opt
|
--security-opt
|
||||||
--shm-size
|
--shm-size
|
||||||
--stop-signal
|
--stop-signal
|
||||||
|
@ -470,6 +470,13 @@ its root filesystem mounted as read only prohibiting any writes.
|
|||||||
|
|
||||||
Automatically remove the container when it exits. The default is *false*.
|
Automatically remove the container when it exits. The default is *false*.
|
||||||
|
|
||||||
|
**--rootfs**
|
||||||
|
|
||||||
|
If specified, the first argument refers to an exploded container on the file system.
|
||||||
|
|
||||||
|
This is useful to run a container without requiring any image management, the rootfs
|
||||||
|
of the container is assumed to be managed externally.
|
||||||
|
|
||||||
**--security-opt**=[]
|
**--security-opt**=[]
|
||||||
|
|
||||||
Security Options
|
Security Options
|
||||||
|
@ -491,6 +491,13 @@ its root filesystem mounted as read only prohibiting any writes.
|
|||||||
|
|
||||||
Automatically remove the container when it exits. The default is *false*.
|
Automatically remove the container when it exits. The default is *false*.
|
||||||
|
|
||||||
|
**--rootfs**
|
||||||
|
|
||||||
|
If specified, the first argument refers to an exploded container on the file system.
|
||||||
|
|
||||||
|
This is useful to run a container without requiring any image management, the rootfs
|
||||||
|
of the container is assumed to be managed externally.
|
||||||
|
|
||||||
**--security-opt**=[]
|
**--security-opt**=[]
|
||||||
|
|
||||||
Security Options
|
Security Options
|
||||||
|
@ -197,6 +197,8 @@ type ContainerConfig struct {
|
|||||||
// Information on the image used for the root filesystem/
|
// Information on the image used for the root filesystem/
|
||||||
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
||||||
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
||||||
|
// Rootfs to use for the container, this conflicts with RootfsImageID
|
||||||
|
Rootfs string `json:"rootfs,omitempty"`
|
||||||
// Whether to mount volumes specified in the image.
|
// Whether to mount volumes specified in the image.
|
||||||
ImageVolumes bool `json:"imageVolumes"`
|
ImageVolumes bool `json:"imageVolumes"`
|
||||||
// Src path to be mounted on /dev/shm in container.
|
// Src path to be mounted on /dev/shm in container.
|
||||||
|
@ -34,6 +34,10 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
|
|||||||
isEnvCleared, isLabelCleared, isExposeCleared, isVolumeCleared bool
|
isEnvCleared, isLabelCleared, isExposeCleared, isVolumeCleared bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if c.config.Rootfs != "" {
|
||||||
|
return nil, errors.Errorf("cannot commit a container that uses an exploded rootfs")
|
||||||
|
}
|
||||||
|
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
@ -70,6 +70,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
|
|||||||
},
|
},
|
||||||
ImageID: config.RootfsImageID,
|
ImageID: config.RootfsImageID,
|
||||||
ImageName: config.RootfsImageName,
|
ImageName: config.RootfsImageName,
|
||||||
|
Rootfs: config.Rootfs,
|
||||||
ResolvConfPath: resolvPath,
|
ResolvConfPath: resolvPath,
|
||||||
HostnamePath: hostnamePath,
|
HostnamePath: hostnamePath,
|
||||||
HostsPath: hostsPath,
|
HostsPath: hostsPath,
|
||||||
|
@ -56,6 +56,10 @@ var (
|
|||||||
// mutable layer, and the rest is the RootFS: the set of immutable layers
|
// mutable layer, and the rest is the RootFS: the set of immutable layers
|
||||||
// that make up the image on which the container is based.
|
// that make up the image on which the container is based.
|
||||||
func (c *Container) rootFsSize() (int64, error) {
|
func (c *Container) rootFsSize() (int64, error) {
|
||||||
|
if c.config.Rootfs != "" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
container, err := c.runtime.store.Container(c.ID())
|
container, err := c.runtime.store.Container(c.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -93,6 +97,18 @@ func (c *Container) rootFsSize() (int64, error) {
|
|||||||
|
|
||||||
// rwSize Gets the size of the mutable top layer of the container.
|
// rwSize Gets the size of the mutable top layer of the container.
|
||||||
func (c *Container) rwSize() (int64, error) {
|
func (c *Container) rwSize() (int64, error) {
|
||||||
|
if c.config.Rootfs != "" {
|
||||||
|
var size int64
|
||||||
|
err := filepath.Walk(c.config.Rootfs, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size += info.Size()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
container, err := c.runtime.store.Container(c.ID())
|
container, err := c.runtime.store.Container(c.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -205,7 +221,7 @@ func (c *Container) setupStorage(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Need both an image ID and image name, plus a bool telling us whether to use the image configuration
|
// Need both an image ID and image name, plus a bool telling us whether to use the image configuration
|
||||||
if c.config.RootfsImageID == "" || c.config.RootfsImageName == "" {
|
if c.config.Rootfs == "" && (c.config.RootfsImageID == "" || c.config.RootfsImageName == "") {
|
||||||
return errors.Wrapf(ErrInvalidArg, "must provide image ID and image name to use an image")
|
return errors.Wrapf(ErrInvalidArg, "must provide image ID and image name to use an image")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,9 +707,12 @@ func (c *Container) mountStorage() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mountPoint, err := c.runtime.storageService.MountContainerImage(c.ID())
|
mountPoint := c.config.Rootfs
|
||||||
if err != nil {
|
if mountPoint == "" {
|
||||||
return errors.Wrapf(err, "error mounting storage for container %s", c.ID())
|
mountPoint, err = c.runtime.storageService.MountContainerImage(c.ID())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error mounting storage for container %s", c.ID())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.state.Mounted = true
|
c.state.Mounted = true
|
||||||
c.state.Mountpoint = mountPoint
|
c.state.Mountpoint = mountPoint
|
||||||
@ -796,6 +815,9 @@ func (c *Container) cleanupStorage() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.config.Rootfs != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Also unmount storage
|
// Also unmount storage
|
||||||
if err := c.runtime.storageService.UnmountContainerImage(c.ID()); err != nil {
|
if err := c.runtime.storageService.UnmountContainerImage(c.ID()); err != nil {
|
||||||
@ -1154,7 +1176,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||||||
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
|
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
|
||||||
}
|
}
|
||||||
// Bind builtin image volumes
|
// Bind builtin image volumes
|
||||||
if c.config.ImageVolumes {
|
if c.config.Rootfs == "" && c.config.ImageVolumes {
|
||||||
if err := c.addImageVolumes(ctx, &g); err != nil {
|
if err := c.addImageVolumes(ctx, &g); err != nil {
|
||||||
return nil, errors.Wrapf(err, "error mounting image volumes")
|
return nil, errors.Wrapf(err, "error mounting image volumes")
|
||||||
}
|
}
|
||||||
@ -1235,8 +1257,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil {
|
if c.config.Rootfs == "" {
|
||||||
return nil, err
|
if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.SetRootPath(c.state.RealMountpoint)
|
g.SetRootPath(c.state.RealMountpoint)
|
||||||
|
@ -2,6 +2,7 @@ package libpod
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -361,6 +362,9 @@ func WithRootFSFromImage(imageID string, imageName string, useImageVolumes bool)
|
|||||||
if ctr.config.RootfsImageID != "" || ctr.config.RootfsImageName != "" {
|
if ctr.config.RootfsImageID != "" || ctr.config.RootfsImageName != "" {
|
||||||
return errors.Wrapf(ErrInvalidArg, "container already configured with root filesystem")
|
return errors.Wrapf(ErrInvalidArg, "container already configured with root filesystem")
|
||||||
}
|
}
|
||||||
|
if ctr.config.Rootfs != "" {
|
||||||
|
return errors.Wrapf(ErrInvalidArg, "cannot set both an image ID and a rootfs for a container")
|
||||||
|
}
|
||||||
|
|
||||||
ctr.config.RootfsImageID = imageID
|
ctr.config.RootfsImageID = imageID
|
||||||
ctr.config.RootfsImageName = imageName
|
ctr.config.RootfsImageName = imageName
|
||||||
@ -909,6 +913,23 @@ func WithCommand(command []string) CtrCreateOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithRootFS sets the rootfs for the container
|
||||||
|
func WithRootFS(rootfs string) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return ErrCtrFinalized
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(rootfs); err != nil {
|
||||||
|
return errors.Wrapf(err, "error checking path %q", rootfs)
|
||||||
|
}
|
||||||
|
if ctr.config.RootfsImageID != "" {
|
||||||
|
return errors.Wrapf(ErrInvalidArg, "cannot set both an image ID and a rootfs for a container")
|
||||||
|
}
|
||||||
|
ctr.config.Rootfs = rootfs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pod Creation Options
|
// Pod Creation Options
|
||||||
|
|
||||||
// WithPodName sets the name of the pod.
|
// WithPodName sets the name of the pod.
|
||||||
|
@ -60,40 +60,40 @@ func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
|
|||||||
// CreateContainerStorage creates the storage end of things. We already have the container spec created
|
// CreateContainerStorage creates the storage end of things. We already have the container spec created
|
||||||
// TO-DO We should be passing in an Image object in the future.
|
// TO-DO We should be passing in an Image object in the future.
|
||||||
func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID, mountLabel string, options *storage.ContainerOptions) (ContainerInfo, error) {
|
func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID, mountLabel string, options *storage.ContainerOptions) (ContainerInfo, error) {
|
||||||
var ref types.ImageReference
|
var imageConfig *v1.Image
|
||||||
if imageName == "" && imageID == "" {
|
if imageName != "" {
|
||||||
return ContainerInfo{}, ErrEmptyID
|
var ref types.ImageReference
|
||||||
}
|
if containerName == "" {
|
||||||
if containerName == "" {
|
return ContainerInfo{}, ErrEmptyID
|
||||||
return ContainerInfo{}, ErrEmptyID
|
}
|
||||||
}
|
// Check if we have the specified image.
|
||||||
// Check if we have the specified image.
|
ref, err := istorage.Transport.ParseStoreReference(r.store, imageID)
|
||||||
ref, err := istorage.Transport.ParseStoreReference(r.store, imageID)
|
if err != nil {
|
||||||
if err != nil {
|
return ContainerInfo{}, err
|
||||||
return ContainerInfo{}, err
|
}
|
||||||
}
|
img, err := istorage.Transport.GetStoreImage(r.store, ref)
|
||||||
img, err := istorage.Transport.GetStoreImage(r.store, ref)
|
if err != nil {
|
||||||
if err != nil {
|
return ContainerInfo{}, err
|
||||||
return ContainerInfo{}, err
|
}
|
||||||
}
|
// Pull out a copy of the image's configuration.
|
||||||
// Pull out a copy of the image's configuration.
|
image, err := ref.NewImage(ctx, systemContext)
|
||||||
image, err := ref.NewImage(ctx, systemContext)
|
if err != nil {
|
||||||
if err != nil {
|
return ContainerInfo{}, err
|
||||||
return ContainerInfo{}, err
|
}
|
||||||
}
|
defer image.Close()
|
||||||
defer image.Close()
|
|
||||||
|
|
||||||
// Get OCI configuration of image
|
// Get OCI configuration of image
|
||||||
imageConfig, err := image.OCIConfig(ctx)
|
imageConfig, err = image.OCIConfig(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ContainerInfo{}, err
|
return ContainerInfo{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the image name and ID.
|
// Update the image name and ID.
|
||||||
if imageName == "" && len(img.Names) > 0 {
|
if imageName == "" && len(img.Names) > 0 {
|
||||||
imageName = img.Names[0]
|
imageName = img.Names[0]
|
||||||
|
}
|
||||||
|
imageID = img.ID
|
||||||
}
|
}
|
||||||
imageID = img.ID
|
|
||||||
|
|
||||||
// Build metadata to store with the container.
|
// Build metadata to store with the container.
|
||||||
metadata := RuntimeContainerMetadata{
|
metadata := RuntimeContainerMetadata{
|
||||||
@ -119,7 +119,7 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container, err := r.store.CreateContainer(containerID, names, img.ID, "", string(mdata), options)
|
container, err := r.store.CreateContainer(containerID, names, imageID, "", string(mdata), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
|
logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
|
||||||
|
|
||||||
|
@ -148,6 +148,7 @@ type ContainerInspectData struct {
|
|||||||
State *ContainerInspectState `json:"State"`
|
State *ContainerInspectState `json:"State"`
|
||||||
ImageID string `json:"Image"`
|
ImageID string `json:"Image"`
|
||||||
ImageName string `json:"ImageName"`
|
ImageName string `json:"ImageName"`
|
||||||
|
Rootfs string `json:"Rootfs"`
|
||||||
ResolvConfPath string `json:"ResolvConfPath"`
|
ResolvConfPath string `json:"ResolvConfPath"`
|
||||||
HostnamePath string `json:"HostnamePath"`
|
HostnamePath string `json:"HostnamePath"`
|
||||||
HostsPath string `json:"HostsPath"`
|
HostsPath string `json:"HostsPath"`
|
||||||
|
@ -130,6 +130,7 @@ type CreateConfig struct {
|
|||||||
ApparmorProfile string //SecurityOpts
|
ApparmorProfile string //SecurityOpts
|
||||||
SeccompProfilePath string //SecurityOpts
|
SeccompProfilePath string //SecurityOpts
|
||||||
SecurityOpts []string
|
SecurityOpts []string
|
||||||
|
Rootfs string
|
||||||
}
|
}
|
||||||
|
|
||||||
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
|
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
|
||||||
|
Reference in New Issue
Block a user