Skip to content

Commit a6d6228

Browse files
authored
Merge pull request #279 from Mashimiao/generate-add-mounts-add-option
generate: support adding additional mounts for container
2 parents 5edfd7f + 58374ae commit a6d6228

4 files changed

Lines changed: 40 additions & 148 deletions

File tree

cmd/oci-runtime-tool/generate.go

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ var generateFlags = []cli.Flag{
7777
cli.StringFlag{Name: "linux-selinux-label", Usage: "process selinux label"},
7878
cli.StringSliceFlag{Name: "linux-sysctl", Usage: "add sysctl settings e.g net.ipv4.forward=1"},
7979
cli.StringSliceFlag{Name: "linux-uidmappings", Usage: "add UIDMappings e.g HostID:ContainerID:Size"},
80-
cli.StringSliceFlag{Name: "mount-bind", Usage: "bind mount directories src:dest[:options...]"},
81-
cli.StringFlag{Name: "mount-cgroups", Value: "no", Usage: "mount cgroups (rw,ro,no)"},
80+
cli.StringSliceFlag{Name: "mounts-add", Usage: "configures additional mounts inside container"},
81+
cli.BoolFlag{Name: "mounts-remove-all", Usage: "remove all mounts inside container"},
8282
cli.StringFlag{Name: "output", Usage: "output file (defaults to stdout)"},
8383
cli.BoolFlag{Name: "privileged", Usage: "enable privileged container settings"},
8484
cli.StringSliceFlag{Name: "process-cap-add-ambient", Usage: "add Linux ambient capabilities"},
@@ -105,7 +105,6 @@ var generateFlags = []cli.Flag{
105105
cli.StringFlag{Name: "rootfs-path", Value: "rootfs", Usage: "path to the root filesystem"},
106106
cli.BoolFlag{Name: "rootfs-readonly", Usage: "make the container's rootfs readonly"},
107107
cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"},
108-
cli.StringSliceFlag{Name: "tmpfs", Usage: "mount tmpfs e.g. ContainerDIR[:OPTIONS...]"},
109108
}
110109

111110
var generateCommand = cli.Command{
@@ -412,30 +411,17 @@ func setupSpec(g *generate.Generator, context *cli.Context) error {
412411
g.AddOrReplaceLinuxNamespace("user", "")
413412
}
414413

415-
if context.IsSet("tmpfs") {
416-
tmpfsSlice := context.StringSlice("tmpfs")
417-
for _, s := range tmpfsSlice {
418-
dest, options, err := parseTmpfsMount(s)
419-
if err != nil {
420-
return err
421-
}
422-
g.AddTmpfsMount(dest, options)
423-
}
424-
}
425-
426-
mountCgroupOption := context.String("mount-cgroups")
427-
if err := g.AddCgroupsMount(mountCgroupOption); err != nil {
428-
return err
414+
if context.IsSet("mounts-remove-all") {
415+
g.ClearMounts()
429416
}
430417

431-
if context.IsSet("mount-bind") {
432-
binds := context.StringSlice("mount-bind")
433-
for _, bind := range binds {
434-
source, dest, options, err := parseBindMount(bind)
418+
if context.IsSet("mounts-add") {
419+
mounts := context.StringSlice("mounts-add")
420+
for _, mount := range mounts {
421+
err := g.AddMounts(mount)
435422
if err != nil {
436423
return err
437424
}
438-
g.AddBindMount(source, dest, options)
439425
}
440426
}
441427

@@ -759,45 +745,6 @@ func parseNetworkPriority(np string) (string, int32, error) {
759745
return parts[0], int32(priority), nil
760746
}
761747

762-
func parseTmpfsMount(s string) (string, []string, error) {
763-
var dest string
764-
var options []string
765-
var err error
766-
767-
parts := strings.Split(s, ":")
768-
if len(parts) == 2 && parts[0] != "" {
769-
dest = parts[0]
770-
options = strings.Split(parts[1], ",")
771-
} else if len(parts) == 1 {
772-
dest = parts[0]
773-
options = []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
774-
} else {
775-
err = fmt.Errorf("invalid -- tmpfs value: %s", s)
776-
}
777-
778-
return dest, options, err
779-
}
780-
781-
func parseBindMount(s string) (string, string, []string, error) {
782-
var source, dest string
783-
options := []string{}
784-
785-
bparts := strings.SplitN(s, ":", 3)
786-
switch len(bparts) {
787-
case 2:
788-
source, dest = bparts[0], bparts[1]
789-
case 3:
790-
source, dest, options = bparts[0], bparts[1], strings.Split(bparts[2], ":")
791-
default:
792-
return source, dest, options, fmt.Errorf("--mount-bind should have format src:dest[:options...]")
793-
}
794-
795-
if source == "" || dest == "" {
796-
return source, dest, options, fmt.Errorf("--mount-bind should have format src:dest[:options...]")
797-
}
798-
return source, dest, options, nil
799-
}
800-
801748
func parseRlimit(rlimit string) (string, uint64, uint64, error) {
802749
parts := strings.Split(rlimit, ":")
803750
if len(parts) != 3 || parts[0] == "" {

completions/bash/oci-runtime-tool

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,7 @@ _oci-runtime-tool_generate() {
354354
--linux-selinux-label
355355
--linux-sysctl
356356
--linux-uidmappings
357-
--mount-bind
358-
--mount-cgroups
357+
--mounts-add
359358
--output
360359
--process-cap-add-ambient
361360
--process-cap-add-bounding
@@ -376,7 +375,6 @@ _oci-runtime-tool_generate() {
376375
--process-uid
377376
--rootfs-path
378377
--template
379-
--tmpfs
380378
"
381379

382380
local boolean_options="
@@ -389,6 +387,7 @@ _oci-runtime-tool_generate() {
389387
--linux-namespace-remove-all
390388
--linux-seccomp-only
391389
--linux-seccomp-remove-all
390+
--mounts-remove-all
392391
--privileged
393392
--process-cap-drop-all
394393
--process-no-new-privileges
@@ -433,11 +432,6 @@ _oci-runtime-tool_generate() {
433432
return
434433
;;
435434

436-
--mount-cgroups)
437-
COMPREPLY=( $( compgen -W "no ro rw" -- "$cur" ) )
438-
return
439-
;;
440-
441435
--process-cap-add-ambient|--process-cap-add-bounding|--process-cap-add-effective|--process-cap-add-inheritable|--process-cap-add-permitted|--process-cap-drop-ambient|--process-cap-drop-bounding|--process-cap-drop-effective|--process-cap-drop-inheritable|--process-cap-drop-permitted)
442436
__oci-runtime-tool_complete_capabilities
443437
return
@@ -459,7 +453,7 @@ _oci-runtime-tool_generate() {
459453
return
460454
;;
461455

462-
--rootfs-path|--tmpfs|--mount-bind|--process-cwd)
456+
--rootfs-path|--process-cwd)
463457
case "$cur" in
464458
*:*)
465459
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)

generate/generate.go

Lines changed: 11 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -812,69 +812,26 @@ func (g *Generator) AddPostStartHook(hookObject string) error {
812812
return nil
813813
}
814814

815-
// AddTmpfsMount adds a tmpfs mount into g.spec.Mounts.
816-
func (g *Generator) AddTmpfsMount(dest string, options []string) {
817-
mnt := rspec.Mount{
818-
Destination: dest,
819-
Type: "tmpfs",
820-
Source: "tmpfs",
821-
Options: options,
822-
}
823-
815+
// AddMounts adds a mount into g.spec.Mounts.
816+
func (g *Generator) AddMounts(mountObject string) error {
824817
g.initSpec()
825-
g.spec.Mounts = append(g.spec.Mounts, mnt)
826-
}
827818

828-
// AddCgroupsMount adds a cgroup mount into g.spec.Mounts.
829-
func (g *Generator) AddCgroupsMount(mountCgroupOption string) error {
830-
switch mountCgroupOption {
831-
case "ro":
832-
case "rw":
833-
case "no":
834-
return nil
835-
default:
836-
return fmt.Errorf("--mount-cgroups should be one of (ro,rw,no)")
837-
}
838-
839-
mnt := rspec.Mount{
840-
Destination: "/sys/fs/cgroup",
841-
Type: "cgroup",
842-
Source: "cgroup",
843-
Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption},
819+
mnt := rspec.Mount{}
820+
err := json.Unmarshal([]byte(mountObject), &mnt)
821+
if err != nil {
822+
return err
844823
}
845-
g.initSpec()
846824
g.spec.Mounts = append(g.spec.Mounts, mnt)
847825

848826
return nil
849827
}
850828

851-
// AddBindMount adds a bind mount into g.spec.Mounts.
852-
func (g *Generator) AddBindMount(source, dest string, options []string) {
853-
if len(options) == 0 {
854-
options = []string{"rw"}
855-
}
856-
857-
// We have to make sure that there is a bind option set, otherwise it won't
858-
// be an actual bindmount.
859-
foundBindOption := false
860-
for _, opt := range options {
861-
if opt == "bind" || opt == "rbind" {
862-
foundBindOption = true
863-
break
864-
}
865-
}
866-
if !foundBindOption {
867-
options = append(options, "bind")
868-
}
869-
870-
mnt := rspec.Mount{
871-
Destination: dest,
872-
Type: "bind",
873-
Source: source,
874-
Options: options,
829+
// ClearMounts clear g.spec.Mounts
830+
func (g *Generator) ClearMounts() {
831+
if g.spec == nil {
832+
return
875833
}
876-
g.initSpec()
877-
g.spec.Mounts = append(g.spec.Mounts, mnt)
834+
g.spec.Mounts = []rspec.Mount{}
878835
}
879836

880837
// SetupPrivileged sets up the privilege-related fields inside g.spec.

man/oci-runtime-tool-generate.1.md

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -289,18 +289,20 @@ read the configuration from `config.json`.
289289

290290
Add UIDMappings e.g HostUID:ContainerID:Size. Implies **--user=**.
291291

292-
**--mount-bind**=*[[HOST-DIR:CONTAINER-DIR][:OPTIONS...]]*
293-
Bind mount directories src:dest:(rw,ro) If you specify, ` --mount-bind
294-
/HOST-DIR:/CONTAINER-DIR`, runc bind mounts `/HOST-DIR` in the host
295-
to `/CONTAINER-DIR` in the OCI container. The `OPTIONS` are a colon
296-
delimited list and can be any mount option support by the runtime such
297-
as [rw|ro|rbind|bind|...]. The `HOST_DIR` and `CONTAINER-DIR` must be
298-
absolute paths such as `/src/docs`. You can set the `ro` or `rw`
299-
options to a bind-mount to mount it read-only or read-write mode,
300-
respectively. By default, bind-mounts are mounted read-write.
301-
302-
**--mount-cgroups**=[rw|ro|no]
303-
Mount cgroups. The default is *no*.
292+
**--mounts-add**=[]
293+
Configures additional mounts inside container.
294+
This option can be specified multiple times.
295+
For example,
296+
A. Add tmpfs into container.
297+
--mounts-add '{"destination": "/tmp","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}'
298+
B. Bind host directory into containeri.
299+
--mounts-add '{"destination": "/data","type": "bind","source": "/volumes/testing","options": ["rbind","rw"]}'
300+
C. mount for windows platform
301+
--mount-add '{"destination": "C:\\folder-inside-container","source": "C:\\folder-on-host","options": ["ro"]}'
302+
303+
**--mounts-remove-all**=true|false
304+
Remove all mounts inside the container. The default is *false*.
305+
When specified with --mount-add, this option will be parsed first.
304306

305307
**--output**=PATH
306308
Instead of writing the configuration JSON to stdout, write it to a
@@ -398,14 +400,6 @@ read the configuration from `config.json`.
398400
Additional options will only adjust the relevant portions of your template.
399401
Templates are not validated for correctness, so the user should ensure that they are correct.
400402

401-
**--tmpfs**=[] Create a tmpfs mount
402-
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
403-
404-
$ oci-runtime-tool generate -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
405-
406-
This command mounts a `tmpfs` at `/tmp` within the container. The supported mount options are the same as the Linux default `mount` flags. If you do not specify any options, the systems uses the following options:
407-
`rw,noexec,nosuid,nodev,size=65536k`.
408-
409403
# EXAMPLES
410404

411405
## Generating container in read-only mode
@@ -419,14 +413,14 @@ This protects the containers image from modification. Read only containers may
419413
still need to write temporary data. The best way to handle this is to mount
420414
tmpfs directories on /generate and /tmp.
421415

422-
$ oci-runtime-tool generate --rootfs-readonly --tmpfs /generate --tmpfs /tmp --tmpfs /run --rootfs-path /var/lib/containers/fedora --args bash
416+
$ oci-runtime-tool generate --rootfs-readonly --mounts-add '{"destination": "/tmp","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}' --mounts-add '{"destination": "/run","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}' --rootfs-path /var/lib/containers/fedora --args bash
423417

424418
## Exposing log messages from the container to the host's log
425419

426420
If you want messages that are logged in your container to show up in the host's
427421
syslog/journal then you should bind mount the /dev/log directory as follows.
428422

429-
$ oci-runtime-tool generate --mount-bind /dev/log:/dev/log --rootfs-path /var/lib/containers/fedora --args bash
423+
$ oci-runtime-tool generate --mounts-add '{"destination": "/dev/log","type": "bind","source": "/dev/log","options": ["rbind","rw"]}' --rootfs-path /var/lib/containers/fedora --args bash
430424

431425
From inside the container you can test this by sending a message to the log.
432426

@@ -446,13 +440,13 @@ To mount a host directory as a container volume, specify the absolute path to
446440
the directory and the absolute path for the container directory separated by a
447441
colon:
448442

449-
$ oci-runtime-tool generate --mount-bind /var/db:/data1 --rootfs-path /var/lib/containers/fedora --args bash
443+
$ oci-runtime-tool generate --mounts-add '{"destination": "/var/db","type": "bind","source": "/data1","options": ["rbind","rw"]}' --rootfs-path /var/lib/containers/fedora --args bash
450444

451445
## Using SELinux
452446

453447
You can use SELinux to add security to the container. You must specify the process label to run the init process inside of the container using `--linux-selinux-label`.
454448

455-
$ oci-runtime-tool generate --mount-bind /var/db:/data1 --linux-selinux-label system_u:system_r:svirt_lxc_net_t:s0:c1,c2 --linux-mount-label system_u:object_r:svirt_sandbo x_file_t:s0:c1,c2 --rootfs-path /var/lib/containers/fedora --args bash
449+
$ oci-runtime-tool generate --mounts-add '{"destination": "/var/db","type": "bind","source": "/data1","options": ["rbind","rw"]}' --linux-selinux-label system_u:system_r:svirt_lxc_net_t:s0:c1,c2 --linux-mount-label system_u:object_r:svirt_sandbo x_file_t:s0:c1,c2 --rootfs-path /var/lib/containers/fedora --args bash
456450

457451
Not in the above example we used a type of svirt_lxc_net_t and an MCS Label of s0:c1,c2. If you want to guarantee separation between containers, you need to make sure that each container gets launched with a different MCS Label pair.
458452

0 commit comments

Comments
 (0)