Move environment configuration to Process

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-02-06 19:16:11 -08:00
parent 58023ad32f
commit 21bb5ccc4f
11 changed files with 64 additions and 38 deletions

View File

@ -55,11 +55,6 @@ type Config struct {
// WorkingDir will change the processes current working directory inside the container's rootfs
WorkingDir string `json:"working_dir,omitempty"`
// Env will populate the processes environment with the provided values
// Any values from the parent processes will be cleared before the values
// provided in Env are provided to the process
Env []string `json:"environment,omitempty"`
// Console is the path to the console allocated to the container.
Console string `json:"console,omitempty"`

View File

@ -261,6 +261,7 @@ func TestEnter(t *testing.T) {
pconfig := libcontainer.Process{
Args: []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"},
Env: standardEnvironment,
Stdin: stdinR,
Stdout: &stdout,
}
@ -361,6 +362,7 @@ func TestFreeze(t *testing.T) {
pconfig := libcontainer.Process{
Args: []string{"cat"},
Env: standardEnvironment,
Stdin: stdinR,
}
pid, err := container.Start(&pconfig)

View File

@ -6,6 +6,13 @@ import (
"github.com/docker/libcontainer/configs"
)
var standardEnvironment = []string{
"HOME=/root",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=integration",
"TERM=xterm",
}
// newTemplateConfig returns a base template for running a container
//
// it uses a network strategy of just setting a loopback interface
@ -45,12 +52,6 @@ func newTemplateConfig(rootfs string) *configs.Config {
Devices: configs.DefaultAutoCreatedDevices,
Hostname: "integration",
Env: []string{
"HOME=/root",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=integration",
"TERM=xterm",
},
Networks: []*configs.Network{
{
Type: "loopback",

View File

@ -93,6 +93,7 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe
process := &libcontainer.Process{
Args: args,
Env: standardEnvironment,
Stdin: buffers.Stdin,
Stdout: buffers.Stdout,
Stderr: buffers.Stderr,

View File

@ -18,10 +18,6 @@ import (
"github.com/golang/glog"
)
const (
EXIT_SIGNAL_OFFSET = 128
)
type pid struct {
Pid int `json:"Pid"`
}
@ -50,8 +46,7 @@ func (c *linuxContainer) Status() (configs.Status, error) {
return configs.Destroyed, nil
}
// return Running if the init process is alive
err := syscall.Kill(c.state.InitPid, 0)
if err != nil {
if err := syscall.Kill(c.state.InitPid, 0); err != nil {
if err == syscall.ESRCH {
return configs.Destroyed, nil
}
@ -96,10 +91,9 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
cmd := c.commandTemplate(process)
if status != configs.Destroyed {
// TODO: (crosbymichael) check out console use for execin
return c.startNewProcess(cmd, process.Args)
//return namespaces.ExecIn(process.Args, c.config.Env, "", cmd, c.config, c.state)
return c.startNewProcess(cmd, process)
}
if err := c.startInitialProcess(cmd, process.Args); err != nil {
if err := c.startInitialProcess(cmd, process); err != nil {
return -1, err
}
return c.state.InitPid, nil
@ -112,7 +106,7 @@ func (c *linuxContainer) commandTemplate(process *Process) *exec.Cmd {
cmd.Stdin = process.Stdin
cmd.Stdout = process.Stdout
cmd.Stderr = process.Stderr
cmd.Env = c.config.Env
cmd.Env = process.Env
cmd.Dir = c.config.Rootfs
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
@ -122,9 +116,9 @@ func (c *linuxContainer) commandTemplate(process *Process) *exec.Cmd {
}
// startNewProcess adds another process to an already running container
func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, error) {
func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, process *Process) (int, error) {
glog.Info("start new container process")
parent, child, err := newInitPipe()
parent, child, err := c.newInitPipe()
if err != nil {
return -1, err
}
@ -169,18 +163,20 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err
}
if err := json.NewEncoder(parent).Encode(&initConfig{
Config: c.config,
Args: args,
Args: process.Args,
Env: process.Env,
}); err != nil {
return terminate(err)
}
return pid.Pid, nil
}
func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, args []string) error {
// startInitialProcess starts PID 1 for the container.
func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, process *Process) error {
glog.Info("starting container initial process")
// create a pipe so that we can syncronize with the namespaced process and
// pass the state and configuration to the child process
parent, child, err := newInitPipe()
parent, child, err := c.newInitPipe()
if err != nil {
return err
}
@ -239,13 +235,14 @@ func (c *linuxContainer) startInitialProcess(cmd *exec.Cmd, args []string) error
return terminate(err)
}
iconfig := &initConfig{
Args: args,
Args: process.Args,
Config: c.config,
Env: process.Env,
NetworkState: &networkState,
}
// Start the setup process to setup the init process
if c.config.Namespaces.Contains(configs.NEWUSER) {
if err = executeSetupCmd(cmd.Args, cmd.Process.Pid, c.config, iconfig, &networkState); err != nil {
if err = c.executeSetupCmd(cmd.Args, cmd.Process.Pid, c.config, iconfig, &networkState); err != nil {
return terminate(err)
}
}
@ -281,6 +278,7 @@ func (c *linuxContainer) Destroy() error {
if status != configs.Destroyed {
return newGenericError(nil, ContainerNotStopped)
}
// TODO: remove cgroups
return os.RemoveAll(c.root)
}
@ -297,6 +295,7 @@ func (c *linuxContainer) Signal(signal os.Signal) error {
panic("not implemented")
}
// TODO: rename to be more descriptive
func (c *linuxContainer) OOM() (<-chan struct{}, error) {
return NotifyOnOOM(c.state)
}
@ -322,7 +321,7 @@ func (c *linuxContainer) updateStateFile() error {
}
// New returns a newly initialized Pipe for communication between processes
func newInitPipe() (parent *os.File, child *os.File, err error) {
func (c *linuxContainer) newInitPipe() (parent *os.File, child *os.File, err error) {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
if err != nil {
return nil, nil, err
@ -392,9 +391,9 @@ func (c *linuxContainer) initializeNetworking(nspid int, networkState *configs.N
return nil
}
func executeSetupCmd(args []string, ppid int, container *configs.Config, process *initConfig, networkState *configs.NetworkState) error {
func (c *linuxContainer) executeSetupCmd(args []string, ppid int, container *configs.Config, process *initConfig, networkState *configs.NetworkState) error {
command := exec.Command(args[0], args[1:]...)
parent, child, err := newInitPipe()
parent, child, err := c.newInitPipe()
if err != nil {
return err
}

View File

@ -30,6 +30,7 @@ const (
// Process is used for transferring parameters from Exec() to Init()
type initConfig struct {
Args []string `json:"args,omitempty"`
Env []string `json:"env,omitempty"`
Config *configs.Config `json:"config,omitempty"`
NetworkState *configs.NetworkState `json:"network_state,omitempty"`
}
@ -43,18 +44,20 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) {
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
return nil, err
}
if err := populateProcessEnvironment(config.Config.Env); err != nil {
if err := populateProcessEnvironment(config.Env); err != nil {
return nil, err
}
switch t {
case initSetns:
return &linuxSetnsInit{
args: config.Args,
env: config.Env,
config: config.Config,
}, nil
case initUserns:
return &linuxUsernsInit{
args: config.Args,
env: config.Env,
config: config.Config,
}, nil
case initUsernsSideCar:
@ -65,6 +68,7 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) {
case initStandard:
return &linuxStandardInit{
config: config,
env: config.Env,
}, nil
}
return nil, fmt.Errorf("unknown init type %q", t)

View File

@ -13,6 +13,7 @@ import (
// inside an existing container.
type linuxSetnsInit struct {
args []string
env []string
config *configs.Config
}
@ -31,5 +32,5 @@ func (l *linuxSetnsInit) Init() error {
return err
}
}
return system.Execv(l.args[0], l.args[0:], l.config.Env)
return system.Execv(l.args[0], l.args[0:], l.env)
}

View File

@ -16,6 +16,7 @@ import (
type linuxStandardInit struct {
config *initConfig
env []string
}
func (l *linuxStandardInit) Init() error {
@ -86,5 +87,5 @@ func (l *linuxStandardInit) Init() error {
if syscall.Getppid() == 1 {
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
}
return system.Execv(l.config.Args[0], l.config.Args[0:], l.config.Config.Env)
return system.Execv(l.config.Args[0], l.config.Args[0:], l.env)
}

View File

@ -15,6 +15,7 @@ import (
type linuxUsernsInit struct {
args []string
env []string
config *configs.Config
}
@ -76,5 +77,5 @@ func (l *linuxUsernsInit) Init() error {
if syscall.Getppid() == 1 {
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
}
return system.Execv(l.args[0], l.args[0:], l.config.Env)
return system.Execv(l.args[0], l.args[0:], l.env)
}

View File

@ -1,6 +1,9 @@
package libcontainer
import "io"
import (
"io"
"os/exec"
)
// Process specifies the configuration and IO for a process inside
// a container.
@ -8,6 +11,9 @@ type Process struct {
// The command to be run followed by any arguments.
Args []string
// Env specifies the environment variables for the process.
Env []string
// Stdin is a pointer to a reader which provides the standard input stream.
Stdin io.Reader
@ -16,4 +22,6 @@ type Process struct {
// Stderr is a pointer to a writer which receives the standard error stream.
Stderr io.Writer
cmd *exec.Cmd
}

View File

@ -10,6 +10,10 @@ import (
"syscall"
)
const (
exitSignalOffset = 128
)
// GenerateRandomName returns a new name joined with a prefix. This size
// specified is used to truncate the randomly generated value
func GenerateRandomName(prefix string, size int) (string, error) {
@ -53,3 +57,12 @@ func CloseExecFrom(minFd int) error {
}
return nil
}
// ExitStatus returns the correct exit status for a process based on if it
// was signaled or existed cleanly.
func ExitStatus(status syscall.WaitStatus) int {
if status.Signaled() {
return exitSignalOffset + int(status.Signal())
}
return status.ExitStatus()
}