Skip to content

Commit

Permalink
Refactor Get and Describe to allow extension of types
Browse files Browse the repository at this point in the history
Get should use ResourceMapper, allow Printer to be abstracted,
and extract Describe as *Describer types.
  • Loading branch information
smarterclayton committed Nov 4, 2014
1 parent 39882a3 commit 09cfa36
Show file tree
Hide file tree
Showing 18 changed files with 750 additions and 336 deletions.
2 changes: 1 addition & 1 deletion cmd/kubectl/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ import (
)

func main() {
cmd.RunKubectl(os.Stdout)
cmd.NewFactory().Run(os.Stdout)
}
2 changes: 1 addition & 1 deletion pkg/client/fake_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (c *FakePods) List(selector labels.Selector) (*api.PodList, error) {

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

func (c *FakePods) Delete(name string) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/fake_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (c *FakeServices) List(selector labels.Selector) (*api.ServiceList, error)

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

func (c *FakeServices) Create(service *api.Service) (*api.Service, error) {
Expand Down
52 changes: 34 additions & 18 deletions pkg/kubectl/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand All @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
17 changes: 9 additions & 8 deletions pkg/kubectl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand Down
33 changes: 19 additions & 14 deletions pkg/kubectl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
},
}
Expand Down
59 changes: 57 additions & 2 deletions pkg/kubectl/cmd/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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")
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
Loading

0 comments on commit 09cfa36

Please sign in to comment.