Skip to content

Commit

Permalink
create: add support for --bundle and --pid-file
Browse files Browse the repository at this point in the history
Co-authored-by: Samuel Karp <me@samuelkarp.com>
  • Loading branch information
dfr and samuelkarp committed Jun 7, 2022
1 parent fef0489 commit 889c370
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 12 deletions.
58 changes: 47 additions & 11 deletions cmd/runj/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"

"go.sbk.wtf/runj/jail"
"go.sbk.wtf/runj/oci"
Expand Down Expand Up @@ -45,8 +46,14 @@ import (
// integrations on top of runc expect this behavior, runj copies that at the
// expense of more complication in the codebase.
func createCommand() *cobra.Command {
var (
bundle string
consoleSocket string
pidFile string
)

create := &cobra.Command{
Use: "create <container-id> <path-to-bundle>",
Use: "create <container-id> [path-to-bundle]",
Short: "Create a new container with given ID and bundle",
Long: `Create a new container with given ID and bundle. IDs must be unique.
Expand All @@ -57,9 +64,17 @@ filesystem.
The specification file includes an args parameter. The args parameter is used
to specify command(s) that get run when the container is started. To change the
command(s) that get executed on start, edit the args parameter of the spec.`,
Args: cobra.ExactArgs(2),
Args: cobra.RangeArgs(1, 2),
PreRunE: func(cmd *cobra.Command, args []string) error {
bundle := args[1]
if len(args) == 2 {
if bundle != "" {
return fmt.Errorf("must specify bundle via argument (%q) or flag (%q), not both", args[1], bundle)
}
bundle = args[1]
}
if bundle == "" {
return errors.New("bundle is required, specify via argument or --bundle flag")
}
bundleConfig := filepath.Join(bundle, oci.ConfigFileName)
fInfo, err := os.Stat(bundleConfig)
if err != nil {
Expand All @@ -71,16 +86,29 @@ command(s) that get executed on start, edit the args parameter of the spec.`,
return nil
},
}
consoleSocket := create.Flags().String(
flags := create.Flags()
flags.StringVarP(
&bundle,
"bundle",
"b",
"",
"path to the root of the bundle directory")
flags.StringVar(
&consoleSocket,
"console-socket",
"",
`path to an AF_UNIX socket which will receive a
file descriptor referencing the master end of
the console's pseudoterminal`)
flags.StringVar(
&pidFile,
"pid-file",
"",
`specify a file where the process ID will be
written`)
create.RunE = func(cmd *cobra.Command, args []string) (err error) {
disableUsage(cmd)
id := args[0]
bundle := args[1]
var s *state.State
s, err = state.Create(id, bundle)
if err != nil {
Expand Down Expand Up @@ -121,15 +149,15 @@ the console's pseudoterminal`)
}
// console socket validation
if ociConfig.Process.Terminal {
if *consoleSocket == "" {
if consoleSocket == "" {
return errors.New("console-socket is required when Process.Terminal is true")
}
if socketStat, err := os.Stat(*consoleSocket); err != nil {
return fmt.Errorf("failed to stat console socket %q: %w", *consoleSocket, err)
if socketStat, err := os.Stat(consoleSocket); err != nil {
return fmt.Errorf("failed to stat console socket %q: %w", consoleSocket, err)
} else if socketStat.Mode()&os.ModeSocket != os.ModeSocket {
return fmt.Errorf("console-socket %q is not a socket", *consoleSocket)
return fmt.Errorf("console-socket %q is not a socket", consoleSocket)
}
} else if *consoleSocket != "" {
} else if consoleSocket != "" {
return errors.New("console-socket provided but Process.Terminal is false")
}
var confPath string
Expand All @@ -154,13 +182,21 @@ the console's pseudoterminal`)
// Setup and start the "runj-entrypoint" helper program in order to
// get the container STDIO hooked up properly.
var entrypoint *exec.Cmd
entrypoint, err = jail.SetupEntrypoint(id, true, ociConfig.Process.Args, ociConfig.Process.Env, *consoleSocket)
entrypoint, err = jail.SetupEntrypoint(id, true, ociConfig.Process.Args, ociConfig.Process.Env, consoleSocket)
if err != nil {
return err
}
// the runj-entrypoint pid will become the container process's pid
// through a series of exec(2) calls
s.PID = entrypoint.Process.Pid
if pidFile != "" {
pidValue := strconv.Itoa(s.PID)
err = os.WriteFile(pidFile, []byte(pidValue), 0o666)
if err != nil {
return err
}
}

return nil
}
return create
Expand Down
4 changes: 3 additions & 1 deletion docs/oci.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ spec](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md#crea
as requiring two positional arguments: `<container-id> <path-to-bundle>`.
However, `runc` (the reference implementation of the spec) accepts only a single
positional argument (`<container-id>`) and instead uses either the current
working directory as the bundle or accepts it through the `-b`/`bundle` flag.
working directory as the bundle or accepts it through the `-b`/`--bundle` flag.
See [here](https://github.com/opencontainers/runc/blob/2cf8d240075dd322b9385100c9af4b149c973391/create.go#L12-L30).
For compatibility with runc and other integrations, runj now supports the flag
in addition to the positional argument form.

## Non-terminal STDIO

Expand Down

0 comments on commit 889c370

Please sign in to comment.