package util import ( "errors" "fmt" "io" "path/filepath" "strings" "github.com/spf13/cobra" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/runtime" ) // ErrExit is a marker interface for cli commands indicating that the response has been processed var ErrExit = fmt.Errorf("exit directly") // ReplaceCommandName recursively processes the examples in a given command to change a hardcoded // command name (like 'kubectl' to the appropriate target name). It returns c. func ReplaceCommandName(from, to string, c *cobra.Command) *cobra.Command { c.Example = strings.Replace(c.Example, from, to, -1) for _, sub := range c.Commands() { ReplaceCommandName(from, to, sub) } return c } // RequireNoArguments exits with a usage error if extra arguments are provided. func RequireNoArguments(c *cobra.Command, args []string) { if len(args) > 0 { kcmdutil.CheckErr(kcmdutil.UsageError(c, fmt.Sprintf(`unknown command "%s"`, strings.Join(args, " ")))) } } func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) { return func(c *cobra.Command, args []string) { c.SetOutput(out) RequireNoArguments(c, args) c.Help() } } // GetDisplayFilename returns the absolute path of the filename as long as there was no error, otherwise it returns the filename as-is func GetDisplayFilename(filename string) string { if absName, err := filepath.Abs(filename); err == nil { return absName } return filename } // ResolveResource returns the resource type and name of the resourceString. // If the resource string has no specified type, defaultResource will be returned. func ResolveResource(defaultResource unversioned.GroupResource, resourceString string, mapper meta.RESTMapper) (unversioned.GroupResource, string, error) { if mapper == nil { return unversioned.GroupResource{}, "", errors.New("mapper cannot be nil") } var name string parts := strings.Split(resourceString, "/") switch len(parts) { case 1: name = parts[0] case 2: name = parts[1] // Allow specifying the group the same way kubectl does, as "resource.group.name" groupResource := unversioned.ParseGroupResource(parts[0]) // normalize resource case groupResource.Resource = strings.ToLower(groupResource.Resource) gvr, err := mapper.ResourceFor(groupResource.WithVersion("")) if err != nil { return unversioned.GroupResource{}, "", err } return gvr.GroupResource(), name, nil default: return unversioned.GroupResource{}, "", fmt.Errorf("invalid resource format: %s", resourceString) } return defaultResource, name, nil } // convertItemsForDisplay returns a new list that contains parallel elements that have been converted to the most preferred external version func convertItemsForDisplay(objs []runtime.Object, preferredVersions ...unversioned.GroupVersion) ([]runtime.Object, error) { ret := []runtime.Object{} for i := range objs { obj := objs[i] kind, _, err := kapi.Scheme.ObjectKind(obj) if err != nil { return nil, err } groupMeta, err := registered.Group(kind.Group) if err != nil { return nil, err } requestedVersion := unversioned.GroupVersion{} for _, preferredVersion := range preferredVersions { if preferredVersion.Group == kind.Group { requestedVersion = preferredVersion break } } actualOutputVersion := unversioned.GroupVersion{} for _, externalVersion := range groupMeta.GroupVersions { if externalVersion == requestedVersion { actualOutputVersion = externalVersion break } if actualOutputVersion.IsEmpty() { actualOutputVersion = externalVersion } } convertedObject, err := kapi.Scheme.ConvertToVersion(obj, actualOutputVersion) if err != nil { return nil, err } ret = append(ret, convertedObject) } return ret, nil } // convertItemsForDisplayFromDefaultCommand returns a new list that contains parallel elements that have been converted to the most preferred external version // TODO: move this function into the core factory PrintObjects method // TODO: print-objects should have preferred output versions func convertItemsForDisplayFromDefaultCommand(cmd *cobra.Command, objs []runtime.Object) ([]runtime.Object, error) { requested := kcmdutil.GetFlagString(cmd, "output-version") version, err := unversioned.ParseGroupVersion(requested) if err != nil { return nil, err } return convertItemsForDisplay(objs, version) } // VersionedPrintObject handles printing an object in the appropriate version by looking at 'output-version' // on the command func VersionedPrintObject(fn func(*cobra.Command, meta.RESTMapper, runtime.Object, io.Writer) error, c *cobra.Command, mapper meta.RESTMapper, out io.Writer) func(runtime.Object) error { return func(obj runtime.Object) error { // TODO: fold into the core printer functionality (preferred output version) if list, ok := obj.(*kapi.List); ok { var err error if list.Items, err = convertItemsForDisplayFromDefaultCommand(c, list.Items); err != nil { return err } } else { result, err := convertItemsForDisplayFromDefaultCommand(c, []runtime.Object{obj}) if err != nil { return err } obj = result[0] } return fn(c, mapper, obj, out) } }