Merge pull request #75 from hashicorp/h-spawn-daemon-build-dep
Make spawn-daemon only defined on linux
This commit is contained in:
commit
33117772b7
|
@ -2,34 +2,14 @@ package command
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
type SpawnDaemonCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
// Configuration for the command to start as a daemon.
|
||||
type DaemonConfig struct {
|
||||
exec.Cmd
|
||||
|
||||
// The paths, if not /dev/null, must be either in the tasks root directory
|
||||
// or in the shared alloc directory.
|
||||
StdoutFile string
|
||||
StdinFile string
|
||||
StderrFile string
|
||||
|
||||
Groups *cgroupConfig.Cgroup
|
||||
}
|
||||
|
||||
// Status of executing the user's command.
|
||||
type SpawnStartStatus struct {
|
||||
// ErrorMsg will be empty if the user command was started successfully.
|
||||
|
@ -37,11 +17,6 @@ type SpawnStartStatus struct {
|
|||
ErrorMsg string
|
||||
}
|
||||
|
||||
// The exit status of the user's command.
|
||||
type SpawnExitStatus struct {
|
||||
Success bool
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad spawn-daemon [options] <daemon_config>
|
||||
|
@ -65,99 +40,10 @@ func (c *SpawnDaemonCommand) Synopsis() string {
|
|||
return "Spawn a daemon command with configurable isolation."
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Run(args []string) int {
|
||||
flags := c.Meta.FlagSet("spawn-daemon", FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check that we got json input.
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error(c.Help())
|
||||
return 1
|
||||
}
|
||||
jsonInput, err := strconv.Unquote(args[0])
|
||||
if err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Failed to unquote json input: %v", err), 1)
|
||||
}
|
||||
|
||||
// De-serialize the passed command.
|
||||
var cmd DaemonConfig
|
||||
dec := json.NewDecoder(strings.NewReader(jsonInput))
|
||||
if err := dec.Decode(&cmd); err != nil {
|
||||
return outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Join this process to the cgroup.
|
||||
if cmd.Groups != nil {
|
||||
manager := cgroupFs.Manager{}
|
||||
manager.Cgroups = cmd.Groups
|
||||
|
||||
// Apply will place the current pid into the tasks file for each of the
|
||||
// created cgroups:
|
||||
// /sys/fs/cgroup/memory/user/1000.user/4.session/<uuid>/tasks
|
||||
//
|
||||
// Apply requires superuser permissions, and may fail if Nomad is not run with
|
||||
// the required permissions
|
||||
if err := manager.Apply(os.Getpid()); err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Failed to join cgroup: %v", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Isolate the user process.
|
||||
if _, err := syscall.Setsid(); err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Failed to join cgroup: %v",
|
||||
fmt.Errorf("Failed setting sid: %v", err)), 1)
|
||||
}
|
||||
|
||||
syscall.Umask(0)
|
||||
|
||||
// Redirect logs.
|
||||
stdo, err := os.OpenFile(cmd.StdoutFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Error opening file to redirect Stdout: %v", err), 1)
|
||||
}
|
||||
|
||||
stde, err := os.OpenFile(cmd.StderrFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Error opening file to redirect Stderr: %v", err), 1)
|
||||
}
|
||||
|
||||
stdi, err := os.OpenFile(cmd.StdinFile, os.O_CREATE|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Error opening file to redirect Stdin: %v", err), 1)
|
||||
}
|
||||
|
||||
cmd.Stdout = stdo
|
||||
cmd.Stderr = stde
|
||||
cmd.Stdin = stdi
|
||||
|
||||
// Spawn the user process.
|
||||
if err := cmd.Cmd.Start(); err != nil {
|
||||
return outputStartStatus(fmt.Errorf("Error starting user command: %v", err), 1)
|
||||
}
|
||||
|
||||
// Indicate that the command was started successfully.
|
||||
outputStartStatus(nil, 0)
|
||||
|
||||
// Wait and then output the exit status.
|
||||
exitStatus := &SpawnExitStatus{}
|
||||
if err := cmd.Wait(); err == nil {
|
||||
exitStatus.Success = true
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.Encode(exitStatus)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// outputStartStatus is a helper function that outputs a SpawnStartStatus to
|
||||
// Stdout with the passed error, which may be nil to indicate no error. It
|
||||
// returns the passed status.
|
||||
func outputStartStatus(err error, status int) int {
|
||||
func (c *SpawnDaemonCommand) outputStartStatus(err error, status int) int {
|
||||
startStatus := &SpawnStartStatus{}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
cgroupConfig "github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
// Configuration for the command to start as a daemon.
|
||||
type DaemonConfig struct {
|
||||
exec.Cmd
|
||||
|
||||
// The paths, if not /dev/null, must be either in the tasks root directory
|
||||
// or in the shared alloc directory.
|
||||
StdoutFile string
|
||||
StdinFile string
|
||||
StderrFile string
|
||||
|
||||
Groups *cgroupConfig.Cgroup
|
||||
}
|
||||
|
||||
// The exit status of the user's command.
|
||||
type SpawnExitStatus struct {
|
||||
Success bool
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Run(args []string) int {
|
||||
flags := c.Meta.FlagSet("spawn-daemon", FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check that we got json input.
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error(c.Help())
|
||||
return 1
|
||||
}
|
||||
jsonInput, err := strconv.Unquote(args[0])
|
||||
if err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Failed to unquote json input: %v", err), 1)
|
||||
}
|
||||
|
||||
// De-serialize the passed command.
|
||||
var cmd DaemonConfig
|
||||
dec := json.NewDecoder(strings.NewReader(jsonInput))
|
||||
if err := dec.Decode(&cmd); err != nil {
|
||||
return c.outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Join this process to the cgroup.
|
||||
if cmd.Groups != nil {
|
||||
manager := cgroupFs.Manager{}
|
||||
manager.Cgroups = cmd.Groups
|
||||
|
||||
// Apply will place the current pid into the tasks file for each of the
|
||||
// created cgroups:
|
||||
// /sys/fs/cgroup/memory/user/1000.user/4.session/<uuid>/tasks
|
||||
//
|
||||
// Apply requires superuser permissions, and may fail if Nomad is not run with
|
||||
// the required permissions
|
||||
if err := manager.Apply(os.Getpid()); err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Failed to join cgroup: %v", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Isolate the user process.
|
||||
if _, err := syscall.Setsid(); err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Failed to join cgroup: %v",
|
||||
fmt.Errorf("Failed setting sid: %v", err)), 1)
|
||||
}
|
||||
|
||||
syscall.Umask(0)
|
||||
|
||||
// Redirect logs.
|
||||
stdo, err := os.OpenFile(cmd.StdoutFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdout: %v", err), 1)
|
||||
}
|
||||
|
||||
stde, err := os.OpenFile(cmd.StderrFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stderr: %v", err), 1)
|
||||
}
|
||||
|
||||
stdi, err := os.OpenFile(cmd.StdinFile, os.O_CREATE|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error opening file to redirect Stdin: %v", err), 1)
|
||||
}
|
||||
|
||||
cmd.Stdout = stdo
|
||||
cmd.Stderr = stde
|
||||
cmd.Stdin = stdi
|
||||
|
||||
// Spawn the user process.
|
||||
if err := cmd.Cmd.Start(); err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error starting user command: %v", err), 1)
|
||||
}
|
||||
|
||||
// Indicate that the command was started successfully.
|
||||
c.outputStartStatus(nil, 0)
|
||||
|
||||
// Wait and then output the exit status.
|
||||
exitStatus := &SpawnExitStatus{}
|
||||
if err := cmd.Wait(); err == nil {
|
||||
exitStatus.Success = true
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.Encode(exitStatus)
|
||||
|
||||
return 0
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// +build !linux
|
||||
|
||||
package command
|
||||
|
||||
import "errors"
|
||||
|
||||
func (c *SpawnDaemonCommand) Run(args []string) int {
|
||||
return c.outputStartStatus(errors.New("spawn-daemon not supported"), 1)
|
||||
}
|
Loading…
Reference in New Issue