diff --git a/rkt/help.go b/rkt/help.go index 44f021f..211d070 100644 --- a/rkt/help.go +++ b/rkt/help.go @@ -34,9 +34,10 @@ var ( } helpFlags flag.FlagSet - globalUsageTemplate *template.Template - commandUsageTemplate *template.Template - templFuncs = template.FuncMap{ + globalUsageTemplate *template.Template + commandUsageTemplate *template.Template + subCommandUsageTemplate *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") @@ -86,6 +87,20 @@ DESCRIPTION: {{printOption .Name .DefValue .Usage}}{{end}} {{end}}For help on global options run "{{.Executable}} help" +`[1:])) + subCommandUsageTemplate = template.Must(template.New("subcommand_usage").Funcs(templFuncs).Parse(` +NAME: +{{printf "\t%s %s - %s" .CmdName .SubCmd.Name .SubCmd.Summary}} + +USAGE: +{{printf "\t%s %s %s %s" .Executable .CmdName .SubCmd.Name .SubCmd.Usage}} + +DESCRIPTION: +{{range $line := descToLines .SubCmd.Description}}{{printf "\t%s" $line}} +{{end}} +{{if .SubCmdFlags}}OPTIONS:{{range .SubCmdFlags}} +{{printOption .Name .DefValue .Usage}}{{end}} +{{end}} `[1:])) } @@ -95,7 +110,17 @@ func runHelp(args []string) (exit int) { return } - if err := printCommandUsageByName(args[0]); err != nil { + if len(args) == 1 { + if err := printCommandUsageByName(args[0]); err != nil { + printGlobalUsage() + stderr("\nHelp error: %v\n", err) + return 1 + } + return + } + + // Help for sub-commands. + if err := printSubCommandUsageByName(args[0], args[1], subCommands[args[0]]); err != nil { printGlobalUsage() stderr("\nHelp error: %v\n", err) return 1 @@ -151,3 +176,37 @@ func printCommandUsageByName(name string) error { return nil } + +func printSubCommandUsage(cmdName string, subCmd *Command) { + subCommandUsageTemplate.Execute(tabOut, struct { + Executable string + CmdName string + SubCmd *Command + SubCmdFlags []*flag.Flag + }{ + cliName, + cmdName, + subCmd, + getFlags(subCmd.Flags), + }) + tabOut.Flush() +} + +func printSubCommandUsageByName(name, subName string, subCommands []*Command) error { + var cmd *Command + + for _, c := range subCommands { + if c.Name == subName { + cmd = c + break + } + } + + if cmd == nil { + return fmt.Errorf("unrecognized sub-command: %s", subName) + } + + printSubCommandUsage(name, cmd) + + return nil +} diff --git a/rkt/image.go b/rkt/image.go new file mode 100644 index 0000000..a84533c --- /dev/null +++ b/rkt/image.go @@ -0,0 +1,65 @@ +// Copyright 2015 The rkt Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "os" +) + +var ( + cmdImage = &Command{ + Name: "image", + Summary: "Operate on an image in the local store", + Usage: "SUBCOMMAND IMAGE [args...]", + Description: `SUBCOMMAND could be "cat-manifest". IMAGE should be a string referencing an image; either a hash, local file on disk, or URL. +They will be checked in that order and the first match will be used.`, + Run: runImage, + Flags: &imageFlags, + } + imageFlags flag.FlagSet +) + +func init() { + commands = append(commands, cmdImage) +} + +func runImage(args []string) (exit int) { + if len(args) < 1 { + printCommandUsageByName("image") + return 1 + } + + var subCmd *Command + subArgs := args[1:] + + // determine which Command should be run + for _, c := range subCommands["image"] { + if c.Name == args[0] { + subCmd = c + if err := c.Flags.Parse(subArgs); err != nil { + stderr("%v", err) + os.Exit(2) + } + break + } + } + + if subCmd == nil { + stderr("image: unknown subcommand: %q", args[0]) + os.Exit(2) + } + return subCmd.Run(subArgs[subCmd.Flags.NFlag():]) +} diff --git a/rkt/image_cat_manifest.go b/rkt/image_cat_manifest.go new file mode 100644 index 0000000..8f9fb06 --- /dev/null +++ b/rkt/image_cat_manifest.go @@ -0,0 +1,93 @@ +// Copyright 2015 The rkt Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "encoding/json" + "flag" + + "github.com/coreos/rkt/store" +) + +var ( + cmdImageCatManifest = &Command{ + Name: "cat-manifest", + Summary: "Inspect and print the image manifest", + Usage: "IMAGE", + Description: `IMAGE should be a string referencing an image; either a hash, local file on disk, or URL. +They will be checked in that order and the first match will be used.`, + Run: runImageCatManifest, + Flags: &imageCatManifestFlag, + } + imageCatManifestFlag flag.FlagSet + flagPrettyPrint bool +) + +func init() { + subCommands["image"] = append(subCommands["image"], cmdImageCatManifest) + + imageCatManifestFlag.BoolVar(&flagPrettyPrint, "pretty-print", false, "apply indent to format the output") +} + +func runImageCatManifest(args []string) (exit int) { + if len(args) != 1 { + printSubCommandUsageByName("image", "cat-manifest", subCommands["image"]) + return 1 + } + + s, err := store.NewStore(globalFlags.Dir) + if err != nil { + stderr("image cat-manifest: cannot open store: %v\n", err) + return 1 + } + ks := getKeystore() + + fn := &finder{ + imageActionData: imageActionData{ + s: s, + ks: ks, + insecureSkipVerify: true, + debug: globalFlags.Debug, + }, + local: true, + withDeps: false, + } + + h, err := fn.findImage(args[0], "", true) + if err != nil { + stderr("image cat-manifest: cannot find image: %v\n", err) + return 1 + } + + manifest, err := fn.s.GetImageManifest(h.String()) + if err != nil { + stderr("image cat-manifest: cannot get image manifest: %v\n", err) + return 1 + } + + var b []byte + if flagPrettyPrint { + b, err = json.MarshalIndent(manifest, "", "\t") + } else { + b, err = json.Marshal(manifest) + } + if err != nil { + stderr("image cat-manifest: cannot read the image manifest: %v\n", err) + return 1 + } + + stdout(string(b)) + return 0 +} diff --git a/rkt/rkt.go b/rkt/rkt.go index 3eab8cd..2eeb2f1 100644 --- a/rkt/rkt.go +++ b/rkt/rkt.go @@ -40,6 +40,7 @@ var ( globalFlagset = flag.NewFlagSet(cliName, flag.ExitOnError) tabOut *tabwriter.Writer commands []*Command // Commands should register themselves by appending + subCommands = make(map[string][]*Command) globalFlags = struct { Dir string SystemConfigDir string