Merge pull request #155 from crosbymichael/execin-func
Implement execin by using registered functions
This commit is contained in:
commit
1befa2fe9e
|
@ -3,6 +3,7 @@
|
|||
package namespaces
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -18,10 +19,10 @@ import (
|
|||
|
||||
// ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the
|
||||
// setns code in a single threaded environment joining the existing containers' namespaces.
|
||||
func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath string,
|
||||
func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string,
|
||||
stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
|
||||
|
||||
args := []string{"nsenter", "--nspid", strconv.Itoa(state.InitPid)}
|
||||
args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)}
|
||||
|
||||
if console != "" {
|
||||
args = append(args, "--console", console)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -15,7 +15,7 @@ var configCommand = cli.Command{
|
|||
}
|
||||
|
||||
func configAction(context *cli.Context) {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -8,6 +8,7 @@ import (
|
|||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
|
@ -20,12 +21,29 @@ var execCommand = cli.Command{
|
|||
Name: "exec",
|
||||
Usage: "execute a new command inside a container",
|
||||
Action: execAction,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "list", Usage: "list all registered exec functions"},
|
||||
cli.StringFlag{Name: "func", Value: "exec", Usage: "function name to exec inside a container"},
|
||||
},
|
||||
}
|
||||
|
||||
func execAction(context *cli.Context) {
|
||||
if context.Bool("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
|
||||
fmt.Fprint(w, "NAME\tUSAGE\n")
|
||||
|
||||
for k, f := range argvs {
|
||||
fmt.Fprintf(w, "%s\t%s\n", k, f.Usage)
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -36,7 +54,7 @@ func execAction(context *cli.Context) {
|
|||
}
|
||||
|
||||
if state != nil {
|
||||
exitCode, err = startInExistingContainer(container, state, context)
|
||||
exitCode, err = startInExistingContainer(container, state, context.String("func"), context)
|
||||
} else {
|
||||
exitCode, err = startContainer(container, dataPath, []string(context.Args()))
|
||||
}
|
||||
|
@ -52,7 +70,7 @@ func execAction(context *cli.Context) {
|
|||
// with the nsenter argument so that the C code can setns an the namespaces that we require. Then that
|
||||
// code path will drop us into the path that we can do the final setup of the namespace and exec the users
|
||||
// application.
|
||||
func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, context *cli.Context) (int, error) {
|
||||
func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) {
|
||||
var (
|
||||
master *os.File
|
||||
console string
|
||||
|
@ -102,7 +120,7 @@ func startInExistingContainer(config *libcontainer.Config, state *libcontainer.S
|
|||
}()
|
||||
}
|
||||
|
||||
return namespaces.ExecIn(config, state, context.Args(), os.Args[0], stdin, stdout, stderr, console, startCallback)
|
||||
return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback)
|
||||
}
|
||||
|
||||
// startContainer starts the container. Returns the exit status or -1 and an
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
@ -26,7 +26,7 @@ var (
|
|||
func initAction(context *cli.Context) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
logPath = os.Getenv("log")
|
||||
argvs = make(map[string]func())
|
||||
argvs = make(map[string]*rFunc)
|
||||
)
|
||||
|
||||
func init() {
|
||||
argvs["nsenter"] = nsenter
|
||||
}
|
||||
|
||||
func preload(context *cli.Context) error {
|
||||
if logPath != "" {
|
||||
if err := openLog(logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
argvs["exec"] = &rFunc{
|
||||
Usage: "execute a process inside an existing container",
|
||||
Action: nsenterExec,
|
||||
}
|
||||
|
||||
return nil
|
||||
argvs["mknod"] = &rFunc{
|
||||
Usage: "mknod a device inside an existing container",
|
||||
Action: nsenterMknod,
|
||||
}
|
||||
|
||||
argvs["ip"] = &rFunc{
|
||||
Usage: "display the container's network interfaces",
|
||||
Action: nsenterIp,
|
||||
}
|
||||
}
|
||||
|
||||
func NsInit() {
|
||||
func main() {
|
||||
// we need to check our argv 0 for any registred functions to run instead of the
|
||||
// normal cli code path
|
||||
|
||||
action, exists := argvs[os.Args[0]]
|
||||
f, exists := argvs[strings.TrimPrefix(os.Args[0], "nsenter-")]
|
||||
if exists {
|
||||
action()
|
||||
runFunc(f)
|
||||
|
||||
return
|
||||
}
|
|
@ -1,42 +1,84 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/devices"
|
||||
"github.com/docker/libcontainer/mount/nodes"
|
||||
"github.com/docker/libcontainer/namespaces"
|
||||
_ "github.com/docker/libcontainer/namespaces/nsenter"
|
||||
"github.com/docker/libcontainer/syncpipe"
|
||||
)
|
||||
|
||||
func findUserArgs() []string {
|
||||
i := 0
|
||||
for _, a := range os.Args {
|
||||
i++
|
||||
|
||||
if a == "--" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return os.Args[i:]
|
||||
}
|
||||
|
||||
// this expects that we already have our namespaces setup by the C initializer
|
||||
// we are expected to finalize the namespace and exec the user's application
|
||||
func nsenter() {
|
||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create sync pipe: %s", err)
|
||||
}
|
||||
|
||||
var config *libcontainer.Config
|
||||
if err := syncPipe.ReadFromParent(&config); err != nil {
|
||||
log.Fatalf("reading container config from parent: %s", err)
|
||||
}
|
||||
|
||||
if err := namespaces.FinalizeSetns(config, findUserArgs()); err != nil {
|
||||
// nsenterExec exec's a process inside an existing container
|
||||
func nsenterExec(config *libcontainer.Config, args []string) {
|
||||
if err := namespaces.FinalizeSetns(config, args); err != nil {
|
||||
log.Fatalf("failed to nsenter: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// nsenterMknod runs mknod inside an existing container
|
||||
//
|
||||
// mknod <path> <type> <major> <minor>
|
||||
func nsenterMknod(config *libcontainer.Config, args []string) {
|
||||
if len(args) != 4 {
|
||||
log.Fatalf("expected mknod to have 4 arguments not %d", len(args))
|
||||
}
|
||||
|
||||
t := rune(args[1][0])
|
||||
|
||||
major, err := strconv.Atoi(args[2])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(args[3])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
n := &devices.Device{
|
||||
Path: args[0],
|
||||
Type: t,
|
||||
MajorNumber: int64(major),
|
||||
MinorNumber: int64(minor),
|
||||
}
|
||||
|
||||
if err := nodes.CreateDeviceNode("/", n); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// nsenterIp displays the network interfaces inside a container's net namespace
|
||||
func nsenterIp(config *libcontainer.Config, args []string) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
|
||||
fmt.Fprint(w, "NAME\tMTU\tMAC\tFLAG\tADDRS\n")
|
||||
|
||||
for _, iface := range interfaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
o := []string{}
|
||||
|
||||
for _, a := range addrs {
|
||||
o = append(o, a.String())
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", iface.Name, iface.MTU, iface.HardwareAddr, iface.Flags, strings.Join(o, ","))
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package main
|
||||
|
||||
import "github.com/docker/libcontainer/nsinit"
|
||||
|
||||
func main() {
|
||||
nsinit.NsInit()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
@ -34,7 +34,7 @@ func unpauseAction(context *cli.Context) {
|
|||
}
|
||||
|
||||
func toggle(state cgroups.FreezerState) error {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -16,7 +16,7 @@ var statsCommand = cli.Command{
|
|||
}
|
||||
|
||||
func statsAction(context *cli.Context) {
|
||||
container, err := loadContainer()
|
||||
container, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nsinit
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,10 +6,18 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/syncpipe"
|
||||
)
|
||||
|
||||
func loadContainer() (*libcontainer.Config, error) {
|
||||
// rFunc is a function registration for calling after an execin
|
||||
type rFunc struct {
|
||||
Usage string
|
||||
Action func(*libcontainer.Config, []string)
|
||||
}
|
||||
|
||||
func loadConfig() (*libcontainer.Config, error) {
|
||||
f, err := os.Open(filepath.Join(dataPath, "container.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -35,12 +43,52 @@ func openLog(name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func loadContainerFromJson(rawData string) (*libcontainer.Config, error) {
|
||||
var container *libcontainer.Config
|
||||
func findUserArgs() []string {
|
||||
i := 0
|
||||
for _, a := range os.Args {
|
||||
i++
|
||||
|
||||
if err := json.Unmarshal([]byte(rawData), &container); err != nil {
|
||||
if a == "--" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return os.Args[i:]
|
||||
}
|
||||
|
||||
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
||||
// fd 3 when running a process
|
||||
func loadConfigFromFd() (*libcontainer.Config, error) {
|
||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return container, nil
|
||||
var config *libcontainer.Config
|
||||
if err := syncPipe.ReadFromParent(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func preload(context *cli.Context) error {
|
||||
if logPath != "" {
|
||||
if err := openLog(logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runFunc(f *rFunc) {
|
||||
userArgs := findUserArgs()
|
||||
|
||||
config, err := loadConfigFromFd()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to receive config from sync pipe: %s", err)
|
||||
}
|
||||
|
||||
f.Action(config, userArgs)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue