| 3 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,135 +0,0 @@ |
| 1 |
-package execdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "io" |
|
| 6 |
- "os" |
|
| 7 |
- "os/exec" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-var ( |
|
| 11 |
- ErrNotRunning = errors.New("Process could not be started")
|
|
| 12 |
- ErrWaitTimeoutReached = errors.New("Wait timeout reached")
|
|
| 13 |
- ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function")
|
|
| 14 |
- ErrDriverNotFound = errors.New("The requested docker init has not been found")
|
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-var dockerInitFcts map[string]InitFunc |
|
| 18 |
- |
|
| 19 |
-type ( |
|
| 20 |
- StartCallback func(*Command) |
|
| 21 |
- InitFunc func(i *InitArgs) error |
|
| 22 |
-) |
|
| 23 |
- |
|
| 24 |
-func RegisterInitFunc(name string, fct InitFunc) error {
|
|
| 25 |
- if dockerInitFcts == nil {
|
|
| 26 |
- dockerInitFcts = make(map[string]InitFunc) |
|
| 27 |
- } |
|
| 28 |
- if _, ok := dockerInitFcts[name]; ok {
|
|
| 29 |
- return ErrDriverAlreadyRegistered |
|
| 30 |
- } |
|
| 31 |
- dockerInitFcts[name] = fct |
|
| 32 |
- return nil |
|
| 33 |
-} |
|
| 34 |
- |
|
| 35 |
-func GetInitFunc(name string) (InitFunc, error) {
|
|
| 36 |
- fct, ok := dockerInitFcts[name] |
|
| 37 |
- if !ok {
|
|
| 38 |
- return nil, ErrDriverNotFound |
|
| 39 |
- } |
|
| 40 |
- return fct, nil |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-// Args provided to the init function for a driver |
|
| 44 |
-type InitArgs struct {
|
|
| 45 |
- User string |
|
| 46 |
- Gateway string |
|
| 47 |
- Ip string |
|
| 48 |
- WorkDir string |
|
| 49 |
- Privileged bool |
|
| 50 |
- Env []string |
|
| 51 |
- Args []string |
|
| 52 |
- Mtu int |
|
| 53 |
- Driver string |
|
| 54 |
- Console string |
|
| 55 |
- Pipe int |
|
| 56 |
- Root string |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-// Driver specific information based on |
|
| 60 |
-// processes registered with the driver |
|
| 61 |
-type Info interface {
|
|
| 62 |
- IsRunning() bool |
|
| 63 |
-} |
|
| 64 |
- |
|
| 65 |
-// Terminal in an interface for drivers to implement |
|
| 66 |
-// if they want to support Close and Resize calls from |
|
| 67 |
-// the core |
|
| 68 |
-type Terminal interface {
|
|
| 69 |
- io.Closer |
|
| 70 |
- Resize(height, width int) error |
|
| 71 |
-} |
|
| 72 |
- |
|
| 73 |
-type TtyTerminal interface {
|
|
| 74 |
- Master() *os.File |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-type Driver interface {
|
|
| 78 |
- Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code |
|
| 79 |
- Kill(c *Command, sig int) error |
|
| 80 |
- Name() string // Driver name |
|
| 81 |
- Info(id string) Info // "temporary" hack (until we move state from core to plugins) |
|
| 82 |
- GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container. |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-// Network settings of the container |
|
| 86 |
-type Network struct {
|
|
| 87 |
- Gateway string `json:"gateway"` |
|
| 88 |
- IPAddress string `json:"ip"` |
|
| 89 |
- Bridge string `json:"bridge"` |
|
| 90 |
- IPPrefixLen int `json:"ip_prefix_len"` |
|
| 91 |
- Mtu int `json:"mtu"` |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-type Resources struct {
|
|
| 95 |
- Memory int64 `json:"memory"` |
|
| 96 |
- MemorySwap int64 `json:"memory_swap"` |
|
| 97 |
- CpuShares int64 `json:"cpu_shares"` |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-type Mount struct {
|
|
| 101 |
- Source string `json:"source"` |
|
| 102 |
- Destination string `json:"destination"` |
|
| 103 |
- Writable bool `json:"writable"` |
|
| 104 |
- Private bool `json:"private"` |
|
| 105 |
-} |
|
| 106 |
- |
|
| 107 |
-// Process wrapps an os/exec.Cmd to add more metadata |
|
| 108 |
-type Command struct {
|
|
| 109 |
- exec.Cmd `json:"-"` |
|
| 110 |
- |
|
| 111 |
- ID string `json:"id"` |
|
| 112 |
- Privileged bool `json:"privileged"` |
|
| 113 |
- User string `json:"user"` |
|
| 114 |
- Rootfs string `json:"rootfs"` // root fs of the container |
|
| 115 |
- InitPath string `json:"initpath"` // dockerinit |
|
| 116 |
- Entrypoint string `json:"entrypoint"` |
|
| 117 |
- Arguments []string `json:"arguments"` |
|
| 118 |
- WorkingDir string `json:"working_dir"` |
|
| 119 |
- ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver |
|
| 120 |
- Tty bool `json:"tty"` |
|
| 121 |
- Network *Network `json:"network"` // if network is nil then networking is disabled |
|
| 122 |
- Config []string `json:"config"` // generic values that specific drivers can consume |
|
| 123 |
- Resources *Resources `json:"resources"` |
|
| 124 |
- Mounts []Mount `json:"mounts"` |
|
| 125 |
- |
|
| 126 |
- Terminal Terminal `json:"-"` // standard or tty terminal |
|
| 127 |
- Console string `json:"-"` // dev/console path |
|
| 128 |
- ContainerPid int `json:"container_pid"` // the pid for the process inside a container |
|
| 129 |
-} |
|
| 130 |
- |
|
| 131 |
-// Return the pid of the process |
|
| 132 |
-// If the process is nil -1 will be returned |
|
| 133 |
-func (c *Command) Pid() int {
|
|
| 134 |
- return c.ContainerPid |
|
| 135 |
-} |
| 136 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,23 +0,0 @@ |
| 1 |
-package execdrivers |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/execdriver" |
|
| 6 |
- "github.com/dotcloud/docker/execdriver/lxc" |
|
| 7 |
- "github.com/dotcloud/docker/execdriver/native" |
|
| 8 |
- "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 9 |
- "path" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
|
| 13 |
- switch name {
|
|
| 14 |
- case "lxc": |
|
| 15 |
- // we want to five the lxc driver the full docker root because it needs |
|
| 16 |
- // to access and write config and template files in /var/lib/docker/containers/* |
|
| 17 |
- // to be backwards compatible |
|
| 18 |
- return lxc.NewDriver(root, sysInfo.AppArmor) |
|
| 19 |
- case "native": |
|
| 20 |
- return native.NewDriver(path.Join(root, "execdriver", "native"), initPath) |
|
| 21 |
- } |
|
| 22 |
- return nil, fmt.Errorf("unknown exec driver %s", name)
|
|
| 23 |
-} |
| 24 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,396 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/execdriver" |
|
| 6 |
- "github.com/dotcloud/docker/pkg/cgroups" |
|
| 7 |
- "github.com/dotcloud/docker/utils" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "log" |
|
| 10 |
- "os" |
|
| 11 |
- "os/exec" |
|
| 12 |
- "path" |
|
| 13 |
- "path/filepath" |
|
| 14 |
- "strconv" |
|
| 15 |
- "strings" |
|
| 16 |
- "syscall" |
|
| 17 |
- "time" |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-const DriverName = "lxc" |
|
| 21 |
- |
|
| 22 |
-func init() {
|
|
| 23 |
- execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 24 |
- if err := setupEnv(args); err != nil {
|
|
| 25 |
- return err |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- if err := setupHostname(args); err != nil {
|
|
| 29 |
- return err |
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- if err := setupNetworking(args); err != nil {
|
|
| 33 |
- return err |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- if err := setupCapabilities(args); err != nil {
|
|
| 37 |
- return err |
|
| 38 |
- } |
|
| 39 |
- |
|
| 40 |
- if err := setupWorkingDirectory(args); err != nil {
|
|
| 41 |
- return err |
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- if err := changeUser(args); err != nil {
|
|
| 45 |
- return err |
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- path, err := exec.LookPath(args.Args[0]) |
|
| 49 |
- if err != nil {
|
|
| 50 |
- log.Printf("Unable to locate %v", args.Args[0])
|
|
| 51 |
- os.Exit(127) |
|
| 52 |
- } |
|
| 53 |
- if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
|
|
| 54 |
- return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
|
|
| 55 |
- } |
|
| 56 |
- panic("Unreachable")
|
|
| 57 |
- }) |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 |
-type driver struct {
|
|
| 61 |
- root string // root path for the driver to use |
|
| 62 |
- apparmor bool |
|
| 63 |
- sharedRoot bool |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func NewDriver(root string, apparmor bool) (*driver, error) {
|
|
| 67 |
- // setup unconfined symlink |
|
| 68 |
- if err := linkLxcStart(root); err != nil {
|
|
| 69 |
- return nil, err |
|
| 70 |
- } |
|
| 71 |
- return &driver{
|
|
| 72 |
- apparmor: apparmor, |
|
| 73 |
- root: root, |
|
| 74 |
- sharedRoot: rootIsShared(), |
|
| 75 |
- }, nil |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-func (d *driver) Name() string {
|
|
| 79 |
- version := d.version() |
|
| 80 |
- return fmt.Sprintf("%s-%s", DriverName, version)
|
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 84 |
- if err := execdriver.SetTerminal(c, pipes); err != nil {
|
|
| 85 |
- return -1, err |
|
| 86 |
- } |
|
| 87 |
- configPath, err := d.generateLXCConfig(c) |
|
| 88 |
- if err != nil {
|
|
| 89 |
- return -1, err |
|
| 90 |
- } |
|
| 91 |
- params := []string{
|
|
| 92 |
- "lxc-start", |
|
| 93 |
- "-n", c.ID, |
|
| 94 |
- "-f", configPath, |
|
| 95 |
- "--", |
|
| 96 |
- c.InitPath, |
|
| 97 |
- "-driver", |
|
| 98 |
- DriverName, |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- if c.Network != nil {
|
|
| 102 |
- params = append(params, |
|
| 103 |
- "-g", c.Network.Gateway, |
|
| 104 |
- "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
| 105 |
- "-mtu", strconv.Itoa(c.Network.Mtu), |
|
| 106 |
- ) |
|
| 107 |
- } |
|
| 108 |
- |
|
| 109 |
- if c.User != "" {
|
|
| 110 |
- params = append(params, "-u", c.User) |
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- if c.Privileged {
|
|
| 114 |
- if d.apparmor {
|
|
| 115 |
- params[0] = path.Join(d.root, "lxc-start-unconfined") |
|
| 116 |
- |
|
| 117 |
- } |
|
| 118 |
- params = append(params, "-privileged") |
|
| 119 |
- } |
|
| 120 |
- |
|
| 121 |
- if c.WorkingDir != "" {
|
|
| 122 |
- params = append(params, "-w", c.WorkingDir) |
|
| 123 |
- } |
|
| 124 |
- |
|
| 125 |
- params = append(params, "--", c.Entrypoint) |
|
| 126 |
- params = append(params, c.Arguments...) |
|
| 127 |
- |
|
| 128 |
- if d.sharedRoot {
|
|
| 129 |
- // lxc-start really needs / to be non-shared, or all kinds of stuff break |
|
| 130 |
- // when lxc-start unmount things and those unmounts propagate to the main |
|
| 131 |
- // mount namespace. |
|
| 132 |
- // What we really want is to clone into a new namespace and then |
|
| 133 |
- // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork |
|
| 134 |
- // without exec in go we have to do this horrible shell hack... |
|
| 135 |
- shellString := |
|
| 136 |
- "mount --make-rslave /; exec " + |
|
| 137 |
- utils.ShellQuoteArguments(params) |
|
| 138 |
- |
|
| 139 |
- params = []string{
|
|
| 140 |
- "unshare", "-m", "--", "/bin/sh", "-c", shellString, |
|
| 141 |
- } |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- var ( |
|
| 145 |
- name = params[0] |
|
| 146 |
- arg = params[1:] |
|
| 147 |
- ) |
|
| 148 |
- aname, err := exec.LookPath(name) |
|
| 149 |
- if err != nil {
|
|
| 150 |
- aname = name |
|
| 151 |
- } |
|
| 152 |
- c.Path = aname |
|
| 153 |
- c.Args = append([]string{name}, arg...)
|
|
| 154 |
- |
|
| 155 |
- if err := c.Start(); err != nil {
|
|
| 156 |
- return -1, err |
|
| 157 |
- } |
|
| 158 |
- |
|
| 159 |
- var ( |
|
| 160 |
- waitErr error |
|
| 161 |
- waitLock = make(chan struct{})
|
|
| 162 |
- ) |
|
| 163 |
- go func() {
|
|
| 164 |
- if err := c.Wait(); err != nil {
|
|
| 165 |
- if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
|
|
| 166 |
- waitErr = err |
|
| 167 |
- } |
|
| 168 |
- } |
|
| 169 |
- close(waitLock) |
|
| 170 |
- }() |
|
| 171 |
- |
|
| 172 |
- // Poll lxc for RUNNING status |
|
| 173 |
- pid, err := d.waitForStart(c, waitLock) |
|
| 174 |
- if err != nil {
|
|
| 175 |
- if c.Process != nil {
|
|
| 176 |
- c.Process.Kill() |
|
| 177 |
- } |
|
| 178 |
- return -1, err |
|
| 179 |
- } |
|
| 180 |
- c.ContainerPid = pid |
|
| 181 |
- |
|
| 182 |
- if startCallback != nil {
|
|
| 183 |
- startCallback(c) |
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- <-waitLock |
|
| 187 |
- |
|
| 188 |
- return getExitCode(c), waitErr |
|
| 189 |
-} |
|
| 190 |
- |
|
| 191 |
-/// Return the exit code of the process |
|
| 192 |
-// if the process has not exited -1 will be returned |
|
| 193 |
-func getExitCode(c *execdriver.Command) int {
|
|
| 194 |
- if c.ProcessState == nil {
|
|
| 195 |
- return -1 |
|
| 196 |
- } |
|
| 197 |
- return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
|
| 198 |
-} |
|
| 199 |
- |
|
| 200 |
-func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
|
| 201 |
- return KillLxc(c.ID, sig) |
|
| 202 |
-} |
|
| 203 |
- |
|
| 204 |
-func (d *driver) version() string {
|
|
| 205 |
- var ( |
|
| 206 |
- version string |
|
| 207 |
- output []byte |
|
| 208 |
- err error |
|
| 209 |
- ) |
|
| 210 |
- if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
|
|
| 211 |
- output, err = exec.Command("lxc-version").CombinedOutput()
|
|
| 212 |
- } else {
|
|
| 213 |
- output, err = exec.Command("lxc-start", "--version").CombinedOutput()
|
|
| 214 |
- } |
|
| 215 |
- if err == nil {
|
|
| 216 |
- version = strings.TrimSpace(string(output)) |
|
| 217 |
- if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
|
|
| 218 |
- version = strings.TrimSpace(parts[1]) |
|
| 219 |
- } |
|
| 220 |
- } |
|
| 221 |
- return version |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-func KillLxc(id string, sig int) error {
|
|
| 225 |
- var ( |
|
| 226 |
- err error |
|
| 227 |
- output []byte |
|
| 228 |
- ) |
|
| 229 |
- _, err = exec.LookPath("lxc-kill")
|
|
| 230 |
- if err == nil {
|
|
| 231 |
- output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
|
|
| 232 |
- } else {
|
|
| 233 |
- output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
|
|
| 234 |
- } |
|
| 235 |
- if err != nil {
|
|
| 236 |
- return fmt.Errorf("Err: %s Output: %s", err, output)
|
|
| 237 |
- } |
|
| 238 |
- return nil |
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-// wait for the process to start and return the pid for the process |
|
| 242 |
-func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
|
|
| 243 |
- var ( |
|
| 244 |
- err error |
|
| 245 |
- output []byte |
|
| 246 |
- ) |
|
| 247 |
- // We wait for the container to be fully running. |
|
| 248 |
- // Timeout after 5 seconds. In case of broken pipe, just retry. |
|
| 249 |
- // Note: The container can run and finish correctly before |
|
| 250 |
- // the end of this loop |
|
| 251 |
- for now := time.Now(); time.Since(now) < 5*time.Second; {
|
|
| 252 |
- select {
|
|
| 253 |
- case <-waitLock: |
|
| 254 |
- // If the process dies while waiting for it, just return |
|
| 255 |
- return -1, nil |
|
| 256 |
- default: |
|
| 257 |
- } |
|
| 258 |
- |
|
| 259 |
- output, err = d.getInfo(c.ID) |
|
| 260 |
- if err != nil {
|
|
| 261 |
- output, err = d.getInfo(c.ID) |
|
| 262 |
- if err != nil {
|
|
| 263 |
- return -1, err |
|
| 264 |
- } |
|
| 265 |
- } |
|
| 266 |
- info, err := parseLxcInfo(string(output)) |
|
| 267 |
- if err != nil {
|
|
| 268 |
- return -1, err |
|
| 269 |
- } |
|
| 270 |
- if info.Running {
|
|
| 271 |
- return info.Pid, nil |
|
| 272 |
- } |
|
| 273 |
- time.Sleep(50 * time.Millisecond) |
|
| 274 |
- } |
|
| 275 |
- return -1, execdriver.ErrNotRunning |
|
| 276 |
-} |
|
| 277 |
- |
|
| 278 |
-func (d *driver) getInfo(id string) ([]byte, error) {
|
|
| 279 |
- return exec.Command("lxc-info", "-n", id).CombinedOutput()
|
|
| 280 |
-} |
|
| 281 |
- |
|
| 282 |
-type info struct {
|
|
| 283 |
- ID string |
|
| 284 |
- driver *driver |
|
| 285 |
-} |
|
| 286 |
- |
|
| 287 |
-func (i *info) IsRunning() bool {
|
|
| 288 |
- var running bool |
|
| 289 |
- |
|
| 290 |
- output, err := i.driver.getInfo(i.ID) |
|
| 291 |
- if err != nil {
|
|
| 292 |
- utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
|
|
| 293 |
- return false |
|
| 294 |
- } |
|
| 295 |
- if strings.Contains(string(output), "RUNNING") {
|
|
| 296 |
- running = true |
|
| 297 |
- } |
|
| 298 |
- return running |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 |
-func (d *driver) Info(id string) execdriver.Info {
|
|
| 302 |
- return &info{
|
|
| 303 |
- ID: id, |
|
| 304 |
- driver: d, |
|
| 305 |
- } |
|
| 306 |
-} |
|
| 307 |
- |
|
| 308 |
-func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
| 309 |
- pids := []int{}
|
|
| 310 |
- |
|
| 311 |
- // cpu is chosen because it is the only non optional subsystem in cgroups |
|
| 312 |
- subsystem := "cpu" |
|
| 313 |
- cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) |
|
| 314 |
- if err != nil {
|
|
| 315 |
- return pids, err |
|
| 316 |
- } |
|
| 317 |
- |
|
| 318 |
- cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) |
|
| 319 |
- if err != nil {
|
|
| 320 |
- return pids, err |
|
| 321 |
- } |
|
| 322 |
- |
|
| 323 |
- filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks") |
|
| 324 |
- if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
| 325 |
- // With more recent lxc versions use, cgroup will be in lxc/ |
|
| 326 |
- filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks") |
|
| 327 |
- } |
|
| 328 |
- |
|
| 329 |
- output, err := ioutil.ReadFile(filename) |
|
| 330 |
- if err != nil {
|
|
| 331 |
- return pids, err |
|
| 332 |
- } |
|
| 333 |
- for _, p := range strings.Split(string(output), "\n") {
|
|
| 334 |
- if len(p) == 0 {
|
|
| 335 |
- continue |
|
| 336 |
- } |
|
| 337 |
- pid, err := strconv.Atoi(p) |
|
| 338 |
- if err != nil {
|
|
| 339 |
- return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
| 340 |
- } |
|
| 341 |
- pids = append(pids, pid) |
|
| 342 |
- } |
|
| 343 |
- return pids, nil |
|
| 344 |
-} |
|
| 345 |
- |
|
| 346 |
-func linkLxcStart(root string) error {
|
|
| 347 |
- sourcePath, err := exec.LookPath("lxc-start")
|
|
| 348 |
- if err != nil {
|
|
| 349 |
- return err |
|
| 350 |
- } |
|
| 351 |
- targetPath := path.Join(root, "lxc-start-unconfined") |
|
| 352 |
- |
|
| 353 |
- if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
|
|
| 354 |
- return err |
|
| 355 |
- } else if err == nil {
|
|
| 356 |
- if err := os.Remove(targetPath); err != nil {
|
|
| 357 |
- return err |
|
| 358 |
- } |
|
| 359 |
- } |
|
| 360 |
- return os.Symlink(sourcePath, targetPath) |
|
| 361 |
-} |
|
| 362 |
- |
|
| 363 |
-// TODO: This can be moved to the mountinfo reader in the mount pkg |
|
| 364 |
-func rootIsShared() bool {
|
|
| 365 |
- if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
|
|
| 366 |
- for _, line := range strings.Split(string(data), "\n") {
|
|
| 367 |
- cols := strings.Split(line, " ") |
|
| 368 |
- if len(cols) >= 6 && cols[4] == "/" {
|
|
| 369 |
- return strings.HasPrefix(cols[6], "shared") |
|
| 370 |
- } |
|
| 371 |
- } |
|
| 372 |
- } |
|
| 373 |
- |
|
| 374 |
- // No idea, probably safe to assume so |
|
| 375 |
- return true |
|
| 376 |
-} |
|
| 377 |
- |
|
| 378 |
-func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
|
|
| 379 |
- root := path.Join(d.root, "containers", c.ID, "config.lxc") |
|
| 380 |
- fo, err := os.Create(root) |
|
| 381 |
- if err != nil {
|
|
| 382 |
- return "", err |
|
| 383 |
- } |
|
| 384 |
- defer fo.Close() |
|
| 385 |
- |
|
| 386 |
- if err := LxcTemplateCompiled.Execute(fo, struct {
|
|
| 387 |
- *execdriver.Command |
|
| 388 |
- AppArmor bool |
|
| 389 |
- }{
|
|
| 390 |
- Command: c, |
|
| 391 |
- AppArmor: d.apparmor, |
|
| 392 |
- }); err != nil {
|
|
| 393 |
- return "", err |
|
| 394 |
- } |
|
| 395 |
- return root, nil |
|
| 396 |
-} |
| 397 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,50 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "errors" |
|
| 6 |
- "strconv" |
|
| 7 |
- "strings" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-var ( |
|
| 11 |
- ErrCannotParse = errors.New("cannot parse raw input")
|
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-type lxcInfo struct {
|
|
| 15 |
- Running bool |
|
| 16 |
- Pid int |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func parseLxcInfo(raw string) (*lxcInfo, error) {
|
|
| 20 |
- if raw == "" {
|
|
| 21 |
- return nil, ErrCannotParse |
|
| 22 |
- } |
|
| 23 |
- var ( |
|
| 24 |
- err error |
|
| 25 |
- s = bufio.NewScanner(strings.NewReader(raw)) |
|
| 26 |
- info = &lxcInfo{}
|
|
| 27 |
- ) |
|
| 28 |
- for s.Scan() {
|
|
| 29 |
- text := s.Text() |
|
| 30 |
- |
|
| 31 |
- if s.Err() != nil {
|
|
| 32 |
- return nil, s.Err() |
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- parts := strings.Split(text, ":") |
|
| 36 |
- if len(parts) < 2 {
|
|
| 37 |
- continue |
|
| 38 |
- } |
|
| 39 |
- switch strings.ToLower(strings.TrimSpace(parts[0])) {
|
|
| 40 |
- case "state": |
|
| 41 |
- info.Running = strings.TrimSpace(parts[1]) == "RUNNING" |
|
| 42 |
- case "pid": |
|
| 43 |
- info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1])) |
|
| 44 |
- if err != nil {
|
|
| 45 |
- return nil, err |
|
| 46 |
- } |
|
| 47 |
- } |
|
| 48 |
- } |
|
| 49 |
- return info, nil |
|
| 50 |
-} |
| 51 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,36 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "testing" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-func TestParseRunningInfo(t *testing.T) {
|
|
| 8 |
- raw := ` |
|
| 9 |
- state: RUNNING |
|
| 10 |
- pid: 50` |
|
| 11 |
- |
|
| 12 |
- info, err := parseLxcInfo(raw) |
|
| 13 |
- if err != nil {
|
|
| 14 |
- t.Fatal(err) |
|
| 15 |
- } |
|
| 16 |
- if !info.Running {
|
|
| 17 |
- t.Fatal("info should return a running state")
|
|
| 18 |
- } |
|
| 19 |
- if info.Pid != 50 {
|
|
| 20 |
- t.Fatalf("info should have pid 50 got %d", info.Pid)
|
|
| 21 |
- } |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-func TestEmptyInfo(t *testing.T) {
|
|
| 25 |
- _, err := parseLxcInfo("")
|
|
| 26 |
- if err == nil {
|
|
| 27 |
- t.Fatal("error should not be nil")
|
|
| 28 |
- } |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func TestBadInfo(t *testing.T) {
|
|
| 32 |
- _, err := parseLxcInfo("state")
|
|
| 33 |
- if err != nil {
|
|
| 34 |
- t.Fatal(err) |
|
| 35 |
- } |
|
| 36 |
-} |
| 37 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,176 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/execdriver" |
|
| 7 |
- "github.com/dotcloud/docker/pkg/netlink" |
|
| 8 |
- "github.com/dotcloud/docker/pkg/user" |
|
| 9 |
- "github.com/syndtr/gocapability/capability" |
|
| 10 |
- "io/ioutil" |
|
| 11 |
- "net" |
|
| 12 |
- "os" |
|
| 13 |
- "strings" |
|
| 14 |
- "syscall" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// Clear environment pollution introduced by lxc-start |
|
| 18 |
-func setupEnv(args *execdriver.InitArgs) error {
|
|
| 19 |
- // Get env |
|
| 20 |
- var env []string |
|
| 21 |
- content, err := ioutil.ReadFile(".dockerenv")
|
|
| 22 |
- if err != nil {
|
|
| 23 |
- return fmt.Errorf("Unable to load environment variables: %v", err)
|
|
| 24 |
- } |
|
| 25 |
- if err := json.Unmarshal(content, &env); err != nil {
|
|
| 26 |
- return fmt.Errorf("Unable to unmarshal environment variables: %v", err)
|
|
| 27 |
- } |
|
| 28 |
- // Propagate the plugin-specific container env variable |
|
| 29 |
- env = append(env, "container="+os.Getenv("container"))
|
|
| 30 |
- |
|
| 31 |
- args.Env = env |
|
| 32 |
- |
|
| 33 |
- os.Clearenv() |
|
| 34 |
- for _, kv := range args.Env {
|
|
| 35 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 36 |
- if len(parts) == 1 {
|
|
| 37 |
- parts = append(parts, "") |
|
| 38 |
- } |
|
| 39 |
- os.Setenv(parts[0], parts[1]) |
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- return nil |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-func setupHostname(args *execdriver.InitArgs) error {
|
|
| 46 |
- hostname := getEnv(args, "HOSTNAME") |
|
| 47 |
- if hostname == "" {
|
|
| 48 |
- return nil |
|
| 49 |
- } |
|
| 50 |
- return setHostname(hostname) |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// Setup networking |
|
| 54 |
-func setupNetworking(args *execdriver.InitArgs) error {
|
|
| 55 |
- if args.Ip != "" {
|
|
| 56 |
- // eth0 |
|
| 57 |
- iface, err := net.InterfaceByName("eth0")
|
|
| 58 |
- if err != nil {
|
|
| 59 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 60 |
- } |
|
| 61 |
- ip, ipNet, err := net.ParseCIDR(args.Ip) |
|
| 62 |
- if err != nil {
|
|
| 63 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 64 |
- } |
|
| 65 |
- if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
|
| 66 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 67 |
- } |
|
| 68 |
- if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
|
|
| 69 |
- return fmt.Errorf("Unable to set MTU: %v", err)
|
|
| 70 |
- } |
|
| 71 |
- if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 72 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 73 |
- } |
|
| 74 |
- |
|
| 75 |
- // loopback |
|
| 76 |
- iface, err = net.InterfaceByName("lo")
|
|
| 77 |
- if err != nil {
|
|
| 78 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 79 |
- } |
|
| 80 |
- if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 81 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 82 |
- } |
|
| 83 |
- } |
|
| 84 |
- if args.Gateway != "" {
|
|
| 85 |
- gw := net.ParseIP(args.Gateway) |
|
| 86 |
- if gw == nil {
|
|
| 87 |
- return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
|
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- if err := netlink.AddDefaultGw(gw); err != nil {
|
|
| 91 |
- return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 92 |
- } |
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- return nil |
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-// Setup working directory |
|
| 99 |
-func setupWorkingDirectory(args *execdriver.InitArgs) error {
|
|
| 100 |
- if args.WorkDir == "" {
|
|
| 101 |
- return nil |
|
| 102 |
- } |
|
| 103 |
- if err := syscall.Chdir(args.WorkDir); err != nil {
|
|
| 104 |
- return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
|
|
| 105 |
- } |
|
| 106 |
- return nil |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-// Takes care of dropping privileges to the desired user |
|
| 110 |
-func changeUser(args *execdriver.InitArgs) error {
|
|
| 111 |
- uid, gid, suppGids, err := user.GetUserGroupSupplementary( |
|
| 112 |
- args.User, |
|
| 113 |
- syscall.Getuid(), syscall.Getgid(), |
|
| 114 |
- ) |
|
| 115 |
- if err != nil {
|
|
| 116 |
- return err |
|
| 117 |
- } |
|
| 118 |
- |
|
| 119 |
- if err := syscall.Setgroups(suppGids); err != nil {
|
|
| 120 |
- return fmt.Errorf("Setgroups failed: %v", err)
|
|
| 121 |
- } |
|
| 122 |
- if err := syscall.Setgid(gid); err != nil {
|
|
| 123 |
- return fmt.Errorf("Setgid failed: %v", err)
|
|
| 124 |
- } |
|
| 125 |
- if err := syscall.Setuid(uid); err != nil {
|
|
| 126 |
- return fmt.Errorf("Setuid failed: %v", err)
|
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- return nil |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func setupCapabilities(args *execdriver.InitArgs) error {
|
|
| 133 |
- if args.Privileged {
|
|
| 134 |
- return nil |
|
| 135 |
- } |
|
| 136 |
- |
|
| 137 |
- drop := []capability.Cap{
|
|
| 138 |
- capability.CAP_SETPCAP, |
|
| 139 |
- capability.CAP_SYS_MODULE, |
|
| 140 |
- capability.CAP_SYS_RAWIO, |
|
| 141 |
- capability.CAP_SYS_PACCT, |
|
| 142 |
- capability.CAP_SYS_ADMIN, |
|
| 143 |
- capability.CAP_SYS_NICE, |
|
| 144 |
- capability.CAP_SYS_RESOURCE, |
|
| 145 |
- capability.CAP_SYS_TIME, |
|
| 146 |
- capability.CAP_SYS_TTY_CONFIG, |
|
| 147 |
- capability.CAP_MKNOD, |
|
| 148 |
- capability.CAP_AUDIT_WRITE, |
|
| 149 |
- capability.CAP_AUDIT_CONTROL, |
|
| 150 |
- capability.CAP_MAC_OVERRIDE, |
|
| 151 |
- capability.CAP_MAC_ADMIN, |
|
| 152 |
- capability.CAP_NET_ADMIN, |
|
| 153 |
- } |
|
| 154 |
- |
|
| 155 |
- c, err := capability.NewPid(os.Getpid()) |
|
| 156 |
- if err != nil {
|
|
| 157 |
- return err |
|
| 158 |
- } |
|
| 159 |
- |
|
| 160 |
- c.Unset(capability.CAPS|capability.BOUNDS, drop...) |
|
| 161 |
- |
|
| 162 |
- if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
|
|
| 163 |
- return err |
|
| 164 |
- } |
|
| 165 |
- return nil |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func getEnv(args *execdriver.InitArgs, key string) string {
|
|
| 169 |
- for _, kv := range args.Env {
|
|
| 170 |
- parts := strings.SplitN(kv, "=", 2) |
|
| 171 |
- if parts[0] == key && len(parts) == 2 {
|
|
| 172 |
- return parts[1] |
|
| 173 |
- } |
|
| 174 |
- } |
|
| 175 |
- return "" |
|
| 176 |
-} |
| 8 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,155 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/execdriver" |
|
| 5 |
- "strings" |
|
| 6 |
- "text/template" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-const LxcTemplate = ` |
|
| 10 |
-{{if .Network}}
|
|
| 11 |
-# network configuration |
|
| 12 |
-lxc.network.type = veth |
|
| 13 |
-lxc.network.link = {{.Network.Bridge}}
|
|
| 14 |
-lxc.network.name = eth0 |
|
| 15 |
-lxc.network.mtu = {{.Network.Mtu}}
|
|
| 16 |
-{{else}}
|
|
| 17 |
-# network is disabled (-n=false) |
|
| 18 |
-lxc.network.type = empty |
|
| 19 |
-lxc.network.flags = up |
|
| 20 |
-{{end}}
|
|
| 21 |
- |
|
| 22 |
-# root filesystem |
|
| 23 |
-{{$ROOTFS := .Rootfs}}
|
|
| 24 |
-lxc.rootfs = {{$ROOTFS}}
|
|
| 25 |
- |
|
| 26 |
-# use a dedicated pts for the container (and limit the number of pseudo terminal |
|
| 27 |
-# available) |
|
| 28 |
-lxc.pts = 1024 |
|
| 29 |
- |
|
| 30 |
-# disable the main console |
|
| 31 |
-lxc.console = none |
|
| 32 |
- |
|
| 33 |
-# no controlling tty at all |
|
| 34 |
-lxc.tty = 1 |
|
| 35 |
- |
|
| 36 |
-{{if .Privileged}}
|
|
| 37 |
-lxc.cgroup.devices.allow = a |
|
| 38 |
-{{else}}
|
|
| 39 |
-# no implicit access to devices |
|
| 40 |
-lxc.cgroup.devices.deny = a |
|
| 41 |
- |
|
| 42 |
-# /dev/null and zero |
|
| 43 |
-lxc.cgroup.devices.allow = c 1:3 rwm |
|
| 44 |
-lxc.cgroup.devices.allow = c 1:5 rwm |
|
| 45 |
- |
|
| 46 |
-# consoles |
|
| 47 |
-lxc.cgroup.devices.allow = c 5:1 rwm |
|
| 48 |
-lxc.cgroup.devices.allow = c 5:0 rwm |
|
| 49 |
-lxc.cgroup.devices.allow = c 4:0 rwm |
|
| 50 |
-lxc.cgroup.devices.allow = c 4:1 rwm |
|
| 51 |
- |
|
| 52 |
-# /dev/urandom,/dev/random |
|
| 53 |
-lxc.cgroup.devices.allow = c 1:9 rwm |
|
| 54 |
-lxc.cgroup.devices.allow = c 1:8 rwm |
|
| 55 |
- |
|
| 56 |
-# /dev/pts/ - pts namespaces are "coming soon" |
|
| 57 |
-lxc.cgroup.devices.allow = c 136:* rwm |
|
| 58 |
-lxc.cgroup.devices.allow = c 5:2 rwm |
|
| 59 |
- |
|
| 60 |
-# tuntap |
|
| 61 |
-lxc.cgroup.devices.allow = c 10:200 rwm |
|
| 62 |
- |
|
| 63 |
-# fuse |
|
| 64 |
-#lxc.cgroup.devices.allow = c 10:229 rwm |
|
| 65 |
- |
|
| 66 |
-# rtc |
|
| 67 |
-#lxc.cgroup.devices.allow = c 254:0 rwm |
|
| 68 |
-{{end}}
|
|
| 69 |
- |
|
| 70 |
-# standard mount point |
|
| 71 |
-# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385 |
|
| 72 |
-lxc.pivotdir = lxc_putold |
|
| 73 |
- |
|
| 74 |
-# NOTICE: These mounts must be applied within the namespace |
|
| 75 |
- |
|
| 76 |
-# WARNING: procfs is a known attack vector and should probably be disabled |
|
| 77 |
-# if your userspace allows it. eg. see http://blog.zx2c4.com/749 |
|
| 78 |
-lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
|
| 79 |
- |
|
| 80 |
-# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 81 |
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 82 |
-lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
|
| 83 |
- |
|
| 84 |
-{{if .Tty}}
|
|
| 85 |
-lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
|
|
| 86 |
-{{end}}
|
|
| 87 |
- |
|
| 88 |
-lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
|
| 89 |
-lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
|
| 90 |
- |
|
| 91 |
-{{range $value := .Mounts}}
|
|
| 92 |
-{{if $value.Writable}}
|
|
| 93 |
-lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,rw 0 0
|
|
| 94 |
-{{else}}
|
|
| 95 |
-lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,ro 0 0
|
|
| 96 |
-{{end}}
|
|
| 97 |
-{{end}}
|
|
| 98 |
- |
|
| 99 |
-{{if .Privileged}}
|
|
| 100 |
-{{if .AppArmor}}
|
|
| 101 |
-lxc.aa_profile = unconfined |
|
| 102 |
-{{else}}
|
|
| 103 |
-#lxc.aa_profile = unconfined |
|
| 104 |
-{{end}}
|
|
| 105 |
-{{end}}
|
|
| 106 |
- |
|
| 107 |
-# limits |
|
| 108 |
-{{if .Resources}}
|
|
| 109 |
-{{if .Resources.Memory}}
|
|
| 110 |
-lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
|
|
| 111 |
-lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
|
|
| 112 |
-{{with $memSwap := getMemorySwap .Resources}}
|
|
| 113 |
-lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
|
| 114 |
-{{end}}
|
|
| 115 |
-{{end}}
|
|
| 116 |
-{{if .Resources.CpuShares}}
|
|
| 117 |
-lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
|
|
| 118 |
-{{end}}
|
|
| 119 |
-{{end}}
|
|
| 120 |
- |
|
| 121 |
-{{if .Config}}
|
|
| 122 |
-{{range $value := .Config}}
|
|
| 123 |
-{{$value}}
|
|
| 124 |
-{{end}}
|
|
| 125 |
-{{end}}
|
|
| 126 |
-` |
|
| 127 |
- |
|
| 128 |
-var LxcTemplateCompiled *template.Template |
|
| 129 |
- |
|
| 130 |
-// Escape spaces in strings according to the fstab documentation, which is the |
|
| 131 |
-// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab". |
|
| 132 |
-func escapeFstabSpaces(field string) string {
|
|
| 133 |
- return strings.Replace(field, " ", "\\040", -1) |
|
| 134 |
-} |
|
| 135 |
- |
|
| 136 |
-func getMemorySwap(v *execdriver.Resources) int64 {
|
|
| 137 |
- // By default, MemorySwap is set to twice the size of RAM. |
|
| 138 |
- // If you want to omit MemorySwap, set it to `-1'. |
|
| 139 |
- if v.MemorySwap < 0 {
|
|
| 140 |
- return 0 |
|
| 141 |
- } |
|
| 142 |
- return v.Memory * 2 |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-func init() {
|
|
| 146 |
- var err error |
|
| 147 |
- funcMap := template.FuncMap{
|
|
| 148 |
- "getMemorySwap": getMemorySwap, |
|
| 149 |
- "escapeFstabSpaces": escapeFstabSpaces, |
|
| 150 |
- } |
|
| 151 |
- LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
|
| 152 |
- if err != nil {
|
|
| 153 |
- panic(err) |
|
| 154 |
- } |
|
| 155 |
-} |
| 156 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,125 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/execdriver" |
|
| 7 |
- "io/ioutil" |
|
| 8 |
- "math/rand" |
|
| 9 |
- "os" |
|
| 10 |
- "path" |
|
| 11 |
- "strings" |
|
| 12 |
- "testing" |
|
| 13 |
- "time" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-func TestLXCConfig(t *testing.T) {
|
|
| 17 |
- root, err := ioutil.TempDir("", "TestLXCConfig")
|
|
| 18 |
- if err != nil {
|
|
| 19 |
- t.Fatal(err) |
|
| 20 |
- } |
|
| 21 |
- defer os.RemoveAll(root) |
|
| 22 |
- |
|
| 23 |
- os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 24 |
- |
|
| 25 |
- // Memory is allocated randomly for testing |
|
| 26 |
- rand.Seed(time.Now().UTC().UnixNano()) |
|
| 27 |
- var ( |
|
| 28 |
- memMin = 33554432 |
|
| 29 |
- memMax = 536870912 |
|
| 30 |
- mem = memMin + rand.Intn(memMax-memMin) |
|
| 31 |
- cpuMin = 100 |
|
| 32 |
- cpuMax = 10000 |
|
| 33 |
- cpu = cpuMin + rand.Intn(cpuMax-cpuMin) |
|
| 34 |
- ) |
|
| 35 |
- |
|
| 36 |
- driver, err := NewDriver(root, false) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- t.Fatal(err) |
|
| 39 |
- } |
|
| 40 |
- command := &execdriver.Command{
|
|
| 41 |
- ID: "1", |
|
| 42 |
- Resources: &execdriver.Resources{
|
|
| 43 |
- Memory: int64(mem), |
|
| 44 |
- CpuShares: int64(cpu), |
|
| 45 |
- }, |
|
| 46 |
- } |
|
| 47 |
- p, err := driver.generateLXCConfig(command) |
|
| 48 |
- if err != nil {
|
|
| 49 |
- t.Fatal(err) |
|
| 50 |
- } |
|
| 51 |
- grepFile(t, p, |
|
| 52 |
- fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
|
| 53 |
- |
|
| 54 |
- grepFile(t, p, |
|
| 55 |
- fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-func TestCustomLxcConfig(t *testing.T) {
|
|
| 59 |
- root, err := ioutil.TempDir("", "TestCustomLxcConfig")
|
|
| 60 |
- if err != nil {
|
|
| 61 |
- t.Fatal(err) |
|
| 62 |
- } |
|
| 63 |
- defer os.RemoveAll(root) |
|
| 64 |
- |
|
| 65 |
- os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 66 |
- |
|
| 67 |
- driver, err := NewDriver(root, false) |
|
| 68 |
- if err != nil {
|
|
| 69 |
- t.Fatal(err) |
|
| 70 |
- } |
|
| 71 |
- command := &execdriver.Command{
|
|
| 72 |
- ID: "1", |
|
| 73 |
- Privileged: false, |
|
| 74 |
- Config: []string{
|
|
| 75 |
- "lxc.utsname = docker", |
|
| 76 |
- "lxc.cgroup.cpuset.cpus = 0,1", |
|
| 77 |
- }, |
|
| 78 |
- } |
|
| 79 |
- |
|
| 80 |
- p, err := driver.generateLXCConfig(command) |
|
| 81 |
- if err != nil {
|
|
| 82 |
- t.Fatal(err) |
|
| 83 |
- } |
|
| 84 |
- |
|
| 85 |
- grepFile(t, p, "lxc.utsname = docker") |
|
| 86 |
- grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-func grepFile(t *testing.T, path string, pattern string) {
|
|
| 90 |
- f, err := os.Open(path) |
|
| 91 |
- if err != nil {
|
|
| 92 |
- t.Fatal(err) |
|
| 93 |
- } |
|
| 94 |
- defer f.Close() |
|
| 95 |
- r := bufio.NewReader(f) |
|
| 96 |
- var ( |
|
| 97 |
- line string |
|
| 98 |
- ) |
|
| 99 |
- err = nil |
|
| 100 |
- for err == nil {
|
|
| 101 |
- line, err = r.ReadString('\n')
|
|
| 102 |
- if strings.Contains(line, pattern) == true {
|
|
| 103 |
- return |
|
| 104 |
- } |
|
| 105 |
- } |
|
| 106 |
- t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
|
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func TestEscapeFstabSpaces(t *testing.T) {
|
|
| 110 |
- var testInputs = map[string]string{
|
|
| 111 |
- " ": "\\040", |
|
| 112 |
- "": "", |
|
| 113 |
- "/double space": "/double\\040\\040space", |
|
| 114 |
- "/some long test string": "/some\\040long\\040test\\040string", |
|
| 115 |
- "/var/lib/docker": "/var/lib/docker", |
|
| 116 |
- " leading": "\\040leading", |
|
| 117 |
- "trailing ": "trailing\\040", |
|
| 118 |
- } |
|
| 119 |
- for in, exp := range testInputs {
|
|
| 120 |
- if out := escapeFstabSpaces(in); exp != out {
|
|
| 121 |
- t.Logf("Expected %s got %s", exp, out)
|
|
| 122 |
- t.Fail() |
|
| 123 |
- } |
|
| 124 |
- } |
|
| 125 |
-} |
| 126 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,94 +0,0 @@ |
| 1 |
-package native |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/execdriver" |
|
| 6 |
- "github.com/dotcloud/docker/pkg/cgroups" |
|
| 7 |
- "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 8 |
- "os" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-// createContainer populates and configures the container type with the |
|
| 12 |
-// data provided by the execdriver.Command |
|
| 13 |
-func createContainer(c *execdriver.Command) *libcontainer.Container {
|
|
| 14 |
- container := getDefaultTemplate() |
|
| 15 |
- |
|
| 16 |
- container.Hostname = getEnv("HOSTNAME", c.Env)
|
|
| 17 |
- container.Tty = c.Tty |
|
| 18 |
- container.User = c.User |
|
| 19 |
- container.WorkingDir = c.WorkingDir |
|
| 20 |
- container.Env = c.Env |
|
| 21 |
- |
|
| 22 |
- if c.Network != nil {
|
|
| 23 |
- container.Networks = []*libcontainer.Network{
|
|
| 24 |
- {
|
|
| 25 |
- Mtu: c.Network.Mtu, |
|
| 26 |
- Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
| 27 |
- Gateway: c.Network.Gateway, |
|
| 28 |
- Type: "veth", |
|
| 29 |
- Context: libcontainer.Context{
|
|
| 30 |
- "prefix": "veth", |
|
| 31 |
- "bridge": c.Network.Bridge, |
|
| 32 |
- }, |
|
| 33 |
- }, |
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- container.Cgroups.Name = c.ID |
|
| 38 |
- if c.Privileged {
|
|
| 39 |
- container.CapabilitiesMask = nil |
|
| 40 |
- container.Cgroups.DeviceAccess = true |
|
| 41 |
- container.Context["apparmor_profile"] = "unconfined" |
|
| 42 |
- } |
|
| 43 |
- if c.Resources != nil {
|
|
| 44 |
- container.Cgroups.CpuShares = c.Resources.CpuShares |
|
| 45 |
- container.Cgroups.Memory = c.Resources.Memory |
|
| 46 |
- container.Cgroups.MemorySwap = c.Resources.MemorySwap |
|
| 47 |
- } |
|
| 48 |
- // check to see if we are running in ramdisk to disable pivot root |
|
| 49 |
- container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
|
| 50 |
- |
|
| 51 |
- for _, m := range c.Mounts {
|
|
| 52 |
- container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
|
|
| 53 |
- } |
|
| 54 |
- |
|
| 55 |
- return container |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// getDefaultTemplate returns the docker default for |
|
| 59 |
-// the libcontainer configuration file |
|
| 60 |
-func getDefaultTemplate() *libcontainer.Container {
|
|
| 61 |
- return &libcontainer.Container{
|
|
| 62 |
- CapabilitiesMask: libcontainer.Capabilities{
|
|
| 63 |
- libcontainer.GetCapability("SETPCAP"),
|
|
| 64 |
- libcontainer.GetCapability("SYS_MODULE"),
|
|
| 65 |
- libcontainer.GetCapability("SYS_RAWIO"),
|
|
| 66 |
- libcontainer.GetCapability("SYS_PACCT"),
|
|
| 67 |
- libcontainer.GetCapability("SYS_ADMIN"),
|
|
| 68 |
- libcontainer.GetCapability("SYS_NICE"),
|
|
| 69 |
- libcontainer.GetCapability("SYS_RESOURCE"),
|
|
| 70 |
- libcontainer.GetCapability("SYS_TIME"),
|
|
| 71 |
- libcontainer.GetCapability("SYS_TTY_CONFIG"),
|
|
| 72 |
- libcontainer.GetCapability("MKNOD"),
|
|
| 73 |
- libcontainer.GetCapability("AUDIT_WRITE"),
|
|
| 74 |
- libcontainer.GetCapability("AUDIT_CONTROL"),
|
|
| 75 |
- libcontainer.GetCapability("MAC_OVERRIDE"),
|
|
| 76 |
- libcontainer.GetCapability("MAC_ADMIN"),
|
|
| 77 |
- libcontainer.GetCapability("NET_ADMIN"),
|
|
| 78 |
- }, |
|
| 79 |
- Namespaces: libcontainer.Namespaces{
|
|
| 80 |
- libcontainer.GetNamespace("NEWNS"),
|
|
| 81 |
- libcontainer.GetNamespace("NEWUTS"),
|
|
| 82 |
- libcontainer.GetNamespace("NEWIPC"),
|
|
| 83 |
- libcontainer.GetNamespace("NEWPID"),
|
|
| 84 |
- libcontainer.GetNamespace("NEWNET"),
|
|
| 85 |
- }, |
|
| 86 |
- Cgroups: &cgroups.Cgroup{
|
|
| 87 |
- Parent: "docker", |
|
| 88 |
- DeviceAccess: false, |
|
| 89 |
- }, |
|
| 90 |
- Context: libcontainer.Context{
|
|
| 91 |
- "apparmor_profile": "docker-default", |
|
| 92 |
- }, |
|
| 93 |
- } |
|
| 94 |
-} |
| 95 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,266 +0,0 @@ |
| 1 |
-package native |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/execdriver" |
|
| 7 |
- "github.com/dotcloud/docker/pkg/cgroups" |
|
| 8 |
- "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 9 |
- "github.com/dotcloud/docker/pkg/libcontainer/apparmor" |
|
| 10 |
- "github.com/dotcloud/docker/pkg/libcontainer/nsinit" |
|
| 11 |
- "github.com/dotcloud/docker/pkg/system" |
|
| 12 |
- "io" |
|
| 13 |
- "io/ioutil" |
|
| 14 |
- "log" |
|
| 15 |
- "os" |
|
| 16 |
- "os/exec" |
|
| 17 |
- "path/filepath" |
|
| 18 |
- "strconv" |
|
| 19 |
- "strings" |
|
| 20 |
- "syscall" |
|
| 21 |
-) |
|
| 22 |
- |
|
| 23 |
-const ( |
|
| 24 |
- DriverName = "native" |
|
| 25 |
- Version = "0.1" |
|
| 26 |
-) |
|
| 27 |
- |
|
| 28 |
-func init() {
|
|
| 29 |
- execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 30 |
- var ( |
|
| 31 |
- container *libcontainer.Container |
|
| 32 |
- ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
|
|
| 33 |
- ) |
|
| 34 |
- f, err := os.Open(filepath.Join(args.Root, "container.json")) |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return err |
|
| 37 |
- } |
|
| 38 |
- if err := json.NewDecoder(f).Decode(&container); err != nil {
|
|
| 39 |
- f.Close() |
|
| 40 |
- return err |
|
| 41 |
- } |
|
| 42 |
- f.Close() |
|
| 43 |
- |
|
| 44 |
- cwd, err := os.Getwd() |
|
| 45 |
- if err != nil {
|
|
| 46 |
- return err |
|
| 47 |
- } |
|
| 48 |
- syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe)) |
|
| 49 |
- if err != nil {
|
|
| 50 |
- return err |
|
| 51 |
- } |
|
| 52 |
- if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
|
|
| 53 |
- return err |
|
| 54 |
- } |
|
| 55 |
- return nil |
|
| 56 |
- }) |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-type driver struct {
|
|
| 60 |
- root string |
|
| 61 |
- initPath string |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func NewDriver(root, initPath string) (*driver, error) {
|
|
| 65 |
- if err := os.MkdirAll(root, 0700); err != nil {
|
|
| 66 |
- return nil, err |
|
| 67 |
- } |
|
| 68 |
- if err := apparmor.InstallDefaultProfile(); err != nil {
|
|
| 69 |
- return nil, err |
|
| 70 |
- } |
|
| 71 |
- return &driver{
|
|
| 72 |
- root: root, |
|
| 73 |
- initPath: initPath, |
|
| 74 |
- }, nil |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 78 |
- if err := d.validateCommand(c); err != nil {
|
|
| 79 |
- return -1, err |
|
| 80 |
- } |
|
| 81 |
- var ( |
|
| 82 |
- term nsinit.Terminal |
|
| 83 |
- container = createContainer(c) |
|
| 84 |
- factory = &dockerCommandFactory{c: c, driver: d}
|
|
| 85 |
- stateWriter = &dockerStateWriter{
|
|
| 86 |
- callback: startCallback, |
|
| 87 |
- c: c, |
|
| 88 |
- dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
|
|
| 89 |
- } |
|
| 90 |
- ns = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
|
|
| 91 |
- args = append([]string{c.Entrypoint}, c.Arguments...)
|
|
| 92 |
- ) |
|
| 93 |
- if err := d.createContainerRoot(c.ID); err != nil {
|
|
| 94 |
- return -1, err |
|
| 95 |
- } |
|
| 96 |
- defer d.removeContainerRoot(c.ID) |
|
| 97 |
- |
|
| 98 |
- if c.Tty {
|
|
| 99 |
- term = &dockerTtyTerm{
|
|
| 100 |
- pipes: pipes, |
|
| 101 |
- } |
|
| 102 |
- } else {
|
|
| 103 |
- term = &dockerStdTerm{
|
|
| 104 |
- pipes: pipes, |
|
| 105 |
- } |
|
| 106 |
- } |
|
| 107 |
- c.Terminal = term |
|
| 108 |
- if err := d.writeContainerFile(container, c.ID); err != nil {
|
|
| 109 |
- return -1, err |
|
| 110 |
- } |
|
| 111 |
- return ns.Exec(container, term, args) |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
|
| 115 |
- err := syscall.Kill(p.Process.Pid, syscall.Signal(sig)) |
|
| 116 |
- d.removeContainerRoot(p.ID) |
|
| 117 |
- return err |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-func (d *driver) Info(id string) execdriver.Info {
|
|
| 121 |
- return &info{
|
|
| 122 |
- ID: id, |
|
| 123 |
- driver: d, |
|
| 124 |
- } |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-func (d *driver) Name() string {
|
|
| 128 |
- return fmt.Sprintf("%s-%s", DriverName, Version)
|
|
| 129 |
-} |
|
| 130 |
- |
|
| 131 |
-// TODO: this can be improved with our driver |
|
| 132 |
-// there has to be a better way to do this |
|
| 133 |
-func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
| 134 |
- pids := []int{}
|
|
| 135 |
- |
|
| 136 |
- subsystem := "devices" |
|
| 137 |
- cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) |
|
| 138 |
- if err != nil {
|
|
| 139 |
- return pids, err |
|
| 140 |
- } |
|
| 141 |
- cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) |
|
| 142 |
- if err != nil {
|
|
| 143 |
- return pids, err |
|
| 144 |
- } |
|
| 145 |
- |
|
| 146 |
- filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks") |
|
| 147 |
- if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
| 148 |
- filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks") |
|
| 149 |
- } |
|
| 150 |
- |
|
| 151 |
- output, err := ioutil.ReadFile(filename) |
|
| 152 |
- if err != nil {
|
|
| 153 |
- return pids, err |
|
| 154 |
- } |
|
| 155 |
- for _, p := range strings.Split(string(output), "\n") {
|
|
| 156 |
- if len(p) == 0 {
|
|
| 157 |
- continue |
|
| 158 |
- } |
|
| 159 |
- pid, err := strconv.Atoi(p) |
|
| 160 |
- if err != nil {
|
|
| 161 |
- return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
| 162 |
- } |
|
| 163 |
- pids = append(pids, pid) |
|
| 164 |
- } |
|
| 165 |
- return pids, nil |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
|
|
| 169 |
- data, err := json.Marshal(container) |
|
| 170 |
- if err != nil {
|
|
| 171 |
- return err |
|
| 172 |
- } |
|
| 173 |
- return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655) |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-func (d *driver) createContainerRoot(id string) error {
|
|
| 177 |
- return os.MkdirAll(filepath.Join(d.root, id), 0655) |
|
| 178 |
-} |
|
| 179 |
- |
|
| 180 |
-func (d *driver) removeContainerRoot(id string) error {
|
|
| 181 |
- return os.RemoveAll(filepath.Join(d.root, id)) |
|
| 182 |
-} |
|
| 183 |
- |
|
| 184 |
-func (d *driver) validateCommand(c *execdriver.Command) error {
|
|
| 185 |
- // we need to check the Config of the command to make sure that we |
|
| 186 |
- // do not have any of the lxc-conf variables |
|
| 187 |
- for _, conf := range c.Config {
|
|
| 188 |
- if strings.Contains(conf, "lxc") {
|
|
| 189 |
- return fmt.Errorf("%s is not supported by the native driver", conf)
|
|
| 190 |
- } |
|
| 191 |
- } |
|
| 192 |
- return nil |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-func getEnv(key string, env []string) string {
|
|
| 196 |
- for _, pair := range env {
|
|
| 197 |
- parts := strings.Split(pair, "=") |
|
| 198 |
- if parts[0] == key {
|
|
| 199 |
- return parts[1] |
|
| 200 |
- } |
|
| 201 |
- } |
|
| 202 |
- return "" |
|
| 203 |
-} |
|
| 204 |
- |
|
| 205 |
-type dockerCommandFactory struct {
|
|
| 206 |
- c *execdriver.Command |
|
| 207 |
- driver *driver |
|
| 208 |
-} |
|
| 209 |
- |
|
| 210 |
-// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces |
|
| 211 |
-// defined on the container's configuration and use the current binary as the init with the |
|
| 212 |
-// args provided |
|
| 213 |
-func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
|
|
| 214 |
- // we need to join the rootfs because nsinit will setup the rootfs and chroot |
|
| 215 |
- initPath := filepath.Join(d.c.Rootfs, d.c.InitPath) |
|
| 216 |
- |
|
| 217 |
- d.c.Path = d.driver.initPath |
|
| 218 |
- d.c.Args = append([]string{
|
|
| 219 |
- initPath, |
|
| 220 |
- "-driver", DriverName, |
|
| 221 |
- "-console", console, |
|
| 222 |
- "-pipe", "3", |
|
| 223 |
- "-root", filepath.Join(d.driver.root, d.c.ID), |
|
| 224 |
- "--", |
|
| 225 |
- }, args...) |
|
| 226 |
- |
|
| 227 |
- // set this to nil so that when we set the clone flags anything else is reset |
|
| 228 |
- d.c.SysProcAttr = nil |
|
| 229 |
- system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces))) |
|
| 230 |
- d.c.ExtraFiles = []*os.File{syncFile}
|
|
| 231 |
- |
|
| 232 |
- d.c.Env = container.Env |
|
| 233 |
- d.c.Dir = d.c.Rootfs |
|
| 234 |
- |
|
| 235 |
- return &d.c.Cmd |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-type dockerStateWriter struct {
|
|
| 239 |
- dsw nsinit.StateWriter |
|
| 240 |
- c *execdriver.Command |
|
| 241 |
- callback execdriver.StartCallback |
|
| 242 |
-} |
|
| 243 |
- |
|
| 244 |
-func (d *dockerStateWriter) WritePid(pid int) error {
|
|
| 245 |
- d.c.ContainerPid = pid |
|
| 246 |
- err := d.dsw.WritePid(pid) |
|
| 247 |
- if d.callback != nil {
|
|
| 248 |
- d.callback(d.c) |
|
| 249 |
- } |
|
| 250 |
- return err |
|
| 251 |
-} |
|
| 252 |
- |
|
| 253 |
-func (d *dockerStateWriter) DeletePid() error {
|
|
| 254 |
- return d.dsw.DeletePid() |
|
| 255 |
-} |
|
| 256 |
- |
|
| 257 |
-func createLogger(debug string) *log.Logger {
|
|
| 258 |
- var w io.Writer |
|
| 259 |
- // if we are in debug mode set the logger to stderr |
|
| 260 |
- if debug != "" {
|
|
| 261 |
- w = os.Stderr |
|
| 262 |
- } else {
|
|
| 263 |
- w = ioutil.Discard |
|
| 264 |
- } |
|
| 265 |
- return log.New(w, "[libcontainer] ", log.LstdFlags) |
|
| 266 |
-} |
| 267 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,21 +0,0 @@ |
| 1 |
-package native |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "os" |
|
| 5 |
- "path/filepath" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-type info struct {
|
|
| 9 |
- ID string |
|
| 10 |
- driver *driver |
|
| 11 |
-} |
|
| 12 |
- |
|
| 13 |
-// IsRunning is determined by looking for the |
|
| 14 |
-// pid file for a container. If the file exists then the |
|
| 15 |
-// container is currently running |
|
| 16 |
-func (i *info) IsRunning() bool {
|
|
| 17 |
- if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
|
|
| 18 |
- return true |
|
| 19 |
- } |
|
| 20 |
- return false |
|
| 21 |
-} |
| 22 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,42 +0,0 @@ |
| 1 |
-/* |
|
| 2 |
- These types are wrappers around the libcontainer Terminal interface so that |
|
| 3 |
- we can resuse the docker implementations where possible. |
|
| 4 |
-*/ |
|
| 5 |
-package native |
|
| 6 |
- |
|
| 7 |
-import ( |
|
| 8 |
- "github.com/dotcloud/docker/execdriver" |
|
| 9 |
- "io" |
|
| 10 |
- "os" |
|
| 11 |
- "os/exec" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-type dockerStdTerm struct {
|
|
| 15 |
- execdriver.StdConsole |
|
| 16 |
- pipes *execdriver.Pipes |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
|
|
| 20 |
- return d.AttachPipes(cmd, d.pipes) |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func (d *dockerStdTerm) SetMaster(master *os.File) {
|
|
| 24 |
- // do nothing |
|
| 25 |
-} |
|
| 26 |
- |
|
| 27 |
-type dockerTtyTerm struct {
|
|
| 28 |
- execdriver.TtyConsole |
|
| 29 |
- pipes *execdriver.Pipes |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
|
|
| 33 |
- go io.Copy(t.pipes.Stdout, t.MasterPty) |
|
| 34 |
- if t.pipes.Stdin != nil {
|
|
| 35 |
- go io.Copy(t.MasterPty, t.pipes.Stdin) |
|
| 36 |
- } |
|
| 37 |
- return nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (t *dockerTtyTerm) SetMaster(master *os.File) {
|
|
| 41 |
- t.MasterPty = master |
|
| 42 |
-} |
| 43 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,23 +0,0 @@ |
| 1 |
-package execdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "io" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-// Pipes is a wrapper around a containers output for |
|
| 8 |
-// stdin, stdout, stderr |
|
| 9 |
-type Pipes struct {
|
|
| 10 |
- Stdin io.ReadCloser |
|
| 11 |
- Stdout, Stderr io.Writer |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func NewPipes(stdin io.ReadCloser, stdout, stderr io.Writer, useStdin bool) *Pipes {
|
|
| 15 |
- p := &Pipes{
|
|
| 16 |
- Stdout: stdout, |
|
| 17 |
- Stderr: stderr, |
|
| 18 |
- } |
|
| 19 |
- if useStdin {
|
|
| 20 |
- p.Stdin = stdin |
|
| 21 |
- } |
|
| 22 |
- return p |
|
| 23 |
-} |
| 24 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,126 +0,0 @@ |
| 1 |
-package execdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/pkg/term" |
|
| 5 |
- "github.com/kr/pty" |
|
| 6 |
- "io" |
|
| 7 |
- "os" |
|
| 8 |
- "os/exec" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-func SetTerminal(command *Command, pipes *Pipes) error {
|
|
| 12 |
- var ( |
|
| 13 |
- term Terminal |
|
| 14 |
- err error |
|
| 15 |
- ) |
|
| 16 |
- if command.Tty {
|
|
| 17 |
- term, err = NewTtyConsole(command, pipes) |
|
| 18 |
- } else {
|
|
| 19 |
- term, err = NewStdConsole(command, pipes) |
|
| 20 |
- } |
|
| 21 |
- if err != nil {
|
|
| 22 |
- return err |
|
| 23 |
- } |
|
| 24 |
- command.Terminal = term |
|
| 25 |
- return nil |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-type TtyConsole struct {
|
|
| 29 |
- MasterPty *os.File |
|
| 30 |
- SlavePty *os.File |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
|
|
| 34 |
- ptyMaster, ptySlave, err := pty.Open() |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return nil, err |
|
| 37 |
- } |
|
| 38 |
- tty := &TtyConsole{
|
|
| 39 |
- MasterPty: ptyMaster, |
|
| 40 |
- SlavePty: ptySlave, |
|
| 41 |
- } |
|
| 42 |
- if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
|
|
| 43 |
- tty.Close() |
|
| 44 |
- return nil, err |
|
| 45 |
- } |
|
| 46 |
- command.Console = tty.SlavePty.Name() |
|
| 47 |
- return tty, nil |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func (t *TtyConsole) Master() *os.File {
|
|
| 51 |
- return t.MasterPty |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-func (t *TtyConsole) Resize(h, w int) error {
|
|
| 55 |
- return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
|
| 59 |
- command.Stdout = t.SlavePty |
|
| 60 |
- command.Stderr = t.SlavePty |
|
| 61 |
- |
|
| 62 |
- go func() {
|
|
| 63 |
- if wb, ok := pipes.Stdout.(interface {
|
|
| 64 |
- CloseWriters() error |
|
| 65 |
- }); ok {
|
|
| 66 |
- defer wb.CloseWriters() |
|
| 67 |
- } |
|
| 68 |
- io.Copy(pipes.Stdout, t.MasterPty) |
|
| 69 |
- }() |
|
| 70 |
- |
|
| 71 |
- if pipes.Stdin != nil {
|
|
| 72 |
- command.Stdin = t.SlavePty |
|
| 73 |
- command.SysProcAttr.Setctty = true |
|
| 74 |
- |
|
| 75 |
- go func() {
|
|
| 76 |
- defer pipes.Stdin.Close() |
|
| 77 |
- io.Copy(t.MasterPty, pipes.Stdin) |
|
| 78 |
- }() |
|
| 79 |
- } |
|
| 80 |
- return nil |
|
| 81 |
-} |
|
| 82 |
- |
|
| 83 |
-func (t *TtyConsole) Close() error {
|
|
| 84 |
- t.SlavePty.Close() |
|
| 85 |
- return t.MasterPty.Close() |
|
| 86 |
-} |
|
| 87 |
- |
|
| 88 |
-type StdConsole struct {
|
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
|
|
| 92 |
- std := &StdConsole{}
|
|
| 93 |
- |
|
| 94 |
- if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
|
|
| 95 |
- return nil, err |
|
| 96 |
- } |
|
| 97 |
- return std, nil |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
|
| 101 |
- command.Stdout = pipes.Stdout |
|
| 102 |
- command.Stderr = pipes.Stderr |
|
| 103 |
- |
|
| 104 |
- if pipes.Stdin != nil {
|
|
| 105 |
- stdin, err := command.StdinPipe() |
|
| 106 |
- if err != nil {
|
|
| 107 |
- return err |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- go func() {
|
|
| 111 |
- defer stdin.Close() |
|
| 112 |
- io.Copy(stdin, pipes.Stdin) |
|
| 113 |
- }() |
|
| 114 |
- } |
|
| 115 |
- return nil |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-func (s *StdConsole) Resize(h, w int) error {
|
|
| 119 |
- // we do not need to reside a non tty |
|
| 120 |
- return nil |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-func (s *StdConsole) Close() error {
|
|
| 124 |
- // nothing to close here |
|
| 125 |
- return nil |
|
| 126 |
-} |
| ... | ... |
@@ -6,7 +6,7 @@ import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"github.com/dotcloud/docker/archive" |
| 8 | 8 |
"github.com/dotcloud/docker/engine" |
| 9 |
- "github.com/dotcloud/docker/execdriver" |
|
| 9 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 10 | 10 |
"github.com/dotcloud/docker/graphdriver" |
| 11 | 11 |
"github.com/dotcloud/docker/image" |
| 12 | 12 |
"github.com/dotcloud/docker/links" |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,135 @@ |
| 0 |
+package execdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "io" |
|
| 5 |
+ "os" |
|
| 6 |
+ "os/exec" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ ErrNotRunning = errors.New("Process could not be started")
|
|
| 11 |
+ ErrWaitTimeoutReached = errors.New("Wait timeout reached")
|
|
| 12 |
+ ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function")
|
|
| 13 |
+ ErrDriverNotFound = errors.New("The requested docker init has not been found")
|
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+var dockerInitFcts map[string]InitFunc |
|
| 17 |
+ |
|
| 18 |
+type ( |
|
| 19 |
+ StartCallback func(*Command) |
|
| 20 |
+ InitFunc func(i *InitArgs) error |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+func RegisterInitFunc(name string, fct InitFunc) error {
|
|
| 24 |
+ if dockerInitFcts == nil {
|
|
| 25 |
+ dockerInitFcts = make(map[string]InitFunc) |
|
| 26 |
+ } |
|
| 27 |
+ if _, ok := dockerInitFcts[name]; ok {
|
|
| 28 |
+ return ErrDriverAlreadyRegistered |
|
| 29 |
+ } |
|
| 30 |
+ dockerInitFcts[name] = fct |
|
| 31 |
+ return nil |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func GetInitFunc(name string) (InitFunc, error) {
|
|
| 35 |
+ fct, ok := dockerInitFcts[name] |
|
| 36 |
+ if !ok {
|
|
| 37 |
+ return nil, ErrDriverNotFound |
|
| 38 |
+ } |
|
| 39 |
+ return fct, nil |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Args provided to the init function for a driver |
|
| 43 |
+type InitArgs struct {
|
|
| 44 |
+ User string |
|
| 45 |
+ Gateway string |
|
| 46 |
+ Ip string |
|
| 47 |
+ WorkDir string |
|
| 48 |
+ Privileged bool |
|
| 49 |
+ Env []string |
|
| 50 |
+ Args []string |
|
| 51 |
+ Mtu int |
|
| 52 |
+ Driver string |
|
| 53 |
+ Console string |
|
| 54 |
+ Pipe int |
|
| 55 |
+ Root string |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+// Driver specific information based on |
|
| 59 |
+// processes registered with the driver |
|
| 60 |
+type Info interface {
|
|
| 61 |
+ IsRunning() bool |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// Terminal in an interface for drivers to implement |
|
| 65 |
+// if they want to support Close and Resize calls from |
|
| 66 |
+// the core |
|
| 67 |
+type Terminal interface {
|
|
| 68 |
+ io.Closer |
|
| 69 |
+ Resize(height, width int) error |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+type TtyTerminal interface {
|
|
| 73 |
+ Master() *os.File |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+type Driver interface {
|
|
| 77 |
+ Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code |
|
| 78 |
+ Kill(c *Command, sig int) error |
|
| 79 |
+ Name() string // Driver name |
|
| 80 |
+ Info(id string) Info // "temporary" hack (until we move state from core to plugins) |
|
| 81 |
+ GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container. |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// Network settings of the container |
|
| 85 |
+type Network struct {
|
|
| 86 |
+ Gateway string `json:"gateway"` |
|
| 87 |
+ IPAddress string `json:"ip"` |
|
| 88 |
+ Bridge string `json:"bridge"` |
|
| 89 |
+ IPPrefixLen int `json:"ip_prefix_len"` |
|
| 90 |
+ Mtu int `json:"mtu"` |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+type Resources struct {
|
|
| 94 |
+ Memory int64 `json:"memory"` |
|
| 95 |
+ MemorySwap int64 `json:"memory_swap"` |
|
| 96 |
+ CpuShares int64 `json:"cpu_shares"` |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+type Mount struct {
|
|
| 100 |
+ Source string `json:"source"` |
|
| 101 |
+ Destination string `json:"destination"` |
|
| 102 |
+ Writable bool `json:"writable"` |
|
| 103 |
+ Private bool `json:"private"` |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// Process wrapps an os/exec.Cmd to add more metadata |
|
| 107 |
+type Command struct {
|
|
| 108 |
+ exec.Cmd `json:"-"` |
|
| 109 |
+ |
|
| 110 |
+ ID string `json:"id"` |
|
| 111 |
+ Privileged bool `json:"privileged"` |
|
| 112 |
+ User string `json:"user"` |
|
| 113 |
+ Rootfs string `json:"rootfs"` // root fs of the container |
|
| 114 |
+ InitPath string `json:"initpath"` // dockerinit |
|
| 115 |
+ Entrypoint string `json:"entrypoint"` |
|
| 116 |
+ Arguments []string `json:"arguments"` |
|
| 117 |
+ WorkingDir string `json:"working_dir"` |
|
| 118 |
+ ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver |
|
| 119 |
+ Tty bool `json:"tty"` |
|
| 120 |
+ Network *Network `json:"network"` // if network is nil then networking is disabled |
|
| 121 |
+ Config []string `json:"config"` // generic values that specific drivers can consume |
|
| 122 |
+ Resources *Resources `json:"resources"` |
|
| 123 |
+ Mounts []Mount `json:"mounts"` |
|
| 124 |
+ |
|
| 125 |
+ Terminal Terminal `json:"-"` // standard or tty terminal |
|
| 126 |
+ Console string `json:"-"` // dev/console path |
|
| 127 |
+ ContainerPid int `json:"container_pid"` // the pid for the process inside a container |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+// Return the pid of the process |
|
| 131 |
+// If the process is nil -1 will be returned |
|
| 132 |
+func (c *Command) Pid() int {
|
|
| 133 |
+ return c.ContainerPid |
|
| 134 |
+} |
| 0 | 135 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,23 @@ |
| 0 |
+package execdrivers |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/execdriver/lxc" |
|
| 6 |
+ "github.com/dotcloud/docker/runtime/execdriver/native" |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/sysinfo" |
|
| 8 |
+ "path" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
|
|
| 12 |
+ switch name {
|
|
| 13 |
+ case "lxc": |
|
| 14 |
+ // we want to five the lxc driver the full docker root because it needs |
|
| 15 |
+ // to access and write config and template files in /var/lib/docker/containers/* |
|
| 16 |
+ // to be backwards compatible |
|
| 17 |
+ return lxc.NewDriver(root, sysInfo.AppArmor) |
|
| 18 |
+ case "native": |
|
| 19 |
+ return native.NewDriver(path.Join(root, "execdriver", "native"), initPath) |
|
| 20 |
+ } |
|
| 21 |
+ return nil, fmt.Errorf("unknown exec driver %s", name)
|
|
| 22 |
+} |
| 0 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,396 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/pkg/cgroups" |
|
| 6 |
+ "github.com/dotcloud/docker/utils" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "log" |
|
| 9 |
+ "os" |
|
| 10 |
+ "os/exec" |
|
| 11 |
+ "path" |
|
| 12 |
+ "path/filepath" |
|
| 13 |
+ "strconv" |
|
| 14 |
+ "strings" |
|
| 15 |
+ "syscall" |
|
| 16 |
+ "time" |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+const DriverName = "lxc" |
|
| 20 |
+ |
|
| 21 |
+func init() {
|
|
| 22 |
+ execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 23 |
+ if err := setupEnv(args); err != nil {
|
|
| 24 |
+ return err |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if err := setupHostname(args); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ if err := setupNetworking(args); err != nil {
|
|
| 32 |
+ return err |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ if err := setupCapabilities(args); err != nil {
|
|
| 36 |
+ return err |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ if err := setupWorkingDirectory(args); err != nil {
|
|
| 40 |
+ return err |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ if err := changeUser(args); err != nil {
|
|
| 44 |
+ return err |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ path, err := exec.LookPath(args.Args[0]) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ log.Printf("Unable to locate %v", args.Args[0])
|
|
| 50 |
+ os.Exit(127) |
|
| 51 |
+ } |
|
| 52 |
+ if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
|
|
| 53 |
+ return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
|
|
| 54 |
+ } |
|
| 55 |
+ panic("Unreachable")
|
|
| 56 |
+ }) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+type driver struct {
|
|
| 60 |
+ root string // root path for the driver to use |
|
| 61 |
+ apparmor bool |
|
| 62 |
+ sharedRoot bool |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func NewDriver(root string, apparmor bool) (*driver, error) {
|
|
| 66 |
+ // setup unconfined symlink |
|
| 67 |
+ if err := linkLxcStart(root); err != nil {
|
|
| 68 |
+ return nil, err |
|
| 69 |
+ } |
|
| 70 |
+ return &driver{
|
|
| 71 |
+ apparmor: apparmor, |
|
| 72 |
+ root: root, |
|
| 73 |
+ sharedRoot: rootIsShared(), |
|
| 74 |
+ }, nil |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func (d *driver) Name() string {
|
|
| 78 |
+ version := d.version() |
|
| 79 |
+ return fmt.Sprintf("%s-%s", DriverName, version)
|
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 83 |
+ if err := execdriver.SetTerminal(c, pipes); err != nil {
|
|
| 84 |
+ return -1, err |
|
| 85 |
+ } |
|
| 86 |
+ configPath, err := d.generateLXCConfig(c) |
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ return -1, err |
|
| 89 |
+ } |
|
| 90 |
+ params := []string{
|
|
| 91 |
+ "lxc-start", |
|
| 92 |
+ "-n", c.ID, |
|
| 93 |
+ "-f", configPath, |
|
| 94 |
+ "--", |
|
| 95 |
+ c.InitPath, |
|
| 96 |
+ "-driver", |
|
| 97 |
+ DriverName, |
|
| 98 |
+ } |
|
| 99 |
+ |
|
| 100 |
+ if c.Network != nil {
|
|
| 101 |
+ params = append(params, |
|
| 102 |
+ "-g", c.Network.Gateway, |
|
| 103 |
+ "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
| 104 |
+ "-mtu", strconv.Itoa(c.Network.Mtu), |
|
| 105 |
+ ) |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ if c.User != "" {
|
|
| 109 |
+ params = append(params, "-u", c.User) |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ if c.Privileged {
|
|
| 113 |
+ if d.apparmor {
|
|
| 114 |
+ params[0] = path.Join(d.root, "lxc-start-unconfined") |
|
| 115 |
+ |
|
| 116 |
+ } |
|
| 117 |
+ params = append(params, "-privileged") |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ if c.WorkingDir != "" {
|
|
| 121 |
+ params = append(params, "-w", c.WorkingDir) |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ params = append(params, "--", c.Entrypoint) |
|
| 125 |
+ params = append(params, c.Arguments...) |
|
| 126 |
+ |
|
| 127 |
+ if d.sharedRoot {
|
|
| 128 |
+ // lxc-start really needs / to be non-shared, or all kinds of stuff break |
|
| 129 |
+ // when lxc-start unmount things and those unmounts propagate to the main |
|
| 130 |
+ // mount namespace. |
|
| 131 |
+ // What we really want is to clone into a new namespace and then |
|
| 132 |
+ // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork |
|
| 133 |
+ // without exec in go we have to do this horrible shell hack... |
|
| 134 |
+ shellString := |
|
| 135 |
+ "mount --make-rslave /; exec " + |
|
| 136 |
+ utils.ShellQuoteArguments(params) |
|
| 137 |
+ |
|
| 138 |
+ params = []string{
|
|
| 139 |
+ "unshare", "-m", "--", "/bin/sh", "-c", shellString, |
|
| 140 |
+ } |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ var ( |
|
| 144 |
+ name = params[0] |
|
| 145 |
+ arg = params[1:] |
|
| 146 |
+ ) |
|
| 147 |
+ aname, err := exec.LookPath(name) |
|
| 148 |
+ if err != nil {
|
|
| 149 |
+ aname = name |
|
| 150 |
+ } |
|
| 151 |
+ c.Path = aname |
|
| 152 |
+ c.Args = append([]string{name}, arg...)
|
|
| 153 |
+ |
|
| 154 |
+ if err := c.Start(); err != nil {
|
|
| 155 |
+ return -1, err |
|
| 156 |
+ } |
|
| 157 |
+ |
|
| 158 |
+ var ( |
|
| 159 |
+ waitErr error |
|
| 160 |
+ waitLock = make(chan struct{})
|
|
| 161 |
+ ) |
|
| 162 |
+ go func() {
|
|
| 163 |
+ if err := c.Wait(); err != nil {
|
|
| 164 |
+ if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
|
|
| 165 |
+ waitErr = err |
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ close(waitLock) |
|
| 169 |
+ }() |
|
| 170 |
+ |
|
| 171 |
+ // Poll lxc for RUNNING status |
|
| 172 |
+ pid, err := d.waitForStart(c, waitLock) |
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ if c.Process != nil {
|
|
| 175 |
+ c.Process.Kill() |
|
| 176 |
+ } |
|
| 177 |
+ return -1, err |
|
| 178 |
+ } |
|
| 179 |
+ c.ContainerPid = pid |
|
| 180 |
+ |
|
| 181 |
+ if startCallback != nil {
|
|
| 182 |
+ startCallback(c) |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ <-waitLock |
|
| 186 |
+ |
|
| 187 |
+ return getExitCode(c), waitErr |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+/// Return the exit code of the process |
|
| 191 |
+// if the process has not exited -1 will be returned |
|
| 192 |
+func getExitCode(c *execdriver.Command) int {
|
|
| 193 |
+ if c.ProcessState == nil {
|
|
| 194 |
+ return -1 |
|
| 195 |
+ } |
|
| 196 |
+ return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() |
|
| 197 |
+} |
|
| 198 |
+ |
|
| 199 |
+func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
|
| 200 |
+ return KillLxc(c.ID, sig) |
|
| 201 |
+} |
|
| 202 |
+ |
|
| 203 |
+func (d *driver) version() string {
|
|
| 204 |
+ var ( |
|
| 205 |
+ version string |
|
| 206 |
+ output []byte |
|
| 207 |
+ err error |
|
| 208 |
+ ) |
|
| 209 |
+ if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
|
|
| 210 |
+ output, err = exec.Command("lxc-version").CombinedOutput()
|
|
| 211 |
+ } else {
|
|
| 212 |
+ output, err = exec.Command("lxc-start", "--version").CombinedOutput()
|
|
| 213 |
+ } |
|
| 214 |
+ if err == nil {
|
|
| 215 |
+ version = strings.TrimSpace(string(output)) |
|
| 216 |
+ if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
|
|
| 217 |
+ version = strings.TrimSpace(parts[1]) |
|
| 218 |
+ } |
|
| 219 |
+ } |
|
| 220 |
+ return version |
|
| 221 |
+} |
|
| 222 |
+ |
|
| 223 |
+func KillLxc(id string, sig int) error {
|
|
| 224 |
+ var ( |
|
| 225 |
+ err error |
|
| 226 |
+ output []byte |
|
| 227 |
+ ) |
|
| 228 |
+ _, err = exec.LookPath("lxc-kill")
|
|
| 229 |
+ if err == nil {
|
|
| 230 |
+ output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
|
|
| 231 |
+ } else {
|
|
| 232 |
+ output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
|
|
| 233 |
+ } |
|
| 234 |
+ if err != nil {
|
|
| 235 |
+ return fmt.Errorf("Err: %s Output: %s", err, output)
|
|
| 236 |
+ } |
|
| 237 |
+ return nil |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+// wait for the process to start and return the pid for the process |
|
| 241 |
+func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
|
|
| 242 |
+ var ( |
|
| 243 |
+ err error |
|
| 244 |
+ output []byte |
|
| 245 |
+ ) |
|
| 246 |
+ // We wait for the container to be fully running. |
|
| 247 |
+ // Timeout after 5 seconds. In case of broken pipe, just retry. |
|
| 248 |
+ // Note: The container can run and finish correctly before |
|
| 249 |
+ // the end of this loop |
|
| 250 |
+ for now := time.Now(); time.Since(now) < 5*time.Second; {
|
|
| 251 |
+ select {
|
|
| 252 |
+ case <-waitLock: |
|
| 253 |
+ // If the process dies while waiting for it, just return |
|
| 254 |
+ return -1, nil |
|
| 255 |
+ default: |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ output, err = d.getInfo(c.ID) |
|
| 259 |
+ if err != nil {
|
|
| 260 |
+ output, err = d.getInfo(c.ID) |
|
| 261 |
+ if err != nil {
|
|
| 262 |
+ return -1, err |
|
| 263 |
+ } |
|
| 264 |
+ } |
|
| 265 |
+ info, err := parseLxcInfo(string(output)) |
|
| 266 |
+ if err != nil {
|
|
| 267 |
+ return -1, err |
|
| 268 |
+ } |
|
| 269 |
+ if info.Running {
|
|
| 270 |
+ return info.Pid, nil |
|
| 271 |
+ } |
|
| 272 |
+ time.Sleep(50 * time.Millisecond) |
|
| 273 |
+ } |
|
| 274 |
+ return -1, execdriver.ErrNotRunning |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+func (d *driver) getInfo(id string) ([]byte, error) {
|
|
| 278 |
+ return exec.Command("lxc-info", "-n", id).CombinedOutput()
|
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+type info struct {
|
|
| 282 |
+ ID string |
|
| 283 |
+ driver *driver |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+func (i *info) IsRunning() bool {
|
|
| 287 |
+ var running bool |
|
| 288 |
+ |
|
| 289 |
+ output, err := i.driver.getInfo(i.ID) |
|
| 290 |
+ if err != nil {
|
|
| 291 |
+ utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
|
|
| 292 |
+ return false |
|
| 293 |
+ } |
|
| 294 |
+ if strings.Contains(string(output), "RUNNING") {
|
|
| 295 |
+ running = true |
|
| 296 |
+ } |
|
| 297 |
+ return running |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+func (d *driver) Info(id string) execdriver.Info {
|
|
| 301 |
+ return &info{
|
|
| 302 |
+ ID: id, |
|
| 303 |
+ driver: d, |
|
| 304 |
+ } |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
| 308 |
+ pids := []int{}
|
|
| 309 |
+ |
|
| 310 |
+ // cpu is chosen because it is the only non optional subsystem in cgroups |
|
| 311 |
+ subsystem := "cpu" |
|
| 312 |
+ cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) |
|
| 313 |
+ if err != nil {
|
|
| 314 |
+ return pids, err |
|
| 315 |
+ } |
|
| 316 |
+ |
|
| 317 |
+ cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) |
|
| 318 |
+ if err != nil {
|
|
| 319 |
+ return pids, err |
|
| 320 |
+ } |
|
| 321 |
+ |
|
| 322 |
+ filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks") |
|
| 323 |
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
| 324 |
+ // With more recent lxc versions use, cgroup will be in lxc/ |
|
| 325 |
+ filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks") |
|
| 326 |
+ } |
|
| 327 |
+ |
|
| 328 |
+ output, err := ioutil.ReadFile(filename) |
|
| 329 |
+ if err != nil {
|
|
| 330 |
+ return pids, err |
|
| 331 |
+ } |
|
| 332 |
+ for _, p := range strings.Split(string(output), "\n") {
|
|
| 333 |
+ if len(p) == 0 {
|
|
| 334 |
+ continue |
|
| 335 |
+ } |
|
| 336 |
+ pid, err := strconv.Atoi(p) |
|
| 337 |
+ if err != nil {
|
|
| 338 |
+ return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
| 339 |
+ } |
|
| 340 |
+ pids = append(pids, pid) |
|
| 341 |
+ } |
|
| 342 |
+ return pids, nil |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+func linkLxcStart(root string) error {
|
|
| 346 |
+ sourcePath, err := exec.LookPath("lxc-start")
|
|
| 347 |
+ if err != nil {
|
|
| 348 |
+ return err |
|
| 349 |
+ } |
|
| 350 |
+ targetPath := path.Join(root, "lxc-start-unconfined") |
|
| 351 |
+ |
|
| 352 |
+ if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
|
|
| 353 |
+ return err |
|
| 354 |
+ } else if err == nil {
|
|
| 355 |
+ if err := os.Remove(targetPath); err != nil {
|
|
| 356 |
+ return err |
|
| 357 |
+ } |
|
| 358 |
+ } |
|
| 359 |
+ return os.Symlink(sourcePath, targetPath) |
|
| 360 |
+} |
|
| 361 |
+ |
|
| 362 |
+// TODO: This can be moved to the mountinfo reader in the mount pkg |
|
| 363 |
+func rootIsShared() bool {
|
|
| 364 |
+ if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
|
|
| 365 |
+ for _, line := range strings.Split(string(data), "\n") {
|
|
| 366 |
+ cols := strings.Split(line, " ") |
|
| 367 |
+ if len(cols) >= 6 && cols[4] == "/" {
|
|
| 368 |
+ return strings.HasPrefix(cols[6], "shared") |
|
| 369 |
+ } |
|
| 370 |
+ } |
|
| 371 |
+ } |
|
| 372 |
+ |
|
| 373 |
+ // No idea, probably safe to assume so |
|
| 374 |
+ return true |
|
| 375 |
+} |
|
| 376 |
+ |
|
| 377 |
+func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
|
|
| 378 |
+ root := path.Join(d.root, "containers", c.ID, "config.lxc") |
|
| 379 |
+ fo, err := os.Create(root) |
|
| 380 |
+ if err != nil {
|
|
| 381 |
+ return "", err |
|
| 382 |
+ } |
|
| 383 |
+ defer fo.Close() |
|
| 384 |
+ |
|
| 385 |
+ if err := LxcTemplateCompiled.Execute(fo, struct {
|
|
| 386 |
+ *execdriver.Command |
|
| 387 |
+ AppArmor bool |
|
| 388 |
+ }{
|
|
| 389 |
+ Command: c, |
|
| 390 |
+ AppArmor: d.apparmor, |
|
| 391 |
+ }); err != nil {
|
|
| 392 |
+ return "", err |
|
| 393 |
+ } |
|
| 394 |
+ return root, nil |
|
| 395 |
+} |
| 0 | 396 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,50 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "strings" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ ErrCannotParse = errors.New("cannot parse raw input")
|
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type lxcInfo struct {
|
|
| 14 |
+ Running bool |
|
| 15 |
+ Pid int |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func parseLxcInfo(raw string) (*lxcInfo, error) {
|
|
| 19 |
+ if raw == "" {
|
|
| 20 |
+ return nil, ErrCannotParse |
|
| 21 |
+ } |
|
| 22 |
+ var ( |
|
| 23 |
+ err error |
|
| 24 |
+ s = bufio.NewScanner(strings.NewReader(raw)) |
|
| 25 |
+ info = &lxcInfo{}
|
|
| 26 |
+ ) |
|
| 27 |
+ for s.Scan() {
|
|
| 28 |
+ text := s.Text() |
|
| 29 |
+ |
|
| 30 |
+ if s.Err() != nil {
|
|
| 31 |
+ return nil, s.Err() |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ parts := strings.Split(text, ":") |
|
| 35 |
+ if len(parts) < 2 {
|
|
| 36 |
+ continue |
|
| 37 |
+ } |
|
| 38 |
+ switch strings.ToLower(strings.TrimSpace(parts[0])) {
|
|
| 39 |
+ case "state": |
|
| 40 |
+ info.Running = strings.TrimSpace(parts[1]) == "RUNNING" |
|
| 41 |
+ case "pid": |
|
| 42 |
+ info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1])) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return nil, err |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ } |
|
| 48 |
+ return info, nil |
|
| 49 |
+} |
| 0 | 50 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,36 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+func TestParseRunningInfo(t *testing.T) {
|
|
| 7 |
+ raw := ` |
|
| 8 |
+ state: RUNNING |
|
| 9 |
+ pid: 50` |
|
| 10 |
+ |
|
| 11 |
+ info, err := parseLxcInfo(raw) |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ t.Fatal(err) |
|
| 14 |
+ } |
|
| 15 |
+ if !info.Running {
|
|
| 16 |
+ t.Fatal("info should return a running state")
|
|
| 17 |
+ } |
|
| 18 |
+ if info.Pid != 50 {
|
|
| 19 |
+ t.Fatalf("info should have pid 50 got %d", info.Pid)
|
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func TestEmptyInfo(t *testing.T) {
|
|
| 24 |
+ _, err := parseLxcInfo("")
|
|
| 25 |
+ if err == nil {
|
|
| 26 |
+ t.Fatal("error should not be nil")
|
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func TestBadInfo(t *testing.T) {
|
|
| 31 |
+ _, err := parseLxcInfo("state")
|
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ t.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+} |
| 0 | 36 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,176 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/user" |
|
| 8 |
+ "github.com/syndtr/gocapability/capability" |
|
| 9 |
+ "io/ioutil" |
|
| 10 |
+ "net" |
|
| 11 |
+ "os" |
|
| 12 |
+ "strings" |
|
| 13 |
+ "syscall" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+// Clear environment pollution introduced by lxc-start |
|
| 17 |
+func setupEnv(args *execdriver.InitArgs) error {
|
|
| 18 |
+ // Get env |
|
| 19 |
+ var env []string |
|
| 20 |
+ content, err := ioutil.ReadFile(".dockerenv")
|
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ return fmt.Errorf("Unable to load environment variables: %v", err)
|
|
| 23 |
+ } |
|
| 24 |
+ if err := json.Unmarshal(content, &env); err != nil {
|
|
| 25 |
+ return fmt.Errorf("Unable to unmarshal environment variables: %v", err)
|
|
| 26 |
+ } |
|
| 27 |
+ // Propagate the plugin-specific container env variable |
|
| 28 |
+ env = append(env, "container="+os.Getenv("container"))
|
|
| 29 |
+ |
|
| 30 |
+ args.Env = env |
|
| 31 |
+ |
|
| 32 |
+ os.Clearenv() |
|
| 33 |
+ for _, kv := range args.Env {
|
|
| 34 |
+ parts := strings.SplitN(kv, "=", 2) |
|
| 35 |
+ if len(parts) == 1 {
|
|
| 36 |
+ parts = append(parts, "") |
|
| 37 |
+ } |
|
| 38 |
+ os.Setenv(parts[0], parts[1]) |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return nil |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func setupHostname(args *execdriver.InitArgs) error {
|
|
| 45 |
+ hostname := getEnv(args, "HOSTNAME") |
|
| 46 |
+ if hostname == "" {
|
|
| 47 |
+ return nil |
|
| 48 |
+ } |
|
| 49 |
+ return setHostname(hostname) |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// Setup networking |
|
| 53 |
+func setupNetworking(args *execdriver.InitArgs) error {
|
|
| 54 |
+ if args.Ip != "" {
|
|
| 55 |
+ // eth0 |
|
| 56 |
+ iface, err := net.InterfaceByName("eth0")
|
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 59 |
+ } |
|
| 60 |
+ ip, ipNet, err := net.ParseCIDR(args.Ip) |
|
| 61 |
+ if err != nil {
|
|
| 62 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 63 |
+ } |
|
| 64 |
+ if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
|
| 65 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 66 |
+ } |
|
| 67 |
+ if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
|
|
| 68 |
+ return fmt.Errorf("Unable to set MTU: %v", err)
|
|
| 69 |
+ } |
|
| 70 |
+ if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 71 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 72 |
+ } |
|
| 73 |
+ |
|
| 74 |
+ // loopback |
|
| 75 |
+ iface, err = net.InterfaceByName("lo")
|
|
| 76 |
+ if err != nil {
|
|
| 77 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 78 |
+ } |
|
| 79 |
+ if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 80 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 81 |
+ } |
|
| 82 |
+ } |
|
| 83 |
+ if args.Gateway != "" {
|
|
| 84 |
+ gw := net.ParseIP(args.Gateway) |
|
| 85 |
+ if gw == nil {
|
|
| 86 |
+ return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
|
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ if err := netlink.AddDefaultGw(gw); err != nil {
|
|
| 90 |
+ return fmt.Errorf("Unable to set up networking: %v", err)
|
|
| 91 |
+ } |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ return nil |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+// Setup working directory |
|
| 98 |
+func setupWorkingDirectory(args *execdriver.InitArgs) error {
|
|
| 99 |
+ if args.WorkDir == "" {
|
|
| 100 |
+ return nil |
|
| 101 |
+ } |
|
| 102 |
+ if err := syscall.Chdir(args.WorkDir); err != nil {
|
|
| 103 |
+ return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
|
|
| 104 |
+ } |
|
| 105 |
+ return nil |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// Takes care of dropping privileges to the desired user |
|
| 109 |
+func changeUser(args *execdriver.InitArgs) error {
|
|
| 110 |
+ uid, gid, suppGids, err := user.GetUserGroupSupplementary( |
|
| 111 |
+ args.User, |
|
| 112 |
+ syscall.Getuid(), syscall.Getgid(), |
|
| 113 |
+ ) |
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ return err |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ if err := syscall.Setgroups(suppGids); err != nil {
|
|
| 119 |
+ return fmt.Errorf("Setgroups failed: %v", err)
|
|
| 120 |
+ } |
|
| 121 |
+ if err := syscall.Setgid(gid); err != nil {
|
|
| 122 |
+ return fmt.Errorf("Setgid failed: %v", err)
|
|
| 123 |
+ } |
|
| 124 |
+ if err := syscall.Setuid(uid); err != nil {
|
|
| 125 |
+ return fmt.Errorf("Setuid failed: %v", err)
|
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ return nil |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func setupCapabilities(args *execdriver.InitArgs) error {
|
|
| 132 |
+ if args.Privileged {
|
|
| 133 |
+ return nil |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ drop := []capability.Cap{
|
|
| 137 |
+ capability.CAP_SETPCAP, |
|
| 138 |
+ capability.CAP_SYS_MODULE, |
|
| 139 |
+ capability.CAP_SYS_RAWIO, |
|
| 140 |
+ capability.CAP_SYS_PACCT, |
|
| 141 |
+ capability.CAP_SYS_ADMIN, |
|
| 142 |
+ capability.CAP_SYS_NICE, |
|
| 143 |
+ capability.CAP_SYS_RESOURCE, |
|
| 144 |
+ capability.CAP_SYS_TIME, |
|
| 145 |
+ capability.CAP_SYS_TTY_CONFIG, |
|
| 146 |
+ capability.CAP_MKNOD, |
|
| 147 |
+ capability.CAP_AUDIT_WRITE, |
|
| 148 |
+ capability.CAP_AUDIT_CONTROL, |
|
| 149 |
+ capability.CAP_MAC_OVERRIDE, |
|
| 150 |
+ capability.CAP_MAC_ADMIN, |
|
| 151 |
+ capability.CAP_NET_ADMIN, |
|
| 152 |
+ } |
|
| 153 |
+ |
|
| 154 |
+ c, err := capability.NewPid(os.Getpid()) |
|
| 155 |
+ if err != nil {
|
|
| 156 |
+ return err |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ c.Unset(capability.CAPS|capability.BOUNDS, drop...) |
|
| 160 |
+ |
|
| 161 |
+ if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
|
|
| 162 |
+ return err |
|
| 163 |
+ } |
|
| 164 |
+ return nil |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+func getEnv(args *execdriver.InitArgs, key string) string {
|
|
| 168 |
+ for _, kv := range args.Env {
|
|
| 169 |
+ parts := strings.SplitN(kv, "=", 2) |
|
| 170 |
+ if parts[0] == key && len(parts) == 2 {
|
|
| 171 |
+ return parts[1] |
|
| 172 |
+ } |
|
| 173 |
+ } |
|
| 174 |
+ return "" |
|
| 175 |
+} |
| 0 | 7 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,155 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "text/template" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+const LxcTemplate = ` |
|
| 9 |
+{{if .Network}}
|
|
| 10 |
+# network configuration |
|
| 11 |
+lxc.network.type = veth |
|
| 12 |
+lxc.network.link = {{.Network.Bridge}}
|
|
| 13 |
+lxc.network.name = eth0 |
|
| 14 |
+lxc.network.mtu = {{.Network.Mtu}}
|
|
| 15 |
+{{else}}
|
|
| 16 |
+# network is disabled (-n=false) |
|
| 17 |
+lxc.network.type = empty |
|
| 18 |
+lxc.network.flags = up |
|
| 19 |
+{{end}}
|
|
| 20 |
+ |
|
| 21 |
+# root filesystem |
|
| 22 |
+{{$ROOTFS := .Rootfs}}
|
|
| 23 |
+lxc.rootfs = {{$ROOTFS}}
|
|
| 24 |
+ |
|
| 25 |
+# use a dedicated pts for the container (and limit the number of pseudo terminal |
|
| 26 |
+# available) |
|
| 27 |
+lxc.pts = 1024 |
|
| 28 |
+ |
|
| 29 |
+# disable the main console |
|
| 30 |
+lxc.console = none |
|
| 31 |
+ |
|
| 32 |
+# no controlling tty at all |
|
| 33 |
+lxc.tty = 1 |
|
| 34 |
+ |
|
| 35 |
+{{if .Privileged}}
|
|
| 36 |
+lxc.cgroup.devices.allow = a |
|
| 37 |
+{{else}}
|
|
| 38 |
+# no implicit access to devices |
|
| 39 |
+lxc.cgroup.devices.deny = a |
|
| 40 |
+ |
|
| 41 |
+# /dev/null and zero |
|
| 42 |
+lxc.cgroup.devices.allow = c 1:3 rwm |
|
| 43 |
+lxc.cgroup.devices.allow = c 1:5 rwm |
|
| 44 |
+ |
|
| 45 |
+# consoles |
|
| 46 |
+lxc.cgroup.devices.allow = c 5:1 rwm |
|
| 47 |
+lxc.cgroup.devices.allow = c 5:0 rwm |
|
| 48 |
+lxc.cgroup.devices.allow = c 4:0 rwm |
|
| 49 |
+lxc.cgroup.devices.allow = c 4:1 rwm |
|
| 50 |
+ |
|
| 51 |
+# /dev/urandom,/dev/random |
|
| 52 |
+lxc.cgroup.devices.allow = c 1:9 rwm |
|
| 53 |
+lxc.cgroup.devices.allow = c 1:8 rwm |
|
| 54 |
+ |
|
| 55 |
+# /dev/pts/ - pts namespaces are "coming soon" |
|
| 56 |
+lxc.cgroup.devices.allow = c 136:* rwm |
|
| 57 |
+lxc.cgroup.devices.allow = c 5:2 rwm |
|
| 58 |
+ |
|
| 59 |
+# tuntap |
|
| 60 |
+lxc.cgroup.devices.allow = c 10:200 rwm |
|
| 61 |
+ |
|
| 62 |
+# fuse |
|
| 63 |
+#lxc.cgroup.devices.allow = c 10:229 rwm |
|
| 64 |
+ |
|
| 65 |
+# rtc |
|
| 66 |
+#lxc.cgroup.devices.allow = c 254:0 rwm |
|
| 67 |
+{{end}}
|
|
| 68 |
+ |
|
| 69 |
+# standard mount point |
|
| 70 |
+# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385 |
|
| 71 |
+lxc.pivotdir = lxc_putold |
|
| 72 |
+ |
|
| 73 |
+# NOTICE: These mounts must be applied within the namespace |
|
| 74 |
+ |
|
| 75 |
+# WARNING: procfs is a known attack vector and should probably be disabled |
|
| 76 |
+# if your userspace allows it. eg. see http://blog.zx2c4.com/749 |
|
| 77 |
+lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
|
| 78 |
+ |
|
| 79 |
+# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 80 |
+# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 81 |
+lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
|
| 82 |
+ |
|
| 83 |
+{{if .Tty}}
|
|
| 84 |
+lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
|
|
| 85 |
+{{end}}
|
|
| 86 |
+ |
|
| 87 |
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
|
| 88 |
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
|
| 89 |
+ |
|
| 90 |
+{{range $value := .Mounts}}
|
|
| 91 |
+{{if $value.Writable}}
|
|
| 92 |
+lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,rw 0 0
|
|
| 93 |
+{{else}}
|
|
| 94 |
+lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,ro 0 0
|
|
| 95 |
+{{end}}
|
|
| 96 |
+{{end}}
|
|
| 97 |
+ |
|
| 98 |
+{{if .Privileged}}
|
|
| 99 |
+{{if .AppArmor}}
|
|
| 100 |
+lxc.aa_profile = unconfined |
|
| 101 |
+{{else}}
|
|
| 102 |
+#lxc.aa_profile = unconfined |
|
| 103 |
+{{end}}
|
|
| 104 |
+{{end}}
|
|
| 105 |
+ |
|
| 106 |
+# limits |
|
| 107 |
+{{if .Resources}}
|
|
| 108 |
+{{if .Resources.Memory}}
|
|
| 109 |
+lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
|
|
| 110 |
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
|
|
| 111 |
+{{with $memSwap := getMemorySwap .Resources}}
|
|
| 112 |
+lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
|
| 113 |
+{{end}}
|
|
| 114 |
+{{end}}
|
|
| 115 |
+{{if .Resources.CpuShares}}
|
|
| 116 |
+lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
|
|
| 117 |
+{{end}}
|
|
| 118 |
+{{end}}
|
|
| 119 |
+ |
|
| 120 |
+{{if .Config}}
|
|
| 121 |
+{{range $value := .Config}}
|
|
| 122 |
+{{$value}}
|
|
| 123 |
+{{end}}
|
|
| 124 |
+{{end}}
|
|
| 125 |
+` |
|
| 126 |
+ |
|
| 127 |
+var LxcTemplateCompiled *template.Template |
|
| 128 |
+ |
|
| 129 |
+// Escape spaces in strings according to the fstab documentation, which is the |
|
| 130 |
+// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab". |
|
| 131 |
+func escapeFstabSpaces(field string) string {
|
|
| 132 |
+ return strings.Replace(field, " ", "\\040", -1) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func getMemorySwap(v *execdriver.Resources) int64 {
|
|
| 136 |
+ // By default, MemorySwap is set to twice the size of RAM. |
|
| 137 |
+ // If you want to omit MemorySwap, set it to `-1'. |
|
| 138 |
+ if v.MemorySwap < 0 {
|
|
| 139 |
+ return 0 |
|
| 140 |
+ } |
|
| 141 |
+ return v.Memory * 2 |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func init() {
|
|
| 145 |
+ var err error |
|
| 146 |
+ funcMap := template.FuncMap{
|
|
| 147 |
+ "getMemorySwap": getMemorySwap, |
|
| 148 |
+ "escapeFstabSpaces": escapeFstabSpaces, |
|
| 149 |
+ } |
|
| 150 |
+ LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
|
| 151 |
+ if err != nil {
|
|
| 152 |
+ panic(err) |
|
| 153 |
+ } |
|
| 154 |
+} |
| 0 | 155 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,125 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "math/rand" |
|
| 8 |
+ "os" |
|
| 9 |
+ "path" |
|
| 10 |
+ "strings" |
|
| 11 |
+ "testing" |
|
| 12 |
+ "time" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func TestLXCConfig(t *testing.T) {
|
|
| 16 |
+ root, err := ioutil.TempDir("", "TestLXCConfig")
|
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ t.Fatal(err) |
|
| 19 |
+ } |
|
| 20 |
+ defer os.RemoveAll(root) |
|
| 21 |
+ |
|
| 22 |
+ os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 23 |
+ |
|
| 24 |
+ // Memory is allocated randomly for testing |
|
| 25 |
+ rand.Seed(time.Now().UTC().UnixNano()) |
|
| 26 |
+ var ( |
|
| 27 |
+ memMin = 33554432 |
|
| 28 |
+ memMax = 536870912 |
|
| 29 |
+ mem = memMin + rand.Intn(memMax-memMin) |
|
| 30 |
+ cpuMin = 100 |
|
| 31 |
+ cpuMax = 10000 |
|
| 32 |
+ cpu = cpuMin + rand.Intn(cpuMax-cpuMin) |
|
| 33 |
+ ) |
|
| 34 |
+ |
|
| 35 |
+ driver, err := NewDriver(root, false) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ t.Fatal(err) |
|
| 38 |
+ } |
|
| 39 |
+ command := &execdriver.Command{
|
|
| 40 |
+ ID: "1", |
|
| 41 |
+ Resources: &execdriver.Resources{
|
|
| 42 |
+ Memory: int64(mem), |
|
| 43 |
+ CpuShares: int64(cpu), |
|
| 44 |
+ }, |
|
| 45 |
+ } |
|
| 46 |
+ p, err := driver.generateLXCConfig(command) |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ t.Fatal(err) |
|
| 49 |
+ } |
|
| 50 |
+ grepFile(t, p, |
|
| 51 |
+ fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
|
| 52 |
+ |
|
| 53 |
+ grepFile(t, p, |
|
| 54 |
+ fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func TestCustomLxcConfig(t *testing.T) {
|
|
| 58 |
+ root, err := ioutil.TempDir("", "TestCustomLxcConfig")
|
|
| 59 |
+ if err != nil {
|
|
| 60 |
+ t.Fatal(err) |
|
| 61 |
+ } |
|
| 62 |
+ defer os.RemoveAll(root) |
|
| 63 |
+ |
|
| 64 |
+ os.MkdirAll(path.Join(root, "containers", "1"), 0777) |
|
| 65 |
+ |
|
| 66 |
+ driver, err := NewDriver(root, false) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ t.Fatal(err) |
|
| 69 |
+ } |
|
| 70 |
+ command := &execdriver.Command{
|
|
| 71 |
+ ID: "1", |
|
| 72 |
+ Privileged: false, |
|
| 73 |
+ Config: []string{
|
|
| 74 |
+ "lxc.utsname = docker", |
|
| 75 |
+ "lxc.cgroup.cpuset.cpus = 0,1", |
|
| 76 |
+ }, |
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ p, err := driver.generateLXCConfig(command) |
|
| 80 |
+ if err != nil {
|
|
| 81 |
+ t.Fatal(err) |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ grepFile(t, p, "lxc.utsname = docker") |
|
| 85 |
+ grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+func grepFile(t *testing.T, path string, pattern string) {
|
|
| 89 |
+ f, err := os.Open(path) |
|
| 90 |
+ if err != nil {
|
|
| 91 |
+ t.Fatal(err) |
|
| 92 |
+ } |
|
| 93 |
+ defer f.Close() |
|
| 94 |
+ r := bufio.NewReader(f) |
|
| 95 |
+ var ( |
|
| 96 |
+ line string |
|
| 97 |
+ ) |
|
| 98 |
+ err = nil |
|
| 99 |
+ for err == nil {
|
|
| 100 |
+ line, err = r.ReadString('\n')
|
|
| 101 |
+ if strings.Contains(line, pattern) == true {
|
|
| 102 |
+ return |
|
| 103 |
+ } |
|
| 104 |
+ } |
|
| 105 |
+ t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
|
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+func TestEscapeFstabSpaces(t *testing.T) {
|
|
| 109 |
+ var testInputs = map[string]string{
|
|
| 110 |
+ " ": "\\040", |
|
| 111 |
+ "": "", |
|
| 112 |
+ "/double space": "/double\\040\\040space", |
|
| 113 |
+ "/some long test string": "/some\\040long\\040test\\040string", |
|
| 114 |
+ "/var/lib/docker": "/var/lib/docker", |
|
| 115 |
+ " leading": "\\040leading", |
|
| 116 |
+ "trailing ": "trailing\\040", |
|
| 117 |
+ } |
|
| 118 |
+ for in, exp := range testInputs {
|
|
| 119 |
+ if out := escapeFstabSpaces(in); exp != out {
|
|
| 120 |
+ t.Logf("Expected %s got %s", exp, out)
|
|
| 121 |
+ t.Fail() |
|
| 122 |
+ } |
|
| 123 |
+ } |
|
| 124 |
+} |
| 0 | 125 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,94 @@ |
| 0 |
+package native |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 5 |
+ "github.com/dotcloud/docker/pkg/cgroups" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 7 |
+ "os" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// createContainer populates and configures the container type with the |
|
| 11 |
+// data provided by the execdriver.Command |
|
| 12 |
+func createContainer(c *execdriver.Command) *libcontainer.Container {
|
|
| 13 |
+ container := getDefaultTemplate() |
|
| 14 |
+ |
|
| 15 |
+ container.Hostname = getEnv("HOSTNAME", c.Env)
|
|
| 16 |
+ container.Tty = c.Tty |
|
| 17 |
+ container.User = c.User |
|
| 18 |
+ container.WorkingDir = c.WorkingDir |
|
| 19 |
+ container.Env = c.Env |
|
| 20 |
+ |
|
| 21 |
+ if c.Network != nil {
|
|
| 22 |
+ container.Networks = []*libcontainer.Network{
|
|
| 23 |
+ {
|
|
| 24 |
+ Mtu: c.Network.Mtu, |
|
| 25 |
+ Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
|
| 26 |
+ Gateway: c.Network.Gateway, |
|
| 27 |
+ Type: "veth", |
|
| 28 |
+ Context: libcontainer.Context{
|
|
| 29 |
+ "prefix": "veth", |
|
| 30 |
+ "bridge": c.Network.Bridge, |
|
| 31 |
+ }, |
|
| 32 |
+ }, |
|
| 33 |
+ } |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ container.Cgroups.Name = c.ID |
|
| 37 |
+ if c.Privileged {
|
|
| 38 |
+ container.CapabilitiesMask = nil |
|
| 39 |
+ container.Cgroups.DeviceAccess = true |
|
| 40 |
+ container.Context["apparmor_profile"] = "unconfined" |
|
| 41 |
+ } |
|
| 42 |
+ if c.Resources != nil {
|
|
| 43 |
+ container.Cgroups.CpuShares = c.Resources.CpuShares |
|
| 44 |
+ container.Cgroups.Memory = c.Resources.Memory |
|
| 45 |
+ container.Cgroups.MemorySwap = c.Resources.MemorySwap |
|
| 46 |
+ } |
|
| 47 |
+ // check to see if we are running in ramdisk to disable pivot root |
|
| 48 |
+ container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
|
| 49 |
+ |
|
| 50 |
+ for _, m := range c.Mounts {
|
|
| 51 |
+ container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
|
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ return container |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// getDefaultTemplate returns the docker default for |
|
| 58 |
+// the libcontainer configuration file |
|
| 59 |
+func getDefaultTemplate() *libcontainer.Container {
|
|
| 60 |
+ return &libcontainer.Container{
|
|
| 61 |
+ CapabilitiesMask: libcontainer.Capabilities{
|
|
| 62 |
+ libcontainer.GetCapability("SETPCAP"),
|
|
| 63 |
+ libcontainer.GetCapability("SYS_MODULE"),
|
|
| 64 |
+ libcontainer.GetCapability("SYS_RAWIO"),
|
|
| 65 |
+ libcontainer.GetCapability("SYS_PACCT"),
|
|
| 66 |
+ libcontainer.GetCapability("SYS_ADMIN"),
|
|
| 67 |
+ libcontainer.GetCapability("SYS_NICE"),
|
|
| 68 |
+ libcontainer.GetCapability("SYS_RESOURCE"),
|
|
| 69 |
+ libcontainer.GetCapability("SYS_TIME"),
|
|
| 70 |
+ libcontainer.GetCapability("SYS_TTY_CONFIG"),
|
|
| 71 |
+ libcontainer.GetCapability("MKNOD"),
|
|
| 72 |
+ libcontainer.GetCapability("AUDIT_WRITE"),
|
|
| 73 |
+ libcontainer.GetCapability("AUDIT_CONTROL"),
|
|
| 74 |
+ libcontainer.GetCapability("MAC_OVERRIDE"),
|
|
| 75 |
+ libcontainer.GetCapability("MAC_ADMIN"),
|
|
| 76 |
+ libcontainer.GetCapability("NET_ADMIN"),
|
|
| 77 |
+ }, |
|
| 78 |
+ Namespaces: libcontainer.Namespaces{
|
|
| 79 |
+ libcontainer.GetNamespace("NEWNS"),
|
|
| 80 |
+ libcontainer.GetNamespace("NEWUTS"),
|
|
| 81 |
+ libcontainer.GetNamespace("NEWIPC"),
|
|
| 82 |
+ libcontainer.GetNamespace("NEWPID"),
|
|
| 83 |
+ libcontainer.GetNamespace("NEWNET"),
|
|
| 84 |
+ }, |
|
| 85 |
+ Cgroups: &cgroups.Cgroup{
|
|
| 86 |
+ Parent: "docker", |
|
| 87 |
+ DeviceAccess: false, |
|
| 88 |
+ }, |
|
| 89 |
+ Context: libcontainer.Context{
|
|
| 90 |
+ "apparmor_profile": "docker-default", |
|
| 91 |
+ }, |
|
| 92 |
+ } |
|
| 93 |
+} |
| 0 | 94 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,266 @@ |
| 0 |
+package native |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/cgroups" |
|
| 7 |
+ "github.com/dotcloud/docker/pkg/libcontainer" |
|
| 8 |
+ "github.com/dotcloud/docker/pkg/libcontainer/apparmor" |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/libcontainer/nsinit" |
|
| 10 |
+ "github.com/dotcloud/docker/pkg/system" |
|
| 11 |
+ "io" |
|
| 12 |
+ "io/ioutil" |
|
| 13 |
+ "log" |
|
| 14 |
+ "os" |
|
| 15 |
+ "os/exec" |
|
| 16 |
+ "path/filepath" |
|
| 17 |
+ "strconv" |
|
| 18 |
+ "strings" |
|
| 19 |
+ "syscall" |
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+const ( |
|
| 23 |
+ DriverName = "native" |
|
| 24 |
+ Version = "0.1" |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+func init() {
|
|
| 28 |
+ execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
|
| 29 |
+ var ( |
|
| 30 |
+ container *libcontainer.Container |
|
| 31 |
+ ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
|
|
| 32 |
+ ) |
|
| 33 |
+ f, err := os.Open(filepath.Join(args.Root, "container.json")) |
|
| 34 |
+ if err != nil {
|
|
| 35 |
+ return err |
|
| 36 |
+ } |
|
| 37 |
+ if err := json.NewDecoder(f).Decode(&container); err != nil {
|
|
| 38 |
+ f.Close() |
|
| 39 |
+ return err |
|
| 40 |
+ } |
|
| 41 |
+ f.Close() |
|
| 42 |
+ |
|
| 43 |
+ cwd, err := os.Getwd() |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return err |
|
| 46 |
+ } |
|
| 47 |
+ syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe)) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
|
|
| 52 |
+ return err |
|
| 53 |
+ } |
|
| 54 |
+ return nil |
|
| 55 |
+ }) |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+type driver struct {
|
|
| 59 |
+ root string |
|
| 60 |
+ initPath string |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func NewDriver(root, initPath string) (*driver, error) {
|
|
| 64 |
+ if err := os.MkdirAll(root, 0700); err != nil {
|
|
| 65 |
+ return nil, err |
|
| 66 |
+ } |
|
| 67 |
+ if err := apparmor.InstallDefaultProfile(); err != nil {
|
|
| 68 |
+ return nil, err |
|
| 69 |
+ } |
|
| 70 |
+ return &driver{
|
|
| 71 |
+ root: root, |
|
| 72 |
+ initPath: initPath, |
|
| 73 |
+ }, nil |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
| 77 |
+ if err := d.validateCommand(c); err != nil {
|
|
| 78 |
+ return -1, err |
|
| 79 |
+ } |
|
| 80 |
+ var ( |
|
| 81 |
+ term nsinit.Terminal |
|
| 82 |
+ container = createContainer(c) |
|
| 83 |
+ factory = &dockerCommandFactory{c: c, driver: d}
|
|
| 84 |
+ stateWriter = &dockerStateWriter{
|
|
| 85 |
+ callback: startCallback, |
|
| 86 |
+ c: c, |
|
| 87 |
+ dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
|
|
| 88 |
+ } |
|
| 89 |
+ ns = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
|
|
| 90 |
+ args = append([]string{c.Entrypoint}, c.Arguments...)
|
|
| 91 |
+ ) |
|
| 92 |
+ if err := d.createContainerRoot(c.ID); err != nil {
|
|
| 93 |
+ return -1, err |
|
| 94 |
+ } |
|
| 95 |
+ defer d.removeContainerRoot(c.ID) |
|
| 96 |
+ |
|
| 97 |
+ if c.Tty {
|
|
| 98 |
+ term = &dockerTtyTerm{
|
|
| 99 |
+ pipes: pipes, |
|
| 100 |
+ } |
|
| 101 |
+ } else {
|
|
| 102 |
+ term = &dockerStdTerm{
|
|
| 103 |
+ pipes: pipes, |
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+ c.Terminal = term |
|
| 107 |
+ if err := d.writeContainerFile(container, c.ID); err != nil {
|
|
| 108 |
+ return -1, err |
|
| 109 |
+ } |
|
| 110 |
+ return ns.Exec(container, term, args) |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
|
| 114 |
+ err := syscall.Kill(p.Process.Pid, syscall.Signal(sig)) |
|
| 115 |
+ d.removeContainerRoot(p.ID) |
|
| 116 |
+ return err |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+func (d *driver) Info(id string) execdriver.Info {
|
|
| 120 |
+ return &info{
|
|
| 121 |
+ ID: id, |
|
| 122 |
+ driver: d, |
|
| 123 |
+ } |
|
| 124 |
+} |
|
| 125 |
+ |
|
| 126 |
+func (d *driver) Name() string {
|
|
| 127 |
+ return fmt.Sprintf("%s-%s", DriverName, Version)
|
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+// TODO: this can be improved with our driver |
|
| 131 |
+// there has to be a better way to do this |
|
| 132 |
+func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
| 133 |
+ pids := []int{}
|
|
| 134 |
+ |
|
| 135 |
+ subsystem := "devices" |
|
| 136 |
+ cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem) |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ return pids, err |
|
| 139 |
+ } |
|
| 140 |
+ cgroupDir, err := cgroups.GetThisCgroupDir(subsystem) |
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ return pids, err |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks") |
|
| 146 |
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
| 147 |
+ filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks") |
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+ output, err := ioutil.ReadFile(filename) |
|
| 151 |
+ if err != nil {
|
|
| 152 |
+ return pids, err |
|
| 153 |
+ } |
|
| 154 |
+ for _, p := range strings.Split(string(output), "\n") {
|
|
| 155 |
+ if len(p) == 0 {
|
|
| 156 |
+ continue |
|
| 157 |
+ } |
|
| 158 |
+ pid, err := strconv.Atoi(p) |
|
| 159 |
+ if err != nil {
|
|
| 160 |
+ return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
| 161 |
+ } |
|
| 162 |
+ pids = append(pids, pid) |
|
| 163 |
+ } |
|
| 164 |
+ return pids, nil |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
|
|
| 168 |
+ data, err := json.Marshal(container) |
|
| 169 |
+ if err != nil {
|
|
| 170 |
+ return err |
|
| 171 |
+ } |
|
| 172 |
+ return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655) |
|
| 173 |
+} |
|
| 174 |
+ |
|
| 175 |
+func (d *driver) createContainerRoot(id string) error {
|
|
| 176 |
+ return os.MkdirAll(filepath.Join(d.root, id), 0655) |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+func (d *driver) removeContainerRoot(id string) error {
|
|
| 180 |
+ return os.RemoveAll(filepath.Join(d.root, id)) |
|
| 181 |
+} |
|
| 182 |
+ |
|
| 183 |
+func (d *driver) validateCommand(c *execdriver.Command) error {
|
|
| 184 |
+ // we need to check the Config of the command to make sure that we |
|
| 185 |
+ // do not have any of the lxc-conf variables |
|
| 186 |
+ for _, conf := range c.Config {
|
|
| 187 |
+ if strings.Contains(conf, "lxc") {
|
|
| 188 |
+ return fmt.Errorf("%s is not supported by the native driver", conf)
|
|
| 189 |
+ } |
|
| 190 |
+ } |
|
| 191 |
+ return nil |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+func getEnv(key string, env []string) string {
|
|
| 195 |
+ for _, pair := range env {
|
|
| 196 |
+ parts := strings.Split(pair, "=") |
|
| 197 |
+ if parts[0] == key {
|
|
| 198 |
+ return parts[1] |
|
| 199 |
+ } |
|
| 200 |
+ } |
|
| 201 |
+ return "" |
|
| 202 |
+} |
|
| 203 |
+ |
|
| 204 |
+type dockerCommandFactory struct {
|
|
| 205 |
+ c *execdriver.Command |
|
| 206 |
+ driver *driver |
|
| 207 |
+} |
|
| 208 |
+ |
|
| 209 |
+// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces |
|
| 210 |
+// defined on the container's configuration and use the current binary as the init with the |
|
| 211 |
+// args provided |
|
| 212 |
+func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
|
|
| 213 |
+ // we need to join the rootfs because nsinit will setup the rootfs and chroot |
|
| 214 |
+ initPath := filepath.Join(d.c.Rootfs, d.c.InitPath) |
|
| 215 |
+ |
|
| 216 |
+ d.c.Path = d.driver.initPath |
|
| 217 |
+ d.c.Args = append([]string{
|
|
| 218 |
+ initPath, |
|
| 219 |
+ "-driver", DriverName, |
|
| 220 |
+ "-console", console, |
|
| 221 |
+ "-pipe", "3", |
|
| 222 |
+ "-root", filepath.Join(d.driver.root, d.c.ID), |
|
| 223 |
+ "--", |
|
| 224 |
+ }, args...) |
|
| 225 |
+ |
|
| 226 |
+ // set this to nil so that when we set the clone flags anything else is reset |
|
| 227 |
+ d.c.SysProcAttr = nil |
|
| 228 |
+ system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces))) |
|
| 229 |
+ d.c.ExtraFiles = []*os.File{syncFile}
|
|
| 230 |
+ |
|
| 231 |
+ d.c.Env = container.Env |
|
| 232 |
+ d.c.Dir = d.c.Rootfs |
|
| 233 |
+ |
|
| 234 |
+ return &d.c.Cmd |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+type dockerStateWriter struct {
|
|
| 238 |
+ dsw nsinit.StateWriter |
|
| 239 |
+ c *execdriver.Command |
|
| 240 |
+ callback execdriver.StartCallback |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+func (d *dockerStateWriter) WritePid(pid int) error {
|
|
| 244 |
+ d.c.ContainerPid = pid |
|
| 245 |
+ err := d.dsw.WritePid(pid) |
|
| 246 |
+ if d.callback != nil {
|
|
| 247 |
+ d.callback(d.c) |
|
| 248 |
+ } |
|
| 249 |
+ return err |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+func (d *dockerStateWriter) DeletePid() error {
|
|
| 253 |
+ return d.dsw.DeletePid() |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+func createLogger(debug string) *log.Logger {
|
|
| 257 |
+ var w io.Writer |
|
| 258 |
+ // if we are in debug mode set the logger to stderr |
|
| 259 |
+ if debug != "" {
|
|
| 260 |
+ w = os.Stderr |
|
| 261 |
+ } else {
|
|
| 262 |
+ w = ioutil.Discard |
|
| 263 |
+ } |
|
| 264 |
+ return log.New(w, "[libcontainer] ", log.LstdFlags) |
|
| 265 |
+} |
| 0 | 266 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,21 @@ |
| 0 |
+package native |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "path/filepath" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type info struct {
|
|
| 8 |
+ ID string |
|
| 9 |
+ driver *driver |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+// IsRunning is determined by looking for the |
|
| 13 |
+// pid file for a container. If the file exists then the |
|
| 14 |
+// container is currently running |
|
| 15 |
+func (i *info) IsRunning() bool {
|
|
| 16 |
+ if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
|
|
| 17 |
+ return true |
|
| 18 |
+ } |
|
| 19 |
+ return false |
|
| 20 |
+} |
| 0 | 21 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,42 @@ |
| 0 |
+/* |
|
| 1 |
+ These types are wrappers around the libcontainer Terminal interface so that |
|
| 2 |
+ we can resuse the docker implementations where possible. |
|
| 3 |
+*/ |
|
| 4 |
+package native |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 8 |
+ "io" |
|
| 9 |
+ "os" |
|
| 10 |
+ "os/exec" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type dockerStdTerm struct {
|
|
| 14 |
+ execdriver.StdConsole |
|
| 15 |
+ pipes *execdriver.Pipes |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
|
|
| 19 |
+ return d.AttachPipes(cmd, d.pipes) |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func (d *dockerStdTerm) SetMaster(master *os.File) {
|
|
| 23 |
+ // do nothing |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+type dockerTtyTerm struct {
|
|
| 27 |
+ execdriver.TtyConsole |
|
| 28 |
+ pipes *execdriver.Pipes |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
|
|
| 32 |
+ go io.Copy(t.pipes.Stdout, t.MasterPty) |
|
| 33 |
+ if t.pipes.Stdin != nil {
|
|
| 34 |
+ go io.Copy(t.MasterPty, t.pipes.Stdin) |
|
| 35 |
+ } |
|
| 36 |
+ return nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (t *dockerTtyTerm) SetMaster(master *os.File) {
|
|
| 40 |
+ t.MasterPty = master |
|
| 41 |
+} |
| 0 | 42 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,23 @@ |
| 0 |
+package execdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// Pipes is a wrapper around a containers output for |
|
| 7 |
+// stdin, stdout, stderr |
|
| 8 |
+type Pipes struct {
|
|
| 9 |
+ Stdin io.ReadCloser |
|
| 10 |
+ Stdout, Stderr io.Writer |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func NewPipes(stdin io.ReadCloser, stdout, stderr io.Writer, useStdin bool) *Pipes {
|
|
| 14 |
+ p := &Pipes{
|
|
| 15 |
+ Stdout: stdout, |
|
| 16 |
+ Stderr: stderr, |
|
| 17 |
+ } |
|
| 18 |
+ if useStdin {
|
|
| 19 |
+ p.Stdin = stdin |
|
| 20 |
+ } |
|
| 21 |
+ return p |
|
| 22 |
+} |
| 0 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,126 @@ |
| 0 |
+package execdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/pkg/term" |
|
| 4 |
+ "github.com/kr/pty" |
|
| 5 |
+ "io" |
|
| 6 |
+ "os" |
|
| 7 |
+ "os/exec" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func SetTerminal(command *Command, pipes *Pipes) error {
|
|
| 11 |
+ var ( |
|
| 12 |
+ term Terminal |
|
| 13 |
+ err error |
|
| 14 |
+ ) |
|
| 15 |
+ if command.Tty {
|
|
| 16 |
+ term, err = NewTtyConsole(command, pipes) |
|
| 17 |
+ } else {
|
|
| 18 |
+ term, err = NewStdConsole(command, pipes) |
|
| 19 |
+ } |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return err |
|
| 22 |
+ } |
|
| 23 |
+ command.Terminal = term |
|
| 24 |
+ return nil |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+type TtyConsole struct {
|
|
| 28 |
+ MasterPty *os.File |
|
| 29 |
+ SlavePty *os.File |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
|
|
| 33 |
+ ptyMaster, ptySlave, err := pty.Open() |
|
| 34 |
+ if err != nil {
|
|
| 35 |
+ return nil, err |
|
| 36 |
+ } |
|
| 37 |
+ tty := &TtyConsole{
|
|
| 38 |
+ MasterPty: ptyMaster, |
|
| 39 |
+ SlavePty: ptySlave, |
|
| 40 |
+ } |
|
| 41 |
+ if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
|
|
| 42 |
+ tty.Close() |
|
| 43 |
+ return nil, err |
|
| 44 |
+ } |
|
| 45 |
+ command.Console = tty.SlavePty.Name() |
|
| 46 |
+ return tty, nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func (t *TtyConsole) Master() *os.File {
|
|
| 50 |
+ return t.MasterPty |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (t *TtyConsole) Resize(h, w int) error {
|
|
| 54 |
+ return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
|
| 58 |
+ command.Stdout = t.SlavePty |
|
| 59 |
+ command.Stderr = t.SlavePty |
|
| 60 |
+ |
|
| 61 |
+ go func() {
|
|
| 62 |
+ if wb, ok := pipes.Stdout.(interface {
|
|
| 63 |
+ CloseWriters() error |
|
| 64 |
+ }); ok {
|
|
| 65 |
+ defer wb.CloseWriters() |
|
| 66 |
+ } |
|
| 67 |
+ io.Copy(pipes.Stdout, t.MasterPty) |
|
| 68 |
+ }() |
|
| 69 |
+ |
|
| 70 |
+ if pipes.Stdin != nil {
|
|
| 71 |
+ command.Stdin = t.SlavePty |
|
| 72 |
+ command.SysProcAttr.Setctty = true |
|
| 73 |
+ |
|
| 74 |
+ go func() {
|
|
| 75 |
+ defer pipes.Stdin.Close() |
|
| 76 |
+ io.Copy(t.MasterPty, pipes.Stdin) |
|
| 77 |
+ }() |
|
| 78 |
+ } |
|
| 79 |
+ return nil |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (t *TtyConsole) Close() error {
|
|
| 83 |
+ t.SlavePty.Close() |
|
| 84 |
+ return t.MasterPty.Close() |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+type StdConsole struct {
|
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
|
|
| 91 |
+ std := &StdConsole{}
|
|
| 92 |
+ |
|
| 93 |
+ if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
|
|
| 94 |
+ return nil, err |
|
| 95 |
+ } |
|
| 96 |
+ return std, nil |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
|
|
| 100 |
+ command.Stdout = pipes.Stdout |
|
| 101 |
+ command.Stderr = pipes.Stderr |
|
| 102 |
+ |
|
| 103 |
+ if pipes.Stdin != nil {
|
|
| 104 |
+ stdin, err := command.StdinPipe() |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ return err |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ go func() {
|
|
| 110 |
+ defer stdin.Close() |
|
| 111 |
+ io.Copy(stdin, pipes.Stdin) |
|
| 112 |
+ }() |
|
| 113 |
+ } |
|
| 114 |
+ return nil |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+func (s *StdConsole) Resize(h, w int) error {
|
|
| 118 |
+ // we do not need to reside a non tty |
|
| 119 |
+ return nil |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (s *StdConsole) Close() error {
|
|
| 123 |
+ // nothing to close here |
|
| 124 |
+ return nil |
|
| 125 |
+} |
| ... | ... |
@@ -7,9 +7,9 @@ import ( |
| 7 | 7 |
"github.com/dotcloud/docker/daemonconfig" |
| 8 | 8 |
"github.com/dotcloud/docker/dockerversion" |
| 9 | 9 |
"github.com/dotcloud/docker/engine" |
| 10 |
- "github.com/dotcloud/docker/execdriver" |
|
| 11 |
- "github.com/dotcloud/docker/execdriver/execdrivers" |
|
| 12 |
- "github.com/dotcloud/docker/execdriver/lxc" |
|
| 10 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 11 |
+ "github.com/dotcloud/docker/runtime/execdriver/execdrivers" |
|
| 12 |
+ "github.com/dotcloud/docker/runtime/execdriver/lxc" |
|
| 13 | 13 |
"github.com/dotcloud/docker/graph" |
| 14 | 14 |
"github.com/dotcloud/docker/graphdriver" |
| 15 | 15 |
"github.com/dotcloud/docker/graphdriver/aufs" |
| ... | ... |
@@ -3,9 +3,9 @@ package sysinit |
| 3 | 3 |
import ( |
| 4 | 4 |
"flag" |
| 5 | 5 |
"fmt" |
| 6 |
- "github.com/dotcloud/docker/execdriver" |
|
| 7 |
- _ "github.com/dotcloud/docker/execdriver/lxc" |
|
| 8 |
- _ "github.com/dotcloud/docker/execdriver/native" |
|
| 6 |
+ "github.com/dotcloud/docker/runtime/execdriver" |
|
| 7 |
+ _ "github.com/dotcloud/docker/runtime/execdriver/lxc" |
|
| 8 |
+ _ "github.com/dotcloud/docker/runtime/execdriver/native" |
|
| 9 | 9 |
"log" |
| 10 | 10 |
"os" |
| 11 | 11 |
) |