From 98689a99ec0abd31bc6bee8672ded885f0898c0c Mon Sep 17 00:00:00 2001 From: feihujiang Date: Wed, 26 Aug 2015 16:06:40 +0800 Subject: [PATCH] Limit the type for kubectl expose command --- docs/man/man1/kubectl-expose.1 | 9 +++++--- docs/user-guide/kubectl/kubectl.md | 4 ++-- docs/user-guide/kubectl/kubectl_expose.md | 11 +++++---- hack/lib/test.sh | 18 +++++++++++++++ hack/test-cmd.sh | 7 ++++++ pkg/kubectl/cmd/cmd_test.go | 8 +++++-- pkg/kubectl/cmd/expose.go | 13 +++++++---- pkg/kubectl/cmd/util/factory.go | 8 +++++++ pkg/kubectl/cmd/util/factory_test.go | 27 +++++++++++++++++++++++ 9 files changed, 90 insertions(+), 15 deletions(-) diff --git a/docs/man/man1/kubectl-expose.1 b/docs/man/man1/kubectl-expose.1 index 18a2d871095..b93b58c4855 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl expose \- Take a replicated application and expose it as Kubernetes Service +kubectl expose \- Take a replication controller, service or pod and expose it as a new Kubernetes Service .SH SYNOPSIS @@ -13,10 +13,10 @@ kubectl expose \- Take a replicated application and expose it as Kubernetes Serv .SH DESCRIPTION .PP -Take a replicated application and expose it as Kubernetes Service. +Take a replication controller, service or pod and expose it as a new Kubernetes Service. .PP -Looks up a replication controller or service by name and uses the selector for that resource as the +Looks up a replication controller, service or pod by name and uses the selector for that resource as the selector for a new Service on the specified port. If no labels are specified, the new service will re\-use the labels from the resource it exposes. @@ -219,6 +219,9 @@ $ kubectl expose rc nginx \-\-port=80 \-\-target\-port=8000 # Create a service for a replication controller identified by type and name specified in "nginx\-controller.yaml", which serves on port 80 and connects to the containers on port 8000. $ kubectl expose \-f nginx\-controller.yaml \-\-port=80 \-\-target\-port=8000 +# Create a service for a pod valid\-pod, which serves on port 444 with the name "frontend" +$ kubectl expose pod valid\-pod \-\-port=444 \-\-name=frontend + # Create a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx\-https" $ kubectl expose service nginx \-\-port=443 \-\-target\-port=8443 \-\-name=nginx\-https diff --git a/docs/user-guide/kubectl/kubectl.md b/docs/user-guide/kubectl/kubectl.md index a9be3c5f030..32f2e3eca1d 100644 --- a/docs/user-guide/kubectl/kubectl.md +++ b/docs/user-guide/kubectl/kubectl.md @@ -85,7 +85,7 @@ kubectl * [kubectl delete](kubectl_delete.md) - Delete resources by filenames, stdin, resources and names, or by resources and label selector. * [kubectl describe](kubectl_describe.md) - Show details of a specific resource or group of resources * [kubectl exec](kubectl_exec.md) - Execute a command in a container. -* [kubectl expose](kubectl_expose.md) - Take a replicated application and expose it as Kubernetes Service +* [kubectl expose](kubectl_expose.md) - Take a replication controller, service or pod and expose it as a new Kubernetes Service * [kubectl get](kubectl_get.md) - Display one or many resources * [kubectl label](kubectl_label.md) - Update the labels on a resource * [kubectl logs](kubectl_logs.md) - Print the logs for a container in a pod. @@ -100,7 +100,7 @@ kubectl * [kubectl stop](kubectl_stop.md) - Deprecated: Gracefully shut down a resource by name or filename. * [kubectl version](kubectl_version.md) - Print the client and server version information. -###### Auto generated by spf13/cobra at 2015-09-10 18:53:03.165115265 +0000 UTC +###### Auto generated by spf13/cobra at 2015-09-11 06:17:55.670147499 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index 9d57bd83446..42748f60a95 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -33,14 +33,14 @@ Documentation for other releases can be found at ## kubectl expose -Take a replicated application and expose it as Kubernetes Service +Take a replication controller, service or pod and expose it as a new Kubernetes Service ### Synopsis -Take a replicated application and expose it as Kubernetes Service. +Take a replication controller, service or pod and expose it as a new Kubernetes Service. -Looks up a replication controller or service by name and uses the selector for that resource as the +Looks up a replication controller, service or pod by name and uses the selector for that resource as the selector for a new Service on the specified port. If no labels are specified, the new service will re-use the labels from the resource it exposes. @@ -57,6 +57,9 @@ $ kubectl expose rc nginx --port=80 --target-port=8000 # Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml", which serves on port 80 and connects to the containers on port 8000. $ kubectl expose -f nginx-controller.yaml --port=80 --target-port=8000 +# Create a service for a pod valid-pod, which serves on port 444 with the name "frontend" +$ kubectl expose pod valid-pod --port=444 --name=frontend + # Create a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx-https" $ kubectl expose service nginx --port=443 --target-port=8443 --name=nginx-https @@ -122,7 +125,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-09-11 03:36:48.458259032 +0000 UTC +###### Auto generated by spf13/cobra at 2015-09-14 01:48:52.452547937 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]() diff --git a/hack/lib/test.sh b/hack/lib/test.sh index 9cda34ea56b..4a34f2ea902 100644 --- a/hack/lib/test.sh +++ b/hack/lib/test.sh @@ -132,3 +132,21 @@ kube::test::describe_resource_assert() { echo -n ${reset} return 0 } + +kube::test::if_has_string() { + local message=$1 + local match=$2 + + if [[ $(echo "$message" | grep "$match") ]]; then + echo "Successful" + echo "message:$message" + echo "has:$match" + return 0 + else + echo "FAIL!" + echo "message:$message" + echo "has not:$match" + caller + return 1 + fi +} diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index ea58af617a7..9ba887a11f0 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -680,6 +680,13 @@ __EOF__ kubectl delete pod valid-pod "${kube_flags[@]}" kubectl delete service frontend{,-2,-3,-4,-5} "${kube_flags[@]}" + ### Expose negative invalid resource test + # Pre-condition: don't need + # Command + output_message=$(! kubectl expose nodes 127.0.0.1 2>&1 "${kube_flags[@]}") + # Post-condition: the error message has "invalid resource" string + kube::test::if_has_string "${output_message}" 'invalid resource' + ### Delete replication controller with id # Pre-condition: frontend replication controller is running kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" 'frontend:' diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index c65497e5d53..c943e8a3bab 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -227,15 +227,19 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { ClientConfig: func() (*client.Config, error) { return t.ClientConfig, t.Err }, + CanBeExposed: func(kind string) error { + if kind != "ReplicationController" && kind != "Service" && kind != "Pod" { + return fmt.Errorf("invalid resource provided: %v, only a replication controller, service or pod is accepted", kind) + } + return nil + }, Generator: func(name string) (kubectl.Generator, bool) { generator, ok := generators[name] return generator, ok }, } - rf := cmdutil.NewFactory(nil) f.PodSelectorForObject = rf.PodSelectorForObject - return f, t, testapi.Default.Codec() } diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index a7e81c3bbaa..514f5d99d68 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -34,9 +34,9 @@ type ExposeOptions struct { } const ( - expose_long = `Take a replicated application and expose it as Kubernetes Service. + expose_long = `Take a replication controller, service or pod and expose it as a new Kubernetes Service. -Looks up a replication controller or service by name and uses the selector for that resource as the +Looks up a replication controller, service or pod by name and uses the selector for that resource as the selector for a new Service on the specified port. If no labels are specified, the new service will re-use the labels from the resource it exposes.` @@ -46,6 +46,9 @@ $ kubectl expose rc nginx --port=80 --target-port=8000 # Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml", which serves on port 80 and connects to the containers on port 8000. $ kubectl expose -f nginx-controller.yaml --port=80 --target-port=8000 +# Create a service for a pod valid-pod, which serves on port 444 with the name "frontend" +$ kubectl expose pod valid-pod --port=444 --name=frontend + # Create a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx-https" $ kubectl expose service nginx --port=443 --target-port=8443 --name=nginx-https @@ -58,7 +61,7 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [----external-ip=external-ip-of-service] [--type=type]", - Short: "Take a replicated application and expose it as Kubernetes Service", + Short: "Take a replication controller, service or pod and expose it as a new Kubernetes Service", Long: expose_long, Example: expose_example, Run: func(cmd *cobra.Command, args []string) { @@ -113,7 +116,9 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } info := infos[0] mapping := info.ResourceMapping() - + if err := f.CanBeExposed(mapping.Kind); err != nil { + return err + } // Get the input object inputObject, err := r.Object() if err != nil { diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 2d90df2ade5..86e245ef749 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -86,6 +86,8 @@ type Factory struct { DefaultNamespace func() (string, bool, error) // Returns the generator for the provided generator name Generator func(name string) (kubectl.Generator, bool) + // Check whether the kind of resources could be exposed + CanBeExposed func(kind string) error } // NewFactory creates a factory with the default Kubernetes resources defined @@ -246,6 +248,12 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { generator, ok := generators[name] return generator, ok }, + CanBeExposed: func(kind string) error { + if kind != "ReplicationController" && kind != "Service" && kind != "Pod" { + return fmt.Errorf("invalid resource provided: %v, only a replication controller, service or pod is accepted", kind) + } + return nil + }, } } diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index a6dd840418b..62df19046e2 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -165,6 +165,33 @@ func TestLabelsForObject(t *testing.T) { } } +func TestCanBeExposed(t *testing.T) { + factory := NewFactory(nil) + tests := []struct { + kind string + expectErr bool + }{ + { + kind: "ReplicationController", + expectErr: false, + }, + { + kind: "Node", + expectErr: true, + }, + } + + for _, test := range tests { + err := factory.CanBeExposed(test.kind) + if test.expectErr && err == nil { + t.Error("unexpected non-error") + } + if !test.expectErr && err != nil { + t.Errorf("unexpected error: %v", err) + } + } +} + func TestFlagUnderscoreRenaming(t *testing.T) { factory := NewFactory(nil)