Skip to content

Commit 09cfa36

Browse files
Refactor Get and Describe to allow extension of types
Get should use ResourceMapper, allow Printer to be abstracted, and extract Describe as *Describer types.
1 parent 39882a3 commit 09cfa36

18 files changed

+750
-336
lines changed

cmd/kubectl/kubectl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ import (
2323
)
2424

2525
func main() {
26-
cmd.RunKubectl(os.Stdout)
26+
cmd.NewFactory().Run(os.Stdout)
2727
}

pkg/client/fake_pods.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (c *FakePods) List(selector labels.Selector) (*api.PodList, error) {
3535

3636
func (c *FakePods) Get(name string) (*api.Pod, error) {
3737
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "get-pod", Value: name})
38-
return &api.Pod{}, nil
38+
return &api.Pod{ObjectMeta: api.ObjectMeta{Name: name, Namespace: c.Namespace}}, nil
3939
}
4040

4141
func (c *FakePods) Delete(name string) error {

pkg/client/fake_services.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (c *FakeServices) List(selector labels.Selector) (*api.ServiceList, error)
3636

3737
func (c *FakeServices) Get(name string) (*api.Service, error) {
3838
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "get-service", Value: name})
39-
return &api.Service{}, nil
39+
return &api.Service{ObjectMeta: api.ObjectMeta{Name: name, Namespace: c.Namespace}}, nil
4040
}
4141

4242
func (c *FakeServices) Create(service *api.Service) (*api.Service, error) {

pkg/kubectl/cmd/cmd.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,38 @@ import (
3535
"github.com/spf13/cobra"
3636
)
3737

38+
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
39+
// of resources and different API sets.
3840
type Factory struct {
39-
Mapper meta.RESTMapper
40-
Typer runtime.ObjectTyper
41-
Client func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error)
41+
Mapper meta.RESTMapper
42+
Typer runtime.ObjectTyper
43+
Client func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error)
44+
Describer func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error)
45+
Printer func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
4246
}
4347

44-
func RunKubectl(out io.Writer) {
48+
// NewFactory creates a factory with the default Kubernetes resources defined
49+
func NewFactory() *Factory {
50+
return &Factory{
51+
Mapper: latest.RESTMapper,
52+
Typer: api.Scheme,
53+
Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
54+
return getKubeClient(cmd), nil
55+
},
56+
Describer: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) {
57+
describer, ok := kubectl.DescriberFor(mapping.Kind, getKubeClient(cmd))
58+
if !ok {
59+
return nil, fmt.Errorf("No description has been implemented for %q", mapping.Kind)
60+
}
61+
return describer, nil
62+
},
63+
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
64+
return kubectl.NewHumanReadablePrinter(noHeaders), nil
65+
},
66+
}
67+
}
68+
69+
func (f *Factory) Run(out io.Writer) {
4570
// Parent command to which all subcommands are added.
4671
cmds := &cobra.Command{
4772
Use: "kubectl",
@@ -52,15 +77,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
5277
Run: runHelp,
5378
}
5479

55-
factory := &Factory{
56-
Mapper: latest.NewDefaultRESTMapper(),
57-
Typer: api.Scheme,
58-
Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
59-
// Will handle all resources defined by the command
60-
return getKubeClient(cmd), nil
61-
},
62-
}
63-
6480
// Globally persistent flags across all subcommands.
6581
// TODO Change flag names to consts to allow safer lookup from subcommands.
6682
// 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.`,
7894

7995
cmds.AddCommand(NewCmdVersion(out))
8096
cmds.AddCommand(NewCmdProxy(out))
81-
cmds.AddCommand(NewCmdGet(out))
82-
cmds.AddCommand(NewCmdDescribe(out))
8397

84-
cmds.AddCommand(factory.NewCmdCreate(out))
85-
cmds.AddCommand(factory.NewCmdUpdate(out))
86-
cmds.AddCommand(factory.NewCmdDelete(out))
98+
cmds.AddCommand(f.NewCmdGet(out))
99+
cmds.AddCommand(f.NewCmdDescribe(out))
100+
cmds.AddCommand(f.NewCmdCreate(out))
101+
cmds.AddCommand(f.NewCmdUpdate(out))
102+
cmds.AddCommand(f.NewCmdDelete(out))
87103

88104
cmds.AddCommand(NewCmdNamespace(out))
89105
cmds.AddCommand(NewCmdLog(out))

pkg/kubectl/cmd/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Examples:
4747
client, err := f.Client(cmd, mapping)
4848
checkErr(err)
4949

50-
err = kubectl.NewRESTModifier(client, mapping).Create(namespace, data)
50+
err = kubectl.NewRESTHelper(client, mapping).Create(namespace, data)
5151
checkErr(err)
5252
fmt.Fprintf(out, "%s\n", name)
5353
},

pkg/kubectl/cmd/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Examples:
5454
client, err := f.Client(cmd, mapping)
5555
checkErr(err)
5656

57-
err = kubectl.NewRESTModifier(client, mapping).Delete(namespace, name)
57+
err = kubectl.NewRESTHelper(client, mapping).Delete(namespace, name)
5858
checkErr(err)
5959
fmt.Fprintf(out, "%s\n", name)
6060
},

pkg/kubectl/cmd/describe.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"fmt"
2021
"io"
2122

22-
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
2323
"github.com/spf13/cobra"
2424
)
2525

26-
func NewCmdDescribe(out io.Writer) *cobra.Command {
26+
func (f *Factory) NewCmdDescribe(out io.Writer) *cobra.Command {
2727
cmd := &cobra.Command{
2828
Use: "describe <resource> <id>",
2929
Short: "Show details of a specific resource",
@@ -32,13 +32,14 @@ func NewCmdDescribe(out io.Writer) *cobra.Command {
3232
This command joins many API calls together to form a detailed description of a
3333
given resource.`,
3434
Run: func(cmd *cobra.Command, args []string) {
35-
if len(args) < 2 {
36-
usageError(cmd, "Need to supply a resource and an ID")
37-
}
38-
resource := args[0]
39-
id := args[1]
40-
err := kubectl.Describe(out, getKubeClient(cmd), resource, id)
35+
mapping, namespace, name := ResourceFromArgs(cmd, args, f.Mapper)
36+
37+
describer, err := f.Describer(cmd, mapping)
38+
checkErr(err)
39+
40+
s, err := describer.Describe(namespace, name)
4141
checkErr(err)
42+
fmt.Fprintf(out, "%s\n", s)
4243
},
4344
}
4445
return cmd

pkg/kubectl/cmd/get.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import (
2020
"io"
2121

2222
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
23+
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
2324
"github.com/spf13/cobra"
2425
)
2526

26-
func NewCmdGet(out io.Writer) *cobra.Command {
27+
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
2728
cmd := &cobra.Command{
28-
Use: "get [(-o|--output=)table|json|yaml|template] [-t <file>|--template=<file>] <resource> [<id>]",
29+
Use: "get [(-o|--output=)console|json|yaml|...] <resource> [<id>]",
2930
Short: "Display one or many resources",
3031
Long: `Display one or many resources.
3132
@@ -44,20 +45,24 @@ Examples:
4445
$ kubectl get -f json pod 1234-56-7890-234234-456456
4546
<list single pod in json output format>`,
4647
Run: func(cmd *cobra.Command, args []string) {
47-
var resource, id string
48-
if len(args) == 0 {
49-
usageError(cmd, "Need to supply a resource.")
50-
}
51-
if len(args) >= 1 {
52-
resource = args[0]
53-
}
54-
if len(args) >= 2 {
55-
id = args[1]
56-
}
48+
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper)
49+
50+
selector := getFlagString(cmd, "selector")
51+
labels, err := labels.ParseSelector(selector)
52+
checkErr(err)
53+
54+
client, err := f.Client(cmd, mapping)
55+
checkErr(err)
56+
57+
obj, err := kubectl.NewRESTHelper(client, mapping).Get(namespace, name, labels)
58+
checkErr(err)
59+
5760
outputFormat := getFlagString(cmd, "output")
5861
templateFile := getFlagString(cmd, "template")
59-
selector := getFlagString(cmd, "selector")
60-
err := kubectl.Get(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), resource, id, selector, outputFormat, getFlagBool(cmd, "no-headers"), templateFile)
62+
defaultPrinter, err := f.Printer(cmd, mapping, getFlagBool(cmd, "no-headers"))
63+
checkErr(err)
64+
65+
err = kubectl.Print(out, obj, outputFormat, templateFile, defaultPrinter)
6166
checkErr(err)
6267
},
6368
}

pkg/kubectl/cmd/resource.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121

2222
"github.com/spf13/cobra"
2323

24-
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
2524
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
2625
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
2726
)
@@ -37,7 +36,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
3736

3837
if len(args) == 2 {
3938
resource := args[0]
40-
namespace = api.NamespaceDefault
39+
namespace = getKubeNamespace(cmd)
4140
name = args[1]
4241
if len(name) == 0 || len(resource) == 0 {
4342
usageError(cmd, "Must specify filename or command line params")
@@ -63,6 +62,62 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
6362
return
6463
}
6564

65+
// ResourceFromArgs expects two arguments with a given type, and extracts the fields necessary
66+
// to uniquely locate a resource. Displays a usageError if that contract is not satisfied, or
67+
// a generic error if any other problems occur.
68+
func ResourceFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
69+
if len(args) != 2 {
70+
usageError(cmd, "Must provide resource and name command line params")
71+
}
72+
73+
resource := args[0]
74+
namespace = getKubeNamespace(cmd)
75+
name = args[1]
76+
if len(name) == 0 || len(resource) == 0 {
77+
usageError(cmd, "Must provide resource and name command line params")
78+
}
79+
80+
version, kind, err := mapper.VersionAndKindForResource(resource)
81+
checkErr(err)
82+
83+
mapping, err = mapper.RESTMapping(version, kind)
84+
checkErr(err)
85+
return
86+
}
87+
88+
// ResourceFromArgs expects two arguments with a given type, and extracts the fields necessary
89+
// to uniquely locate a resource. Displays a usageError if that contract is not satisfied, or
90+
// a generic error if any other problems occur.
91+
func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
92+
if len(args) == 0 || len(args) > 2 {
93+
usageError(cmd, "Must provide resource or a resource and name as command line params")
94+
}
95+
96+
resource := args[0]
97+
if len(resource) == 0 {
98+
usageError(cmd, "Must provide resource or a resource and name as command line params")
99+
}
100+
101+
namespace = getKubeNamespace(cmd)
102+
if len(args) == 2 {
103+
name = args[1]
104+
if len(name) == 0 {
105+
usageError(cmd, "Must provide resource or a resource and name as command line params")
106+
}
107+
}
108+
109+
version, kind, err := mapper.VersionAndKindForResource(resource)
110+
checkErr(err)
111+
112+
mapping, err = mapper.RESTMapping(version, kind)
113+
checkErr(err)
114+
115+
return
116+
}
117+
118+
// ResourceFromFile retrieves the name and namespace from a valid file. If the file does not
119+
// resolve to a known type an error is returned. The returned mapping can be used to determine
120+
// the correct REST endpoint to modify this resource with.
66121
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
67122
configData, err := readConfigData(filename)
68123
checkErr(err)

pkg/kubectl/cmd/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Examples:
4747
client, err := f.Client(cmd, mapping)
4848
checkErr(err)
4949

50-
err = kubectl.NewRESTModifier(client, mapping).Update(namespace, name, true, data)
50+
err = kubectl.NewRESTHelper(client, mapping).Update(namespace, name, true, data)
5151
checkErr(err)
5252
fmt.Fprintf(out, "%s\n", name)
5353
},

0 commit comments

Comments
 (0)