Merge pull request #50 from crosbymichael/create-state

Create state
This commit is contained in:
Michael Crosby
2014-06-25 12:16:02 -07:00
7 changed files with 67 additions and 57 deletions
+3 -4
View File
@@ -19,10 +19,10 @@ process are specified in this file. The configuration is used for each process
See the `sample_configs` folder for examples of what the container configuration should look like.
Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. Running the life of the namespace, a `pid` file
is written to the current directory with the pid of the namespaced process to the external world. A client can use this pid to wait, kill, or perform other operation with the container. If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process.
Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. During the life of the container, a `state.json` file
is written to the current directory with the pid and start time of the container's PID1. A client can use this pid to wait, kill, or perform other operation with the container. If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process.
You may also specify an alternate root place where the `container.json` file is read and where the `pid` file will be saved.
You may also specify an alternate root place where the `container.json` file is read and where the `state.json` file will be saved.
#### nsinit
@@ -38,7 +38,6 @@ nsinit exec /bin/bash
If you wish to spawn another process inside the container while your current bash session is
running just run the exact same command again to get another bash shell or change the command. If the original process dies, PID 1, all other processes spawned inside the container will also be killed and the namespace will be removed.
You can identify if a process is running in a container by looking to see if `pid` is in the root of the directory.
#### Future
See the [roadmap](ROADMAP.md).
+8 -2
View File
@@ -56,12 +56,18 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
if err != nil {
return -1, err
}
if err := WritePid(dataPath, command.Process.Pid, started); err != nil {
state := &libcontainer.State{
InitPid: command.Process.Pid,
InitStartTime: started,
}
if err := libcontainer.SaveState(dataPath, state); err != nil {
command.Process.Kill()
command.Wait()
return -1, err
}
defer DeletePid(dataPath)
defer libcontainer.DeleteState(dataPath)
// Do this before syncing with child so that no children
// can escape the cgroup
+2 -2
View File
@@ -13,7 +13,7 @@ import (
)
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func ExecIn(container *libcontainer.Config, nspid int, args []string) error {
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
// Marshall the container into JSON since it won't be available in the namespace.
containerJson, err := json.Marshal(container)
@@ -22,7 +22,7 @@ func ExecIn(container *libcontainer.Config, nspid int, args []string) error {
}
// Enter the namespace and then finish setup
finalArgs := []string{os.Args[0], "nsenter", "--nspid", strconv.Itoa(nspid), "--containerjson", string(containerJson), "--"}
finalArgs := []string{os.Args[0], "nsenter", "--nspid", strconv.Itoa(state.InitPid), "--containerjson", string(containerJson), "--"}
finalArgs = append(finalArgs, args...)
if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
return err
-28
View File
@@ -1,28 +0,0 @@
package namespaces
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// WritePid writes the namespaced processes pid to pid and it's start time
// to the path specified
func WritePid(path string, pid int, startTime string) error {
err := ioutil.WriteFile(filepath.Join(path, "pid"), []byte(fmt.Sprint(pid)), 0655)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(path, "start"), []byte(startTime), 0655)
}
// DeletePid removes the pid and started file from disk when the container's process
// dies and the container is cleanly removed
func DeletePid(path string) error {
err := os.Remove(filepath.Join(path, "pid"))
if serr := os.Remove(filepath.Join(path, "start")); err == nil {
err = serr
}
return err
}
+6 -5
View File
@@ -19,19 +19,20 @@ var execCommand = cli.Command{
}
func execAction(context *cli.Context) {
var nspid, exitCode int
var exitCode int
container, err := loadContainer()
if err != nil {
log.Fatal(err)
}
if nspid, err = readPid(); err != nil && !os.IsNotExist(err) {
log.Fatalf("unable to read pid: %s", err)
state, err := libcontainer.GetState(dataPath)
if err != nil && !os.IsNotExist(err) {
log.Fatalf("unable to read state.json: %s", err)
}
if nspid > 0 {
err = namespaces.ExecIn(container, nspid, []string(context.Args()))
if state != nil {
err = namespaces.ExecIn(container, state, []string(context.Args()))
} else {
term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
exitCode, err = startContainer(container, term, dataPath, []string(context.Args()))
-16
View File
@@ -2,11 +2,9 @@ package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"github.com/docker/libcontainer"
)
@@ -26,20 +24,6 @@ func loadContainer() (*libcontainer.Config, error) {
return container, nil
}
func readPid() (int, error) {
data, err := ioutil.ReadFile(filepath.Join(dataPath, "pid"))
if err != nil {
return -1, err
}
pid, err := strconv.Atoi(string(data))
if err != nil {
return -1, err
}
return pid, nil
}
func openLog(name string) error {
f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
if err != nil {
+48
View File
@@ -0,0 +1,48 @@
package libcontainer
import (
"encoding/json"
"os"
"path/filepath"
)
// State represents a running container's state
type State struct {
// InitPid is the init process id in the parent namespace
InitPid int `json:"init_pid,omitempty"`
// InitStartTime is the init process start time
InitStartTime string `json:"init_start_time,omitempty"`
}
// SaveState writes the container's runtime state to a state.json file
// in the specified path
func SaveState(basePath string, state *State) error {
f, err := os.Create(filepath.Join(basePath, "state.json"))
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(state)
}
// GetState reads the state.json file for a running container
func GetState(basePath string) (*State, error) {
f, err := os.Open(filepath.Join(basePath, "state.json"))
if err != nil {
return nil, err
}
defer f.Close()
var state *State
if err := json.NewDecoder(f).Decode(&state); err != nil {
return nil, err
}
return state, nil
}
// DeleteState deletes the state.json file
func DeleteState(basePath string) error {
return os.Remove(filepath.Join(basePath, "state.json"))
}