1
0
mirror of https://github.com/rancher/os.git synced 2025-09-01 06:40:31 +00:00

migrate to upstream libcompose in one and a half go

This commit is contained in:
Ivan Mikushin
2015-11-26 17:41:42 +05:00
parent 1d691cd8d6
commit 5a363ab97d
1291 changed files with 40107 additions and 123532 deletions

View File

@@ -5,9 +5,11 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
@@ -15,13 +17,25 @@ import (
"github.com/codegangsta/cli"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/specs"
)
var specCommand = cli.Command{
Name: "spec",
Usage: "create a new specification file",
Flags: []cli.Flag{
cli.StringFlag{
Name: "config-file, c",
Value: "config.json",
Usage: "path to spec config file for writing",
},
cli.StringFlag{
Name: "runtime-file, r",
Value: "runtime.json",
Usage: "path to runtime config file for writing",
},
},
Action: func(context *cli.Context) {
spec := specs.LinuxSpec{
Spec: specs.Spec{
@@ -46,55 +60,89 @@ var specCommand = cli.Command{
},
},
Hostname: "shell",
Mounts: []specs.Mount{
Mounts: []specs.MountPoint{
{
Type: "proc",
Source: "proc",
Destination: "/proc",
Options: "",
Name: "proc",
Path: "/proc",
},
{
Type: "tmpfs",
Source: "tmpfs",
Destination: "/dev",
Options: "nosuid,strictatime,mode=755,size=65536k",
Name: "dev",
Path: "/dev",
},
{
Type: "devpts",
Source: "devpts",
Destination: "/dev/pts",
Options: "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5",
Name: "devpts",
Path: "/dev/pts",
},
{
Type: "tmpfs",
Source: "shm",
Destination: "/dev/shm",
Options: "nosuid,noexec,nodev,mode=1777,size=65536k",
Name: "shm",
Path: "/dev/shm",
},
{
Type: "mqueue",
Source: "mqueue",
Destination: "/dev/mqueue",
Options: "nosuid,noexec,nodev",
Name: "mqueue",
Path: "/dev/mqueue",
},
{
Type: "sysfs",
Source: "sysfs",
Destination: "/sys",
Options: "nosuid,noexec,nodev",
Name: "sysfs",
Path: "/sys",
},
{
Type: "cgroup",
Source: "cgroup",
Destination: "/sys/fs/cgroup",
Options: "nosuid,noexec,nodev,relatime,ro",
Name: "cgroup",
Path: "/sys/fs/cgroup",
},
},
},
Linux: specs.Linux{
Capabilities: []string{
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE",
},
},
}
rspec := specs.LinuxRuntimeSpec{
RuntimeSpec: specs.RuntimeSpec{
Mounts: map[string]specs.Mount{
"proc": {
Type: "proc",
Source: "proc",
Options: nil,
},
"dev": {
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
"devpts": {
Type: "devpts",
Source: "devpts",
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
},
"shm": {
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
},
"mqueue": {
Type: "mqueue",
Source: "mqueue",
Options: []string{"nosuid", "noexec", "nodev"},
},
"sysfs": {
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev"},
},
"cgroup": {
Type: "cgroup",
Source: "cgroup",
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
},
},
},
Linux: specs.LinuxRuntime{
Namespaces: []specs.Namespace{
{
Type: "process",
Type: "pid",
},
{
Type: "network",
@@ -109,62 +157,168 @@ var specCommand = cli.Command{
Type: "mount",
},
},
Capabilities: []string{
"AUDIT_WRITE",
"KILL",
"NET_BIND_SERVICE",
Rlimits: []specs.Rlimit{
{
Type: "RLIMIT_NOFILE",
Hard: uint64(1024),
Soft: uint64(1024),
},
},
Devices: []string{
"null",
"random",
"full",
"tty",
"zero",
"urandom",
Devices: []specs.Device{
{
Type: 'c',
Path: "/dev/null",
Major: 1,
Minor: 3,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
{
Type: 'c',
Path: "/dev/random",
Major: 1,
Minor: 8,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
{
Type: 'c',
Path: "/dev/full",
Major: 1,
Minor: 7,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
{
Type: 'c',
Path: "/dev/tty",
Major: 5,
Minor: 0,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
{
Type: 'c',
Path: "/dev/zero",
Major: 1,
Minor: 5,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
{
Type: 'c',
Path: "/dev/urandom",
Major: 1,
Minor: 9,
Permissions: "rwm",
FileMode: 0666,
UID: 0,
GID: 0,
},
},
Resources: specs.Resources{
Resources: &specs.Resources{
Memory: specs.Memory{
Swappiness: -1,
},
},
Seccomp: specs.Seccomp{
DefaultAction: "SCMP_ACT_ALLOW",
Syscalls: []*specs.Syscall{},
},
},
}
checkNoFile := func(name string) error {
_, err := os.Stat(name)
if err == nil {
return fmt.Errorf("File %s exists. Remove it first", name)
}
if !os.IsNotExist(err) {
return err
}
return nil
}
cName := context.String("config-file")
rName := context.String("runtime-file")
if err := checkNoFile(cName); err != nil {
logrus.Fatal(err)
}
if err := checkNoFile(rName); err != nil {
logrus.Fatal(err)
}
data, err := json.MarshalIndent(&spec, "", "\t")
if err != nil {
logrus.Fatal(err)
}
fmt.Printf("%s", data)
if err := ioutil.WriteFile(cName, data, 0666); err != nil {
logrus.Fatal(err)
}
rdata, err := json.MarshalIndent(&rspec, "", "\t")
if err != nil {
logrus.Fatal(err)
}
if err := ioutil.WriteFile(rName, rdata, 0666); err != nil {
logrus.Fatal(err)
}
},
}
var namespaceMapping = map[string]configs.NamespaceType{
"process": configs.NEWPID,
"network": configs.NEWNET,
"mount": configs.NEWNS,
"user": configs.NEWUSER,
"ipc": configs.NEWIPC,
"uts": configs.NEWUTS,
var namespaceMapping = map[specs.NamespaceType]configs.NamespaceType{
specs.PIDNamespace: configs.NEWPID,
specs.NetworkNamespace: configs.NEWNET,
specs.MountNamespace: configs.NEWNS,
specs.UserNamespace: configs.NEWUSER,
specs.IPCNamespace: configs.NEWIPC,
specs.UTSNamespace: configs.NEWUTS,
}
var mountPropagationMapping = map[string]int{
"rprivate": syscall.MS_PRIVATE | syscall.MS_REC,
"private": syscall.MS_PRIVATE,
"rslave": syscall.MS_SLAVE | syscall.MS_REC,
"slave": syscall.MS_SLAVE,
"rshared": syscall.MS_SHARED | syscall.MS_REC,
"shared": syscall.MS_SHARED,
"": syscall.MS_PRIVATE | syscall.MS_REC,
}
// loadSpec loads the specification from the provided path.
// If the path is empty then the default path will be "config.json"
func loadSpec(path string) (*specs.LinuxSpec, error) {
if path == "" {
path = "config.json"
}
f, err := os.Open(path)
func loadSpec(cPath, rPath string) (spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, err error) {
cf, err := os.Open(cPath)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("JSON specification file for %s not found", path)
return nil, nil, fmt.Errorf("JSON specification file at %s not found", cPath)
}
return nil, err
return
}
defer f.Close()
var s *specs.LinuxSpec
if err := json.NewDecoder(f).Decode(&s); err != nil {
return nil, err
defer cf.Close()
rf, err := os.Open(rPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil, fmt.Errorf("JSON runtime config file at %s not found", rPath)
}
return
}
return s, checkSpecVersion(s)
defer rf.Close()
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
return
}
if err = json.NewDecoder(rf).Decode(&rspec); err != nil {
return
}
return spec, rspec, checkSpecVersion(spec)
}
// checkSpecVersion makes sure that the spec version matches runc's while we are in the initial
@@ -176,7 +330,7 @@ func checkSpecVersion(s *specs.LinuxSpec) error {
return nil
}
func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) {
func createLibcontainerConfig(cgroupName string, spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) (*configs.Config, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
@@ -190,9 +344,14 @@ func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) {
Capabilities: spec.Linux.Capabilities,
Readonlyfs: spec.Root.Readonly,
Hostname: spec.Hostname,
Privatefs: true,
}
for _, ns := range spec.Linux.Namespaces {
exists := false
if config.RootPropagation, exists = mountPropagationMapping[rspec.Linux.RootfsPropagation]; !exists {
return nil, fmt.Errorf("rootfsPropagation=%v is not supported", rspec.Linux.RootfsPropagation)
}
for _, ns := range rspec.Linux.Namespaces {
t, exists := namespaceMapping[ns.Type]
if !exists {
return nil, fmt.Errorf("namespace %q does not exist", ns)
@@ -206,16 +365,27 @@ func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) {
},
}
}
for _, m := range spec.Mounts {
config.Mounts = append(config.Mounts, createLibcontainerMount(cwd, m))
for _, mp := range spec.Mounts {
m, ok := rspec.Mounts[mp.Name]
if !ok {
return nil, fmt.Errorf("Mount with Name %q not found in runtime config", mp.Name)
}
config.Mounts = append(config.Mounts, createLibcontainerMount(cwd, mp.Path, m))
}
if err := createDevices(spec, config); err != nil {
if err := createDevices(rspec, config); err != nil {
return nil, err
}
if err := setupUserNamespace(spec, config); err != nil {
if err := setupUserNamespace(rspec, config); err != nil {
return nil, err
}
c, err := createCgroupConfig(spec, config.Devices)
for _, rlimit := range rspec.Linux.Rlimits {
rl, err := createLibContainerRlimit(rlimit)
if err != nil {
return nil, err
}
config.Rlimits = append(config.Rlimits, rl)
}
c, err := createCgroupConfig(cgroupName, rspec, config.Devices)
if err != nil {
return nil, err
}
@@ -229,12 +399,24 @@ func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) {
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
}
}
config.Sysctl = spec.Linux.Sysctl
seccomp, err := setupSeccomp(&rspec.Linux.Seccomp)
if err != nil {
return nil, err
}
config.Seccomp = seccomp
config.Sysctl = rspec.Linux.Sysctl
config.ProcessLabel = rspec.Linux.SelinuxProcessLabel
config.AppArmorProfile = rspec.Linux.ApparmorProfile
for _, g := range spec.Process.User.AdditionalGids {
config.AdditionalGroups = append(config.AdditionalGroups, strconv.FormatUint(uint64(g), 10))
}
createHooks(rspec, config)
config.Version = specs.Version
return config, nil
}
func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount {
flags, data := parseMountOptions(m.Options)
func createLibcontainerMount(cwd, dest string, m specs.Mount) *configs.Mount {
flags, pgflags, data := parseMountOptions(m.Options)
source := m.Source
if m.Type == "bind" {
if !filepath.IsAbs(source) {
@@ -242,21 +424,22 @@ func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount {
}
}
return &configs.Mount{
Device: m.Type,
Source: source,
Destination: m.Destination,
Data: data,
Flags: flags,
Device: m.Type,
Source: source,
Destination: dest,
Data: data,
Flags: flags,
PropagationFlags: pgflags,
}
}
func createCgroupConfig(spec *specs.LinuxSpec, devices []*configs.Device) (*configs.Cgroup, error) {
func createCgroupConfig(name string, spec *specs.LinuxRuntimeSpec, devices []*configs.Device) (*configs.Cgroup, error) {
myCgroupPath, err := cgroups.GetThisCgroupDir("devices")
if err != nil {
return nil, err
}
c := &configs.Cgroup{
Name: getDefaultID(),
Name: name,
Parent: myCgroupPath,
AllowedDevices: append(devices, allowedDevices...),
}
@@ -273,12 +456,28 @@ func createCgroupConfig(spec *specs.LinuxSpec, devices []*configs.Device) (*conf
c.CpuRtPeriod = r.CPU.RealtimePeriod
c.CpusetCpus = r.CPU.Cpus
c.CpusetMems = r.CPU.Mems
c.BlkioThrottleReadBpsDevice = r.BlockIO.ThrottleReadBpsDevice
c.BlkioThrottleWriteBpsDevice = r.BlockIO.ThrottleWriteBpsDevice
c.BlkioThrottleReadIOpsDevice = r.BlockIO.ThrottleReadIOpsDevice
c.BlkioThrottleWriteIOpsDevice = r.BlockIO.ThrottleWriteIOpsDevice
c.BlkioWeight = r.BlockIO.Weight
c.BlkioWeightDevice = r.BlockIO.WeightDevice
c.BlkioLeafWeight = r.BlockIO.LeafWeight
for _, wd := range r.BlockIO.WeightDevice {
weightDevice := configs.NewWeightDevice(wd.Major, wd.Minor, wd.Weight, wd.LeafWeight)
c.BlkioWeightDevice = append(c.BlkioWeightDevice, weightDevice)
}
for _, td := range r.BlockIO.ThrottleReadBpsDevice {
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
c.BlkioThrottleReadBpsDevice = append(c.BlkioThrottleReadBpsDevice, throttleDevice)
}
for _, td := range r.BlockIO.ThrottleWriteBpsDevice {
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
c.BlkioThrottleWriteBpsDevice = append(c.BlkioThrottleWriteBpsDevice, throttleDevice)
}
for _, td := range r.BlockIO.ThrottleReadIOPSDevice {
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
c.BlkioThrottleReadIOPSDevice = append(c.BlkioThrottleReadIOPSDevice, throttleDevice)
}
for _, td := range r.BlockIO.ThrottleWriteIOPSDevice {
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
c.BlkioThrottleWriteIOPSDevice = append(c.BlkioThrottleWriteIOPSDevice, throttleDevice)
}
for _, l := range r.HugepageLimits {
c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{
Pagesize: l.Pagesize,
@@ -296,13 +495,19 @@ func createCgroupConfig(spec *specs.LinuxSpec, devices []*configs.Device) (*conf
return c, nil
}
func createDevices(spec *specs.LinuxSpec, config *configs.Config) error {
for _, name := range spec.Linux.Devices {
d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm")
if err != nil {
return err
func createDevices(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
for _, d := range spec.Linux.Devices {
device := &configs.Device{
Type: d.Type,
Path: d.Path,
Major: d.Major,
Minor: d.Minor,
Permissions: d.Permissions,
FileMode: d.FileMode,
Uid: d.UID,
Gid: d.GID,
}
config.Devices = append(config.Devices, d)
config.Devices = append(config.Devices, device)
}
return nil
}
@@ -315,8 +520,8 @@ func setReadonly(config *configs.Config) {
}
}
func setupUserNamespace(spec *specs.LinuxSpec, config *configs.Config) error {
if len(spec.Linux.UidMappings) == 0 {
func setupUserNamespace(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
if len(spec.Linux.UIDMappings) == 0 {
return nil
}
config.Namespaces.Add(configs.NEWUSER, "")
@@ -327,33 +532,46 @@ func setupUserNamespace(spec *specs.LinuxSpec, config *configs.Config) error {
Size: int(m.Size),
}
}
for _, m := range spec.Linux.UidMappings {
for _, m := range spec.Linux.UIDMappings {
config.UidMappings = append(config.UidMappings, create(m))
}
for _, m := range spec.Linux.GidMappings {
for _, m := range spec.Linux.GIDMappings {
config.GidMappings = append(config.GidMappings, create(m))
}
rootUid, err := config.HostUID()
rootUID, err := config.HostUID()
if err != nil {
return err
}
rootGid, err := config.HostGID()
rootGID, err := config.HostGID()
if err != nil {
return err
}
for _, node := range config.Devices {
node.Uid = uint32(rootUid)
node.Gid = uint32(rootGid)
node.Uid = uint32(rootUID)
node.Gid = uint32(rootGID)
}
return nil
}
// parseMountOptions parses the string and returns the flags and any mount data that
// it contains.
func parseMountOptions(options string) (int, string) {
func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
rl, err := strToRlimit(rlimit.Type)
if err != nil {
return configs.Rlimit{}, err
}
return configs.Rlimit{
Type: rl,
Hard: uint64(rlimit.Hard),
Soft: uint64(rlimit.Soft),
}, nil
}
// parseMountOptions parses the string and returns the flags, propagation
// flags and any mount data that it contains.
func parseMountOptions(options []string) (int, []int, string) {
var (
flag int
data []string
flag int
pgflag []int
data []string
)
flags := map[string]struct {
clear bool
@@ -376,24 +594,29 @@ func parseMountOptions(options string) (int, string) {
"norelatime": {true, syscall.MS_RELATIME},
"nostrictatime": {true, syscall.MS_STRICTATIME},
"nosuid": {false, syscall.MS_NOSUID},
"private": {false, syscall.MS_PRIVATE},
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
"relatime": {false, syscall.MS_RELATIME},
"remount": {false, syscall.MS_REMOUNT},
"ro": {false, syscall.MS_RDONLY},
"rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC},
"rshared": {false, syscall.MS_SHARED | syscall.MS_REC},
"rslave": {false, syscall.MS_SLAVE | syscall.MS_REC},
"runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC},
"rw": {true, syscall.MS_RDONLY},
"shared": {false, syscall.MS_SHARED},
"slave": {false, syscall.MS_SLAVE},
"strictatime": {false, syscall.MS_STRICTATIME},
"suid": {true, syscall.MS_NOSUID},
"sync": {false, syscall.MS_SYNCHRONOUS},
"unbindable": {false, syscall.MS_UNBINDABLE},
}
for _, o := range strings.Split(options, ",") {
propagationFlags := map[string]struct {
clear bool
flag int
}{
"private": {false, syscall.MS_PRIVATE},
"shared": {false, syscall.MS_SHARED},
"slave": {false, syscall.MS_SLAVE},
"unbindable": {false, syscall.MS_UNBINDABLE},
"rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC},
"rshared": {false, syscall.MS_SHARED | syscall.MS_REC},
"rslave": {false, syscall.MS_SLAVE | syscall.MS_REC},
"runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC},
}
for _, o := range options {
// If the option does not exist in the flags table or the flag
// is not supported on the platform,
// then it is a data value for a specific fs type
@@ -403,9 +626,87 @@ func parseMountOptions(options string) (int, string) {
} else {
flag |= f.flag
}
} else if f, exists := propagationFlags[o]; exists && f.flag != 0 {
pgflag = append(pgflag, f.flag)
} else {
data = append(data, o)
}
}
return flag, strings.Join(data, ",")
return flag, pgflag, strings.Join(data, ",")
}
func setupSeccomp(config *specs.Seccomp) (*configs.Seccomp, error) {
if config == nil {
return nil, nil
}
// No default action specified, no syscalls listed, assume seccomp disabled
if config.DefaultAction == "" && len(config.Syscalls) == 0 {
return nil, nil
}
newConfig := new(configs.Seccomp)
newConfig.Syscalls = []*configs.Syscall{}
// Convert default action from string representation
newDefaultAction, err := seccomp.ConvertStringToAction(string(config.DefaultAction))
if err != nil {
return nil, err
}
newConfig.DefaultAction = newDefaultAction
// Loop through all syscall blocks and convert them to libcontainer format
for _, call := range config.Syscalls {
newAction, err := seccomp.ConvertStringToAction(string(call.Action))
if err != nil {
return nil, err
}
newCall := configs.Syscall{
Name: call.Name,
Action: newAction,
Args: []*configs.Arg{},
}
// Loop through all the arguments of the syscall and convert them
for _, arg := range call.Args {
newOp, err := seccomp.ConvertStringToOperator(string(arg.Op))
if err != nil {
return nil, err
}
newArg := configs.Arg{
Index: arg.Index,
Value: arg.Value,
ValueTwo: arg.ValueTwo,
Op: newOp,
}
newCall.Args = append(newCall.Args, &newArg)
}
newConfig.Syscalls = append(newConfig.Syscalls, &newCall)
}
return newConfig, nil
}
func createHooks(rspec *specs.LinuxRuntimeSpec, config *configs.Config) {
config.Hooks = &configs.Hooks{}
for _, h := range rspec.Hooks.Prestart {
cmd := configs.Command{
Path: h.Path,
Args: h.Args,
Env: h.Env,
}
config.Hooks.Prestart = append(config.Hooks.Prestart, configs.NewCommandHook(cmd))
}
for _, h := range rspec.Hooks.Poststop {
cmd := configs.Command{
Path: h.Path,
Args: h.Args,
Env: h.Env,
}
config.Hooks.Poststop = append(config.Hooks.Poststop, configs.NewCommandHook(cmd))
}
}