summaryrefslogtreecommitdiff
path: root/runtime/execdriver/native/configuration/parse.go
blob: 6d6c6439192b2d7ebbefea8f90db6f7c8c0d2b74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package configuration

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/utils"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

type Action func(*libcontainer.Container, interface{}, string) error

var actions = map[string]Action{
	"cap.add":  addCap,  // add a cap
	"cap.drop": dropCap, // drop a cap

	"ns.add":  addNamespace,  // add a namespace
	"ns.drop": dropNamespace, // drop a namespace when cloning

	"net.join": joinNetNamespace, // join another containers net namespace

	"cgroups.cpu_shares":  cpuShares,  // set the cpu shares
	"cgroups.memory":      memory,     // set the memory limit
	"cgroups.memory_swap": memorySwap, // set the memory swap limit
	"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used

	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply

	"fs.readonly": readonlyFs, // make the rootfs of the container read only
}

func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error {
	if container.Cgroups == nil {
		return fmt.Errorf("cannot set cgroups when they are disabled")
	}
	container.Cgroups.CpusetCpus = value

	return nil
}

func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
	container.Context["apparmor_profile"] = value
	return nil
}

func cpuShares(container *libcontainer.Container, context interface{}, value string) error {
	if container.Cgroups == nil {
		return fmt.Errorf("cannot set cgroups when they are disabled")
	}
	v, err := strconv.ParseInt(value, 10, 0)
	if err != nil {
		return err
	}
	container.Cgroups.CpuShares = v
	return nil
}

func memory(container *libcontainer.Container, context interface{}, value string) error {
	if container.Cgroups == nil {
		return fmt.Errorf("cannot set cgroups when they are disabled")
	}

	v, err := utils.RAMInBytes(value)
	if err != nil {
		return err
	}
	container.Cgroups.Memory = v
	return nil
}

func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
	if container.Cgroups == nil {
		return fmt.Errorf("cannot set cgroups when they are disabled")
	}
	v, err := strconv.ParseInt(value, 0, 64)
	if err != nil {
		return err
	}
	container.Cgroups.MemorySwap = v
	return nil
}

func addCap(container *libcontainer.Container, context interface{}, value string) error {
	c := container.CapabilitiesMask.Get(value)
	if c == nil {
		return fmt.Errorf("%s is not a valid capability", value)
	}
	c.Enabled = true
	return nil
}

func dropCap(container *libcontainer.Container, context interface{}, value string) error {
	c := container.CapabilitiesMask.Get(value)
	if c == nil {
		return fmt.Errorf("%s is not a valid capability", value)
	}
	c.Enabled = false
	return nil
}

func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
	ns := container.Namespaces.Get(value)
	if ns == nil {
		return fmt.Errorf("%s is not a valid namespace", value[1:])
	}
	ns.Enabled = true
	return nil
}

func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
	ns := container.Namespaces.Get(value)
	if ns == nil {
		return fmt.Errorf("%s is not a valid namespace", value[1:])
	}
	ns.Enabled = false
	return nil
}

func readonlyFs(container *libcontainer.Container, context interface{}, value string) error {
	switch value {
	case "1", "true":
		container.ReadonlyFs = true
	default:
		container.ReadonlyFs = false
	}
	return nil
}

func joinNetNamespace(container *libcontainer.Container, context interface{}, value string) error {
	var (
		running = context.(map[string]*exec.Cmd)
		cmd     = running[value]
	)

	if cmd == nil || cmd.Process == nil {
		return fmt.Errorf("%s is not a valid running container to join", value)
	}
	nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
	container.Networks = append(container.Networks, &libcontainer.Network{
		Type: "netns",
		Context: libcontainer.Context{
			"nspath": nspath,
		},
	})
	return nil
}

func vethMacAddress(container *libcontainer.Container, context interface{}, value string) error {
	var veth *libcontainer.Network
	for _, network := range container.Networks {
		if network.Type == "veth" {
			veth = network
			break
		}
	}
	if veth == nil {
		return fmt.Errorf("not veth configured for container")
	}
	veth.Context["mac"] = value
	return nil
}

// configureCustomOptions takes string commands from the user and allows modification of the
// container's default configuration.
//
// TODO: this can be moved to a general utils or parser in pkg
func ParseConfiguration(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error {
	for _, opt := range opts {
		kv := strings.SplitN(opt, "=", 2)
		if len(kv) < 2 {
			return fmt.Errorf("invalid format for %s", opt)
		}

		action, exists := actions[kv[0]]
		if !exists {
			return fmt.Errorf("%s is not a valid option for the native driver", kv[0])
		}

		if err := action(container, running, kv[1]); err != nil {
			return err
		}
	}
	return nil
}