mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-11 06:02:18 +00:00
Refactor Get and Describe to allow extension of types
Get should use ResourceMapper, allow Printer to be abstracted, and extract Describe as *Describer types.
This commit is contained in:
@@ -35,13 +35,38 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
|
||||
// of resources and different API sets.
|
||||
type Factory struct {
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Client func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error)
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Client func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error)
|
||||
Describer func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error)
|
||||
Printer func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
|
||||
}
|
||||
|
||||
func RunKubectl(out io.Writer) {
|
||||
// NewFactory creates a factory with the default Kubernetes resources defined
|
||||
func NewFactory() *Factory {
|
||||
return &Factory{
|
||||
Mapper: latest.RESTMapper,
|
||||
Typer: api.Scheme,
|
||||
Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
|
||||
return getKubeClient(cmd), nil
|
||||
},
|
||||
Describer: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
||||
describer, ok := kubectl.DescriberFor(mapping.Kind, getKubeClient(cmd))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("No description has been implemented for %q", mapping.Kind)
|
||||
}
|
||||
return describer, nil
|
||||
},
|
||||
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
|
||||
return kubectl.NewHumanReadablePrinter(noHeaders), nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Factory) Run(out io.Writer) {
|
||||
// Parent command to which all subcommands are added.
|
||||
cmds := &cobra.Command{
|
||||
Use: "kubectl",
|
||||
@@ -52,15 +77,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
Run: runHelp,
|
||||
}
|
||||
|
||||
factory := &Factory{
|
||||
Mapper: latest.NewDefaultRESTMapper(),
|
||||
Typer: api.Scheme,
|
||||
Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
|
||||
// Will handle all resources defined by the command
|
||||
return getKubeClient(cmd), nil
|
||||
},
|
||||
}
|
||||
|
||||
// Globally persistent flags across all subcommands.
|
||||
// TODO Change flag names to consts to allow safer lookup from subcommands.
|
||||
// TODO Add a verbose flag that turns on glog logging. Probably need a way
|
||||
@@ -78,12 +94,12 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
|
||||
cmds.AddCommand(NewCmdVersion(out))
|
||||
cmds.AddCommand(NewCmdProxy(out))
|
||||
cmds.AddCommand(NewCmdGet(out))
|
||||
cmds.AddCommand(NewCmdDescribe(out))
|
||||
|
||||
cmds.AddCommand(factory.NewCmdCreate(out))
|
||||
cmds.AddCommand(factory.NewCmdUpdate(out))
|
||||
cmds.AddCommand(factory.NewCmdDelete(out))
|
||||
cmds.AddCommand(f.NewCmdGet(out))
|
||||
cmds.AddCommand(f.NewCmdDescribe(out))
|
||||
cmds.AddCommand(f.NewCmdCreate(out))
|
||||
cmds.AddCommand(f.NewCmdUpdate(out))
|
||||
cmds.AddCommand(f.NewCmdDelete(out))
|
||||
|
||||
cmds.AddCommand(NewCmdNamespace(out))
|
||||
cmds.AddCommand(NewCmdLog(out))
|
||||
|
@@ -47,7 +47,7 @@ Examples:
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
err = kubectl.NewRESTModifier(client, mapping).Create(namespace, data)
|
||||
err = kubectl.NewRESTHelper(client, mapping).Create(namespace, data)
|
||||
checkErr(err)
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
},
|
||||
|
@@ -54,7 +54,7 @@ Examples:
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
err = kubectl.NewRESTModifier(client, mapping).Delete(namespace, name)
|
||||
err = kubectl.NewRESTHelper(client, mapping).Delete(namespace, name)
|
||||
checkErr(err)
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
},
|
||||
|
@@ -17,13 +17,13 @@ limitations under the License.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdDescribe(out io.Writer) *cobra.Command {
|
||||
func (f *Factory) NewCmdDescribe(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "describe <resource> <id>",
|
||||
Short: "Show details of a specific resource",
|
||||
@@ -32,13 +32,14 @@ func NewCmdDescribe(out io.Writer) *cobra.Command {
|
||||
This command joins many API calls together to form a detailed description of a
|
||||
given resource.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) < 2 {
|
||||
usageError(cmd, "Need to supply a resource and an ID")
|
||||
}
|
||||
resource := args[0]
|
||||
id := args[1]
|
||||
err := kubectl.Describe(out, getKubeClient(cmd), resource, id)
|
||||
mapping, namespace, name := ResourceFromArgs(cmd, args, f.Mapper)
|
||||
|
||||
describer, err := f.Describer(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
s, err := describer.Describe(namespace, name)
|
||||
checkErr(err)
|
||||
fmt.Fprintf(out, "%s\n", s)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
@@ -20,12 +20,13 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdGet(out io.Writer) *cobra.Command {
|
||||
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [(-o|--output=)table|json|yaml|template] [-t <file>|--template=<file>] <resource> [<id>]",
|
||||
Use: "get [(-o|--output=)console|json|yaml|...] <resource> [<id>]",
|
||||
Short: "Display one or many resources",
|
||||
Long: `Display one or many resources.
|
||||
|
||||
@@ -44,20 +45,24 @@ Examples:
|
||||
$ kubectl get -f json pod 1234-56-7890-234234-456456
|
||||
<list single pod in json output format>`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var resource, id string
|
||||
if len(args) == 0 {
|
||||
usageError(cmd, "Need to supply a resource.")
|
||||
}
|
||||
if len(args) >= 1 {
|
||||
resource = args[0]
|
||||
}
|
||||
if len(args) >= 2 {
|
||||
id = args[1]
|
||||
}
|
||||
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper)
|
||||
|
||||
selector := getFlagString(cmd, "selector")
|
||||
labels, err := labels.ParseSelector(selector)
|
||||
checkErr(err)
|
||||
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
obj, err := kubectl.NewRESTHelper(client, mapping).Get(namespace, name, labels)
|
||||
checkErr(err)
|
||||
|
||||
outputFormat := getFlagString(cmd, "output")
|
||||
templateFile := getFlagString(cmd, "template")
|
||||
selector := getFlagString(cmd, "selector")
|
||||
err := kubectl.Get(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), resource, id, selector, outputFormat, getFlagBool(cmd, "no-headers"), templateFile)
|
||||
defaultPrinter, err := f.Printer(cmd, mapping, getFlagBool(cmd, "no-headers"))
|
||||
checkErr(err)
|
||||
|
||||
err = kubectl.Print(out, obj, outputFormat, templateFile, defaultPrinter)
|
||||
checkErr(err)
|
||||
},
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
)
|
||||
@@ -37,7 +36,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
|
||||
if len(args) == 2 {
|
||||
resource := args[0]
|
||||
namespace = api.NamespaceDefault
|
||||
namespace = getKubeNamespace(cmd)
|
||||
name = args[1]
|
||||
if len(name) == 0 || len(resource) == 0 {
|
||||
usageError(cmd, "Must specify filename or command line params")
|
||||
@@ -63,6 +62,62 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
// a generic error if any other problems occur.
|
||||
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
if len(args) != 2 {
|
||||
usageError(cmd, "Must provide resource and name command line params")
|
||||
}
|
||||
|
||||
resource := args[0]
|
||||
namespace = getKubeNamespace(cmd)
|
||||
name = args[1]
|
||||
if len(name) == 0 || len(resource) == 0 {
|
||||
usageError(cmd, "Must provide resource and name command line params")
|
||||
}
|
||||
|
||||
version, kind, err := mapper.VersionAndKindForResource(resource)
|
||||
checkErr(err)
|
||||
|
||||
mapping, err = mapper.RESTMapping(version, kind)
|
||||
checkErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
// a generic error if any other problems occur.
|
||||
func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
if len(args) == 0 || len(args) > 2 {
|
||||
usageError(cmd, "Must provide resource or a resource and name as command line params")
|
||||
}
|
||||
|
||||
resource := args[0]
|
||||
if len(resource) == 0 {
|
||||
usageError(cmd, "Must provide resource or a resource and name as command line params")
|
||||
}
|
||||
|
||||
namespace = getKubeNamespace(cmd)
|
||||
if len(args) == 2 {
|
||||
name = args[1]
|
||||
if len(name) == 0 {
|
||||
usageError(cmd, "Must provide resource or a resource and name as command line params")
|
||||
}
|
||||
}
|
||||
|
||||
version, kind, err := mapper.VersionAndKindForResource(resource)
|
||||
checkErr(err)
|
||||
|
||||
mapping, err = mapper.RESTMapping(version, kind)
|
||||
checkErr(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ResourceFromFile retrieves the name and namespace from a valid file. If the file does not
|
||||
// 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.
|
||||
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
|
||||
configData, err := readConfigData(filename)
|
||||
checkErr(err)
|
||||
|
@@ -47,7 +47,7 @@ Examples:
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
err = kubectl.NewRESTModifier(client, mapping).Update(namespace, name, true, data)
|
||||
err = kubectl.NewRESTHelper(client, mapping).Update(namespace, name, true, data)
|
||||
checkErr(err)
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
},
|
||||
|
Reference in New Issue
Block a user