Skip to content

Commit

Permalink
Merge pull request #889 from jcrussell/issue-854
Browse files Browse the repository at this point in the history
minimega/vmconfiger: several bug fixes, fix #854
  • Loading branch information
djfritz authored May 12, 2017
2 parents 9914f2f + 84a57a1 commit 139ce34
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 161 deletions.
84 changes: 79 additions & 5 deletions src/minimega/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ type ContainerConfig struct {
//
// Note: this configuration only applies to containers.
Fifos uint64

// Attach one or more volumes to a container. These directories will be
// mounted inside the container at the specified location.
//
// For example, to mount /scratch/data to /data inside the container:
//
// vm config volume /data /scratch/data
//
// Commands with the same <key> will overwrite previous volumes:
//
// vm config volume /data /scratch/data2
// vm config volume /data
// /scratch/data2
//
// Note: this configuration only applies to containers.
VolumePaths map[string]string
}

type ContainerVM struct {
Expand Down Expand Up @@ -372,8 +388,8 @@ func containerTeardown() {
// 7 : uuid
// 8 : number of fifos
// 9 : preinit program
// 10 : init program (relative to filesystem path)
// 11: init args
// 10+: source:target volumes, `--` signifies end
// 11+ : init program and args (relative to filesystem path)
func containerShim() {
args := flag.Args()
if flag.NArg() < 11 { // 11 because init args can be nil
Expand Down Expand Up @@ -411,7 +427,19 @@ func containerShim() {
log.Fatalln(err)
}
vmPreinit := args[9]
vmInit := args[10:]

// find `--` separator between vmVolumes and vmInit
var vmVolumes, vmInit []string
for i, v := range args[10:] {
if v == "--" {
vmInit = args[10+i+1:]
break
}
vmVolumes = append(vmVolumes, v)
}

log.Debug("vmVolumes: %v", vmVolumes)
log.Debug("vmInit: %v", vmInit)

// set hostname
log.Debug("vm %v hostname", vmID)
Expand All @@ -435,6 +463,13 @@ func containerShim() {
log.Fatal("containerMountDefaults: %v", err)
}

// mount volumes
log.Debug("vm %v containerMountVolumes", vmID)
err = containerMountVolumes(vmFSPath, vmVolumes)
if err != nil {
log.Fatal("containerMountVolumes: %v", err)
}

// mknod
log.Debug("vm %v containerMknodDevices", vmID)
err = containerMknodDevices(vmFSPath)
Expand Down Expand Up @@ -590,8 +625,11 @@ func (old ContainerConfig) Copy() ContainerConfig {
// Copy all fields
res := old

// Make deep copy of slices
// none yet - placeholder
// Make deep copy of volumes
res.VolumePaths = map[string]string{}
for k, v := range old.VolumePaths {
res.VolumePaths[k] = v
}

return res
}
Expand Down Expand Up @@ -717,6 +755,8 @@ func (vm *ContainerVM) Info(field string) (string, error) {
switch field {
case "console_port":
return strconv.Itoa(vm.ConsolePort), nil
case "volume":
return marshal(vm.VolumePaths), nil
}

return vm.ContainerConfig.Info(field)
Expand Down Expand Up @@ -762,6 +802,10 @@ func (vm *ContainerConfig) String() string {
fmt.Fprintf(w, "Init:\t%v\n", vm.Init)
fmt.Fprintf(w, "Pre-init:\t%v\n", vm.Preinit)
fmt.Fprintf(w, "FIFOs:\t%v\n", vm.Fifos)
fmt.Fprintf(w, "Volumes:\t\n")
for k, v := range vm.VolumePaths {
fmt.Fprintf(w, "\t%v -> %v\n", k, v)
}
w.Flush()
fmt.Fprintln(&o)
return o.String()
Expand Down Expand Up @@ -881,6 +925,13 @@ func (vm *ContainerVM) launch() error {
fmt.Sprintf("%v", vm.Fifos),
preinit,
}
for k, v := range vm.VolumePaths {
// Create source:target pairs
// TODO: should probably handle spaces
args = append(args, fmt.Sprintf("%v:%v", v, k))
}
// denotes end of volumes
args = append(args, "--")
args = append(args, vm.Init...)

// launch the container
Expand Down Expand Up @@ -1549,6 +1600,28 @@ func containerMountDefaults(fsPath string) error {
return nil
}

func containerMountVolumes(fsPath string, volumes []string) error {
for _, v := range volumes {
f := strings.Split(v, ":")
if len(f) != 2 {
return fmt.Errorf("invalid volume, expected `source:target`: %v", v)
}

source := f[0]
target := filepath.Join(fsPath, f[1])

if err := os.MkdirAll(target, 0755); err != nil {
return err
}

if err := syscall.Mount(source, target, "", syscall.MS_BIND, ""); err != nil {
return err
}
}

return nil
}

// aggressively cleanup container cruff, called by the nuke api
func containerNuke() {
// walk minimega cgroups for tasks, killing each one
Expand Down Expand Up @@ -1653,6 +1726,7 @@ func containerCleanCgroupDirs() {
filepath.Join(*f_cgroup, "freezer", "minimega"),
filepath.Join(*f_cgroup, "memory", "minimega"),
filepath.Join(*f_cgroup, "devices", "minimega"),
filepath.Join(*f_cgroup, "cpu", "minimega"),
}
for _, d := range paths {
_, err := os.Stat(d)
Expand Down
13 changes: 13 additions & 0 deletions src/minimega/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package main

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"image"
Expand Down Expand Up @@ -294,6 +295,18 @@ func mustWrite(fpath, data string) {
}
}

// marshal returns the JSON-marshaled version of `v`. If we are unable to
// marshal it for whatever reason, we log an error and return an empty string.
func marshal(v interface{}) string {
b, err := json.Marshal(v)
if err != nil {
log.Error("unable to marshal %v: %v", v, err)
return ""
}

return string(b)
}

// PermStrings creates a random permutation of the source slice using the
// "inside-out" version of the Fisher-Yates algorithm.
func PermStrings(source []string) []string {
Expand Down
5 changes: 3 additions & 2 deletions src/minimega/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ var vmInfo = []string{
"vcpus", "disk", "snapshot", "initrd", "kernel", "cdrom", "migrate",
"append", "serial-ports", "virtio-ports", "vnc_port",
// container fields
"filesystem", "hostname", "init", "preinit", "fifo", "console_port",
"filesystem", "hostname", "init", "preinit", "fifo", "volume",
"console_port",
// more generic fields (tags can be huge so throw it at the end)
"tags",
}
Expand Down Expand Up @@ -630,7 +631,7 @@ func (vm *BaseVM) Info(field string) (string, error) {
}
}
case "tags":
return vm.Tags.String(), nil
return marshal(vm.Tags), nil
case "cc_active":
return strconv.FormatBool(vm.ActiveCC), nil
default:
Expand Down
21 changes: 4 additions & 17 deletions src/minimega/vmconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@ package main
import (
"bridge"
"bytes"
"encoding/json"
"fmt"
log "minilog"
"strings"
"text/tabwriter"
)

type Tags map[string]string

// VMConfig contains all the configs possible for a VM. When a VM of a
// particular kind is launched, only the pertinent configuration is copied so
// fields from other configs will have the zero value for the field type.
Expand Down Expand Up @@ -66,8 +62,9 @@ type BaseConfig struct {
// Networks for the VM, handler is not generated by vmconfiger.
Networks []NetConfig

// Tags for the VM, handler is not generated by vmconfiger.
Tags Tags
// Set tags in the same manner as "vm tag". These tags will apply to all
// newly launched VMs.
Tags map[string]string
}

// NetConfig contains all the network-related config for an interface. The IP
Expand Down Expand Up @@ -141,7 +138,7 @@ func (vm *BaseConfig) String() string {
fmt.Fprintf(w, "Schedule host:\t%v\n", vm.Schedule)
fmt.Fprintf(w, "Coschedule limit:\t%v\n", vm.Coschedule)
if vm.Tags != nil {
fmt.Fprintf(w, "Tags:\t%v\n", vm.Tags)
fmt.Fprintf(w, "Tags:\t%v\n", marshal(vm.Tags))
} else {
fmt.Fprint(w, "Tags:\t{}\n")
}
Expand Down Expand Up @@ -186,16 +183,6 @@ func (vm *BaseConfig) QosString(b, t, i string) string {
return strings.Trim(val, " ")
}

func (t Tags) String() string {
res, err := json.Marshal(t)
if err != nil {
log.Error("unable to marshal Tags: %v", err)
return ""
}

return string(res)
}

// TODO: Handle if there are spaces or commas in the tap/bridge names
func (net NetConfig) String() (s string) {
parts := []string{}
Expand Down
33 changes: 0 additions & 33 deletions src/minimega/vmconfig_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,6 @@ Calling vm config net with no arguments prints the current configuration.`,
},
Call: wrapSimpleCLI(cliVMConfigNet),
},
{ // vm config tag
HelpShort: "set tags for newly launched VMs",
HelpLong: `
Set tags in the same manner as "vm tag". These tags will apply to all newly
launched VMs.`,
Patterns: []string{
"vm config tag [key]",
"vm config tag <key> <value>",
},
Call: wrapSimpleCLI(cliVMConfigTag),
},
{ // vm config qemu-override
HelpShort: "override parts of the QEMU launch string",
HelpLong: `
Expand Down Expand Up @@ -216,28 +205,6 @@ func cliVMConfigNet(c *minicli.Command, resp *minicli.Response) error {
return nil
}

func cliVMConfigTag(c *minicli.Command, resp *minicli.Response) error {
k := c.StringArgs["key"]

// if Tags were cleared, reinitialize them
if vmConfig.Tags == nil {
vmConfig.Tags = map[string]string{}
}

if v, ok := c.StringArgs["value"]; ok {
// Setting a new value
vmConfig.Tags[k] = v
} else if k != "" {
// Printing a single tag
resp.Response = vmConfig.Tags[k]
} else {
// Printing all configured tags
resp.Response = vmConfig.Tags.String()
}

return nil
}

func cliVMConfigQemuOverride(c *minicli.Command, resp *minicli.Response) error {
if len(c.StringArgs) == 0 {
resp.Response = vmConfig.qemuOverrideString()
Expand Down
Loading

0 comments on commit 139ce34

Please sign in to comment.