Make kubectl commands return errors and centralize exit handling

This commit is contained in:
Jeff Lowdermilk
2015-03-09 15:08:16 -07:00
parent 7b72d9539f
commit cd7d78b696
20 changed files with 875 additions and 644 deletions

View File

@@ -31,10 +31,11 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func CheckErr(err error) {
@@ -52,25 +53,26 @@ func CheckErr(err error) {
}
}
func usageError(cmd *cobra.Command, format string, args ...interface{}) {
glog.Errorf(format, args...)
glog.Errorf("See '%s -h' for help.", cmd.CommandPath())
os.Exit(1)
func UsageError(cmd *cobra.Command, format string, args ...interface{}) error {
msg := fmt.Sprintf(format, args...)
return fmt.Errorf("%s\nsee '%s -h' for help.", msg, cmd.CommandPath())
}
func getFlag(cmd *cobra.Command, flag string) *pflag.Flag {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
return f
}
func GetFlagString(cmd *cobra.Command, flag string) string {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
f := getFlag(cmd, flag)
return f.Value.String()
}
func GetFlagBool(cmd *cobra.Command, flag string) bool {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
f := getFlag(cmd, flag)
result, err := strconv.ParseBool(f.Value.String())
if err != nil {
glog.Fatalf("Invalid value for a boolean flag: %s", f.Value.String())
@@ -80,24 +82,22 @@ func GetFlagBool(cmd *cobra.Command, flag string) bool {
// Assumes the flag has a default value.
func GetFlagInt(cmd *cobra.Command, flag string) int {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
f := getFlag(cmd, flag)
v, err := strconv.Atoi(f.Value.String())
// This is likely not a sufficiently friendly error message, but cobra
// should prevent non-integer values from reaching here.
CheckErr(err)
if err != nil {
glog.Fatalf("unable to convert flag value to int: %v", err)
}
return v
}
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
f := getFlag(cmd, flag)
v, err := time.ParseDuration(f.Value.String())
CheckErr(err)
if err != nil {
glog.Fatalf("unable to convert flag value to Duration: %v", err)
}
return v
}

View File

@@ -27,26 +27,29 @@ import (
)
// ResourceFromArgs expects two arguments with a given type, and extracts the fields necessary
// to uniquely locate a resource. Displays a usageError if that contract is not satisfied, or
// to uniquely locate a resource. Displays a UsageError if that contract is not satisfied, or
// a generic error if any other problems occur.
// DEPRECATED: Use resource.Builder
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper, cmdNamespace string) (mapping *meta.RESTMapping, namespace, name string) {
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper, cmdNamespace string) (mapping *meta.RESTMapping, namespace, name string, err error) {
if len(args) != 2 {
usageError(cmd, "Must provide resource and name command line params")
err = UsageError(cmd, "Must provide resource and name command line params")
return
}
resource := args[0]
namespace = cmdNamespace
name = args[1]
if len(name) == 0 || len(resource) == 0 {
usageError(cmd, "Must provide resource and name command line params")
err = UsageError(cmd, "Must provide resource and name command line params")
return
}
version, kind, err := mapper.VersionAndKindForResource(resource)
CheckErr(err)
if err != nil {
return
}
mapping, err = mapper.RESTMapping(kind, version)
CheckErr(err)
return
}
@@ -54,41 +57,52 @@ func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper,
// resolve to a known type an error is returned. The returned mapping can be used to determine
// the correct REST endpoint to modify this resource with.
// DEPRECATED: Use resource.Builder
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema, cmdVersion string) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
configData, err := ReadConfigData(filename)
CheckErr(err)
data = configData
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema, cmdVersion string) (mapping *meta.RESTMapping, namespace, name string, data []byte, err error) {
data, err = ReadConfigData(filename)
if err != nil {
return
}
objVersion, kind, err := typer.DataVersionAndKind(data)
CheckErr(err)
if err != nil {
return
}
// TODO: allow unversioned objects?
if len(objVersion) == 0 {
CheckErr(fmt.Errorf("the resource in the provided file has no apiVersion defined"))
err = fmt.Errorf("the resource in the provided file has no apiVersion defined")
}
err = schema.ValidateBytes(data)
CheckErr(err)
if err != nil {
return
}
// decode using the version stored with the object (allows codec to vary across versions)
mapping, err = mapper.RESTMapping(kind, objVersion)
CheckErr(err)
if err != nil {
return
}
obj, err := mapping.Codec.Decode(data)
CheckErr(err)
if err != nil {
return
}
meta := mapping.MetadataAccessor
namespace, err = meta.Namespace(obj)
CheckErr(err)
if err != nil {
return
}
name, err = meta.Name(obj)
CheckErr(err)
if err != nil {
return
}
// if the preferred API version differs, get a different mapper
if cmdVersion != objVersion {
mapping, err = mapper.RESTMapping(kind, cmdVersion)
CheckErr(err)
}
return
}