diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 443e19425a3..f19497b37b1 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -702,6 +702,18 @@ __EOF__ # Post-condition: the error message has "invalid resource" string kube::test::if_has_string "${output_message}" 'invalid resource' + ### Try to generate a service with invalid name (exceeding maximum valid size) + # Pre-condition: use --name flag + output_message=$(! kubectl expose -f hack/testdata/pod-with-large-name.yaml --name=invalid-large-service-name --port=8081 2>&1 "${kube_flags[@]}") + # Post-condition: should fail due to invalid name + kube::test::if_has_string "${output_message}" 'metadata.name: invalid value' + # Pre-condition: default run without --name flag; should succeed by truncating the inherited name + output_message=$(kubectl expose -f hack/testdata/pod-with-large-name.yaml --port=8081 2>&1 "${kube_flags[@]}") + # Post-condition: inherited name from pod has been truncated + kube::test::if_has_string "${output_message}" '\"kubernetes-serve-hostnam\" exposed' + # Clean-up + kubectl delete svc kubernetes-serve-hostnam "${kube_flags[@]}" + ### 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/hack/testdata/pod-with-large-name.yaml b/hack/testdata/pod-with-large-name.yaml new file mode 100644 index 00000000000..c8deebc11a8 --- /dev/null +++ b/hack/testdata/pod-with-large-name.yaml @@ -0,0 +1,11 @@ +# Used for testing name truncation in kubectl expose +apiVersion: v1 +kind: Pod +metadata: + name: kubernetes-serve-hostname + labels: + name: kubernetes-serve-hostname +spec: + containers: + - name: kubernetes-serve-hostname + image: gcr.io/google_containers/serve_hostname diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 514f5d99d68..45bce2dcb94 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/util/validation" ) // ExposeOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of @@ -133,7 +134,11 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) - params["default-name"] = info.Name + name := info.Name + if len(name) > validation.DNS952LabelMaxLength { + name = name[:validation.DNS952LabelMaxLength] + } + params["default-name"] = name // For objects that need a pod selector, derive it from the exposed object in case a user // didn't explicitly specify one via --selector diff --git a/pkg/kubectl/cmd/expose_test.go b/pkg/kubectl/cmd/expose_test.go index c7876912a41..c944b2bdd4c 100644 --- a/pkg/kubectl/cmd/expose_test.go +++ b/pkg/kubectl/cmd/expose_test.go @@ -136,7 +136,7 @@ func TestRunExposeService(t *testing.T) { status: 200, }, { - name: "expose-external-service", + name: "expose-service", args: []string{"service", "baz"}, ns: "test", calls: map[string]string{ @@ -168,7 +168,7 @@ func TestRunExposeService(t *testing.T) { status: 200, }, { - name: "expose-external-affinity-service", + name: "expose-affinity-service", args: []string{"service", "baz"}, ns: "test", calls: map[string]string{ @@ -226,11 +226,40 @@ func TestRunExposeService(t *testing.T) { TargetPort: util.NewIntOrStringFromInt(90), }, }, + Selector: map[string]string{"svc": "fromexternal"}, }, }, expected: "service \"frombaz\" exposed", status: 200, }, + { + name: "truncate-name", + args: []string{"pod", "a-name-that-is-toooo-big-for-a-service"}, + ns: "test", + calls: map[string]string{ + "GET": "/namespaces/test/pods/a-name-that-is-toooo-big-for-a-service", + "POST": "/namespaces/test/services", + }, + input: &api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, + }, + flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod", "generator": "service/v2"}, + output: &api.Service{ + ObjectMeta: api.ObjectMeta{Name: "a-name-that-is-toooo-big", Namespace: "", Labels: map[string]string{"svc": "frompod"}}, + Spec: api.ServiceSpec{ + Ports: []api.ServicePort{ + { + Protocol: api.ProtocolTCP, + Port: 90, + TargetPort: util.NewIntOrStringFromInt(90), + }, + }, + Selector: map[string]string{"svc": "frompod"}, + }, + }, + expected: "service \"a-name-that-is-toooo-big\" exposed", + status: 200, + }, } for _, test := range tests {