mirror of
https://github.com/clearlinux/rkt.git
synced 2026-09-01 11:26:05 +00:00
*: split out rkt executable, add CLI framework
In preparation for adding other top-level rkt commands (other than "run"), break out stage0 and add a simple framework for adding commmands.
This commit is contained in:
@@ -13,8 +13,8 @@ export GOPATH=${GOPATH}:${PWD}/gopath
|
||||
|
||||
eval $(go env)
|
||||
|
||||
echo "Building rkt (stage0)..."
|
||||
go build -o $GOBIN/rkt ${REPO_PATH}/stage0
|
||||
echo "Building rkt..."
|
||||
go build -o $GOBIN/rkt ${REPO_PATH}/cmd
|
||||
|
||||
echo "Building init (stage1)..."
|
||||
go build -o $GOBIN/init ${REPO_PATH}/stage1
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/coreos-inc/rkt/rkt"
|
||||
)
|
||||
|
||||
var (
|
||||
cmdHelp = &Command{
|
||||
Name: "help",
|
||||
Summary: "Show a list of commands or help for one command",
|
||||
Usage: "[COMMAND]",
|
||||
Description: "Show a list of commands or detailed help for one command",
|
||||
Run: runHelp,
|
||||
}
|
||||
|
||||
globalUsageTemplate *template.Template
|
||||
commandUsageTemplate *template.Template
|
||||
templFuncs = template.FuncMap{
|
||||
"descToLines": func(s string) []string {
|
||||
// trim leading/trailing whitespace and split into slice of lines
|
||||
return strings.Split(strings.Trim(s, "\n\t "), "\n")
|
||||
},
|
||||
"printOption": func(name, defvalue, usage string) string {
|
||||
prefix := "--"
|
||||
if len(name) == 1 {
|
||||
prefix = "-"
|
||||
}
|
||||
return fmt.Sprintf("\t%s%s=%s\t%s", prefix, name, defvalue, usage)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
globalUsageTemplate = template.Must(template.New("global_usage").Funcs(templFuncs).Parse(`
|
||||
NAME:
|
||||
{{printf "\t%s - %s" .Executable .Description}}
|
||||
|
||||
USAGE:
|
||||
{{printf "\t%s" .Executable}} [global options] <command> [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
{{printf "\t%s" .Version}}
|
||||
|
||||
COMMANDS:{{range .Commands}}
|
||||
{{printf "\t%s\t%s" .Name .Summary}}{{end}}
|
||||
|
||||
GLOBAL OPTIONS:{{range .Flags}}
|
||||
{{printOption .Name .DefValue .Usage}}{{end}}
|
||||
|
||||
Run "{{.Executable}} help <command>" for more details on a specific command.
|
||||
`[1:]))
|
||||
commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(`
|
||||
NAME:
|
||||
{{printf "\t%s - %s" .Cmd.Name .Cmd.Summary}}
|
||||
|
||||
USAGE:
|
||||
{{printf "\t%s %s %s" .Executable .Cmd.Name .Cmd.Usage}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{range $line := descToLines .Cmd.Description}}{{printf "\t%s" $line}}
|
||||
{{end}}
|
||||
{{if .CmdFlags}}OPTIONS:{{range .CmdFlags}}
|
||||
{{printOption .Name .DefValue .Usage}}{{end}}
|
||||
|
||||
{{end}}For help on global options run "{{.Executable}} help"
|
||||
`[1:]))
|
||||
}
|
||||
|
||||
func runHelp(args []string) (exit int) {
|
||||
if len(args) < 1 {
|
||||
printGlobalUsage()
|
||||
return
|
||||
}
|
||||
|
||||
var cmd *Command
|
||||
|
||||
for _, c := range commands {
|
||||
if c.Name == args[0] {
|
||||
cmd = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if cmd == nil {
|
||||
fmt.Fprintf(os.Stderr, "Unrecognized command: %s\n", args[0])
|
||||
return 1
|
||||
}
|
||||
|
||||
printCommandUsage(cmd)
|
||||
return
|
||||
}
|
||||
|
||||
func printGlobalUsage() {
|
||||
globalUsageTemplate.Execute(out, struct {
|
||||
Executable string
|
||||
Commands []*Command
|
||||
Flags []*flag.Flag
|
||||
Description string
|
||||
Version string
|
||||
}{
|
||||
cliName,
|
||||
commands,
|
||||
getAllFlags(),
|
||||
cliDescription,
|
||||
rkt.Version,
|
||||
})
|
||||
out.Flush()
|
||||
}
|
||||
|
||||
func printCommandUsage(cmd *Command) {
|
||||
commandUsageTemplate.Execute(out, struct {
|
||||
Executable string
|
||||
Cmd *Command
|
||||
CmdFlags []*flag.Flag
|
||||
}{
|
||||
cliName,
|
||||
cmd,
|
||||
getFlags(&cmd.Flags),
|
||||
})
|
||||
out.Flush()
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
const (
|
||||
cliName = "rkt"
|
||||
cliDescription = "rkt, the application container runner"
|
||||
)
|
||||
|
||||
var (
|
||||
globalFlagset = flag.NewFlagSet(cliName, flag.ExitOnError)
|
||||
out *tabwriter.Writer
|
||||
commands []*Command
|
||||
globalFlags = struct {
|
||||
Dir string
|
||||
Debug bool
|
||||
Help bool
|
||||
}{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
globalFlagset.BoolVar(&globalFlags.Help, "help", false, "Print usage information and exit")
|
||||
globalFlagset.BoolVar(&globalFlags.Debug, "debug", false, "Print out more debug information to stderr")
|
||||
globalFlagset.StringVar(&globalFlags.Dir, "dir", "", "rocket data directory")
|
||||
}
|
||||
|
||||
type Command struct {
|
||||
Name string // Name of the Command and the string to use to invoke it
|
||||
Summary string // One-sentence summary of what the Command does
|
||||
Usage string // Usage options/arguments
|
||||
Description string // Detailed description of command
|
||||
Flags flag.FlagSet // Set of flags associated with this command
|
||||
|
||||
Run func(args []string) int // Run a command with the given arguments, return exit status
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
out = new(tabwriter.Writer)
|
||||
out.Init(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
commands = []*Command{
|
||||
cmdHelp,
|
||||
cmdRun,
|
||||
cmdVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// parse global arguments
|
||||
globalFlagset.Parse(os.Args[1:])
|
||||
args := globalFlagset.Args()
|
||||
if len(args) < 1 || globalFlags.Help {
|
||||
args = []string{"help"}
|
||||
}
|
||||
|
||||
var cmd *Command
|
||||
|
||||
// determine which Command should be run
|
||||
for _, c := range commands {
|
||||
if c.Name == args[0] {
|
||||
cmd = c
|
||||
if err := c.Flags.Parse(args[1:]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if cmd == nil {
|
||||
fmt.Fprintf(os.Stderr, "%v: unknown subcommand: %q\n", cliName, args[0])
|
||||
fmt.Fprintf(os.Stderr, "Run '%v help' for usage.\n", cliName)
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Exit(cmd.Run(cmd.Flags.Args()))
|
||||
}
|
||||
|
||||
func getAllFlags() (flags []*flag.Flag) {
|
||||
return getFlags(globalFlagset)
|
||||
}
|
||||
|
||||
func getFlags(flagset *flag.FlagSet) (flags []*flag.Flag) {
|
||||
flags = make([]*flag.Flag, 0)
|
||||
flagset.VisitAll(func(f *flag.Flag) {
|
||||
flags = append(flags, f)
|
||||
})
|
||||
return
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos-inc/rkt/stage0"
|
||||
)
|
||||
|
||||
var (
|
||||
flagStage1Init string
|
||||
flagStage1Rootfs string
|
||||
flagVolumes volumeMap
|
||||
cmdRun = &Command{
|
||||
Name: "run",
|
||||
Summary: "Run image(s) in an application container in rocket",
|
||||
Usage: "[--volume LABEL:SOURCE] IMAGE...",
|
||||
Run: runRun,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdRun.Flags.StringVar(&flagStage1Init, "stage1-init", "./bin/init", "path to stage1 binary")
|
||||
cmdRun.Flags.StringVar(&flagStage1Rootfs, "stage1-rootfs", "./stage1-rootfs.tar.gz", "path to stage1 rootfs tarball")
|
||||
cmdRun.Flags.Var(&flagVolumes, "volume", "volumes to mount into the shared container environment")
|
||||
flagVolumes = volumeMap{}
|
||||
}
|
||||
|
||||
func runRun(args []string) (exit int) {
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintf(os.Stderr, "run: Must provide at least one image\n")
|
||||
return 1
|
||||
}
|
||||
cfg := stage0.Config{
|
||||
RktDir: globalFlags.Dir,
|
||||
Debug: globalFlags.Debug,
|
||||
Stage1Init: flagStage1Init,
|
||||
Stage1Rootfs: flagStage1Rootfs,
|
||||
Images: args,
|
||||
Volumes: flagVolumes,
|
||||
}
|
||||
stage0.Run(cfg) // execs, never returns
|
||||
return 1
|
||||
}
|
||||
|
||||
// volumeMap implements the flag.Value interface to contain a set of mappings
|
||||
// from mount label --> mount path
|
||||
type volumeMap map[string]string
|
||||
|
||||
func (vm *volumeMap) Set(s string) error {
|
||||
elems := strings.Split(s, ":")
|
||||
if len(elems) != 2 {
|
||||
return errors.New("volume must be of form key:path")
|
||||
}
|
||||
key := elems[0]
|
||||
if _, ok := (*vm)[key]; ok {
|
||||
return fmt.Errorf("got multiple flags for volume %q", key)
|
||||
}
|
||||
(*vm)[key] = elems[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *volumeMap) String() string {
|
||||
var ss []string
|
||||
for k, v := range *vm {
|
||||
ss = append(ss, fmt.Sprintf("%s:%s", k, v))
|
||||
}
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos-inc/rkt/rkt"
|
||||
)
|
||||
|
||||
var cmdVersion = &Command{
|
||||
Name: "version",
|
||||
Description: "Print the version and exit",
|
||||
Summary: "Print the version and exit",
|
||||
Run: runVersion,
|
||||
}
|
||||
|
||||
func runVersion(args []string) (exit int) {
|
||||
fmt.Printf("rkt version %s\n", rkt.Version)
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package stage0
|
||||
|
||||
//
|
||||
// Rocket is a reference implementation of the app container specification.
|
||||
@@ -29,15 +29,12 @@ import (
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
// WARNING: here be dragons
|
||||
@@ -48,42 +45,27 @@ import (
|
||||
"github.com/coreos-inc/rkt/rkt"
|
||||
)
|
||||
|
||||
var (
|
||||
fs = flag.NewFlagSet("rkt", flag.ExitOnError)
|
||||
|
||||
flagDebug bool
|
||||
flagDir string
|
||||
flagStage1Init string
|
||||
flagStage1Rfs string
|
||||
flagVolumes volumeMap
|
||||
)
|
||||
type Config struct {
|
||||
RktDir string
|
||||
Stage1Init string
|
||||
Stage1Rootfs string
|
||||
Debug bool
|
||||
Images []string
|
||||
Volumes map[string]string
|
||||
}
|
||||
|
||||
func init() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
fs.BoolVar(&flagDebug, "debug", false, "output debugging log information")
|
||||
fs.StringVar(&flagDir, "dir", "", "directory in which to create container filesystem")
|
||||
fs.StringVar(&flagStage1Init, "stage1-init", "./bin/init", "path to stage1 binary")
|
||||
fs.StringVar(&flagStage1Rfs, "stage1-rootfs", "./stage1-rootfs.tar.gz", "path to stage1 rootfs tarball")
|
||||
fs.Var(&flagVolumes, "volume", "volumes to mount into the shared container environment")
|
||||
flagVolumes = volumeMap{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fs.Parse(os.Args[1:])
|
||||
if flagDebug {
|
||||
func Run(cfg Config) {
|
||||
if cfg.Debug {
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
args := fs.Args()
|
||||
if len(args) < 2 || args[0] != "run" {
|
||||
fmt.Fprintf(os.Stderr, "usage: rkt run [OPTION]... IMAGE...\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
images := args[1:]
|
||||
dir := flagDir
|
||||
if dir == "" {
|
||||
log.Printf("-dir unset - using temporary directory")
|
||||
if cfg.RktDir == "" {
|
||||
log.Printf("rktDir unset - using temporary directory")
|
||||
var err error
|
||||
dir, err = ioutil.TempDir("", "rkt")
|
||||
cfg.RktDir, err = ioutil.TempDir("", "rkt")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating temporary directory: %v", err)
|
||||
}
|
||||
@@ -95,13 +77,16 @@ func main() {
|
||||
log.Fatalf("error creating UID: %v", err)
|
||||
}
|
||||
|
||||
// Create a directory for this container
|
||||
dir := filepath.Join(cfg.RktDir, cuid.String())
|
||||
|
||||
// - Creating a filesystem for the container
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
log.Fatalf("error creating directory: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Writing stage1 rootfs")
|
||||
fh, err := os.Open(flagStage1Rfs)
|
||||
fh, err := os.Open(cfg.Stage1Rootfs)
|
||||
if err != nil {
|
||||
log.Fatalf("error opening stage1 rootfs: %v", err)
|
||||
}
|
||||
@@ -118,7 +103,7 @@ func main() {
|
||||
}
|
||||
|
||||
log.Printf("Writing stage1 init")
|
||||
in, err := os.Open(flagStage1Init)
|
||||
in, err := os.Open(cfg.Stage1Init)
|
||||
if err != nil {
|
||||
log.Fatalf("error loading stage1 binary: %v", err)
|
||||
}
|
||||
@@ -156,7 +141,7 @@ func main() {
|
||||
// TODO(jonboulle): clarify imagehash<->appname. Right now we have to
|
||||
// unpack the entire TAF to access the manifest which contains the appname.
|
||||
|
||||
for _, img := range images {
|
||||
for _, img := range cfg.Images {
|
||||
h, err := types.NewHash(img)
|
||||
if err != nil {
|
||||
log.Fatalf("bad hash given: %v", err)
|
||||
@@ -224,7 +209,7 @@ func main() {
|
||||
}
|
||||
|
||||
var sVols []types.Volume
|
||||
for key, path := range flagVolumes {
|
||||
for key, path := range cfg.Volumes {
|
||||
v := types.Volume{
|
||||
Kind: "host",
|
||||
Source: path,
|
||||
@@ -255,8 +240,8 @@ func main() {
|
||||
|
||||
log.Printf("Execing stage1/init")
|
||||
init := "stage1/init"
|
||||
args = []string{init}
|
||||
if flagDebug {
|
||||
args := []string{init}
|
||||
if cfg.Debug {
|
||||
args = append(args, "debug")
|
||||
}
|
||||
if err := syscall.Exec(init, args, os.Environ()); err != nil {
|
||||
@@ -269,28 +254,3 @@ func main() {
|
||||
func genUID() string {
|
||||
return "6733C088-A507-4694-AABF-EDBE4FC5266F"
|
||||
}
|
||||
|
||||
// volumeMap implements the flag.Value interface to contain a set of mappings
|
||||
// from mount label --> mount path
|
||||
type volumeMap map[string]string
|
||||
|
||||
func (vm *volumeMap) Set(s string) error {
|
||||
elems := strings.Split(s, ":")
|
||||
if len(elems) != 2 {
|
||||
return errors.New("volume must be of form key:path")
|
||||
}
|
||||
key := elems[0]
|
||||
if _, ok := (*vm)[key]; ok {
|
||||
return fmt.Errorf("got multiple flags for volume %q", key)
|
||||
}
|
||||
(*vm)[key] = elems[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *volumeMap) String() string {
|
||||
var ss []string
|
||||
for k, v := range *vm {
|
||||
ss = append(ss, fmt.Sprintf("%s:%s", k, v))
|
||||
}
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
Reference in New Issue
Block a user