1771 lines
46 KiB
Go
1771 lines
46 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package expose
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
|
"k8s.io/client-go/rest/fake"
|
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
|
"k8s.io/kubectl/pkg/scheme"
|
|
)
|
|
|
|
func TestRunExposeService(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
ns string
|
|
calls map[string]string
|
|
input runtime.Object
|
|
flags map[string]string
|
|
output runtime.Object
|
|
expected string
|
|
status int
|
|
}{
|
|
{
|
|
name: "expose-service-from-service-no-selector-defined",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
expected: "service/foo exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service-from-service",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
},
|
|
},
|
|
expected: "service/foo exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "no-name-passed-from-the-cli",
|
|
args: []string{"service", "mayor"},
|
|
ns: "default",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/default/services/mayor",
|
|
"POST": "/namespaces/default/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"run": "this"},
|
|
},
|
|
},
|
|
// No --name flag specified below. Service will use the rc's name passed via the 'default-name' parameter
|
|
flags: map[string]string{"selector": "run=this", "port": "80", "labels": "runas=amayor"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "mayor", Namespace: "", Labels: map[string]string{"runas": "amayor"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 80,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
},
|
|
Selector: map[string]string{"run": "this"},
|
|
},
|
|
},
|
|
expected: "service/mayor exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "type": "LoadBalancer", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
Type: corev1.ServiceTypeLoadBalancer,
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-affinity-service",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "type": "LoadBalancer", "session-affinity": "ClientIP", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
Type: corev1.ServiceTypeLoadBalancer,
|
|
SessionAffinity: corev1.ServiceAffinityClientIP,
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service-cluster-ip",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "10.10.10.10", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
ClusterIP: "10.10.10.10",
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-headless-service",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "None", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-headless-service-no-port",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "name": "foo", "labels": "svc=test", "cluster-ip": "None", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{},
|
|
Selector: map[string]string{"func": "stream"},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-from-file",
|
|
args: []string{},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/redis-master",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "redis-master", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"filename": "../../../testdata/redis-master-service.yaml", "selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "truncate-name",
|
|
args: []string{"pod", "a-name-that-is-toooo-big-for-a-service-because-it-can-only-handle-63-characters"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/pods/a-name-that-is-toooo-big-for-a-service-because-it-can-only-handle-63-characters",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
},
|
|
flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod", "generator": "service/v2"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "a-name-that-is-toooo-big-for-a-service-because-it-can-only-handle-63-characters"[:63], Namespace: "", Labels: map[string]string{"svc": "frompod"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 90,
|
|
TargetPort: intstr.FromInt32(90),
|
|
},
|
|
},
|
|
Selector: map[string]string{"svc": "frompod"},
|
|
},
|
|
},
|
|
expected: "service/a-name-that-is-toooo-big-for-a-service-because-it-can-only-hand exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-multiport-object",
|
|
args: []string{"service", "foo"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/foo",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "multiport"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 80,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 443,
|
|
TargetPort: intstr.FromInt32(443),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 80,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 443,
|
|
TargetPort: intstr.FromInt32(443),
|
|
},
|
|
},
|
|
Selector: map[string]string{"svc": "fromfoo"},
|
|
},
|
|
},
|
|
expected: "service/fromfoo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-multiprotocol-object",
|
|
args: []string{"service", "foo"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/foo",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "multiport"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 80,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 8080,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 8081,
|
|
TargetPort: intstr.FromInt32(8081),
|
|
},
|
|
{
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 8082,
|
|
TargetPort: intstr.FromInt32(8082),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Protocol: corev1.ProtocolTCP,
|
|
Port: 80,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 8080,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
{
|
|
Name: "port-3",
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 8081,
|
|
TargetPort: intstr.FromInt32(8081),
|
|
},
|
|
{
|
|
Name: "port-4",
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 8082,
|
|
TargetPort: intstr.FromInt32(8082),
|
|
},
|
|
},
|
|
Selector: map[string]string{"svc": "fromfoo"},
|
|
},
|
|
},
|
|
expected: "service/fromfoo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service-from-service-no-selector-defined-sctp",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
expected: "service/foo exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service-from-service-sctp",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
},
|
|
},
|
|
expected: "service/foo exposed",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-service-cluster-ip-sctp",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "10.10.10.10", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
ClusterIP: "10.10.10.10",
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "expose-headless-service-sctp",
|
|
args: []string{"service", "baz"},
|
|
ns: "test",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/test/services/baz",
|
|
"POST": "/namespaces/test/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "None", "dry-run": "client"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolSCTP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
expected: "service/foo exposed (dry run)",
|
|
status: 200,
|
|
},
|
|
{
|
|
name: "namespace-yaml",
|
|
args: []string{"service", "baz"},
|
|
ns: "testns",
|
|
calls: map[string]string{
|
|
"GET": "/namespaces/testns/services/baz",
|
|
"POST": "/namespaces/testns/services",
|
|
},
|
|
input: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "testns", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
},
|
|
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "type": "LoadBalancer", "dry-run": "client", "output": "yaml"},
|
|
output: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"func": "stream"},
|
|
Type: corev1.ServiceTypeLoadBalancer,
|
|
},
|
|
},
|
|
expected: "namespace: testns",
|
|
status: 200,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
tf := cmdtesting.NewTestFactory().WithNamespace(test.ns)
|
|
defer tf.Cleanup()
|
|
|
|
codec := runtime.NewCodec(scheme.DefaultJSONEncoder(), scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...))
|
|
ns := scheme.Codecs.WithoutConversion()
|
|
|
|
tf.Client = &fake.RESTClient{
|
|
GroupVersion: schema.GroupVersion{Version: "v1"},
|
|
NegotiatedSerializer: ns,
|
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
|
switch p, m := req.URL.Path, req.Method; {
|
|
case p == test.calls[m] && m == "GET":
|
|
return &http.Response{StatusCode: test.status, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, test.input)}, nil
|
|
case p == test.calls[m] && m == "POST":
|
|
return &http.Response{StatusCode: test.status, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, test.output)}, nil
|
|
default:
|
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
|
return nil, nil
|
|
}
|
|
}),
|
|
}
|
|
|
|
ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
|
cmd := NewCmdExposeService(tf, ioStreams)
|
|
cmd.SetOut(buf)
|
|
cmd.SetErr(buf)
|
|
for flag, value := range test.flags {
|
|
cmd.Flags().Set(flag, value)
|
|
}
|
|
cmd.Run(cmd, test.args)
|
|
|
|
out := buf.String()
|
|
|
|
if test.expected == "" {
|
|
t.Errorf("%s: Invalid test case. Specify expected result.\n", test.name)
|
|
}
|
|
|
|
if !strings.Contains(out, test.expected) {
|
|
t.Errorf("%s: Unexpected output! Expected\n%s\ngot\n%s", test.name, test.expected, out)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExposeOverride(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
overrides string
|
|
overrideType string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "expose with merge override type should replace the entire spec",
|
|
overrides: `{"spec": {"ports": [{"protocol": "TCP", "port": 1111, "targetPort": 2222}]}, "selector": {"app": "go"}}`,
|
|
overrideType: "merge",
|
|
expected: `apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
creationTimestamp: null
|
|
labels:
|
|
svc: test
|
|
name: foo
|
|
namespace: test
|
|
spec:
|
|
ports:
|
|
- port: 1111
|
|
protocol: TCP
|
|
targetPort: 2222
|
|
selector:
|
|
app: go
|
|
status:
|
|
loadBalancer: {}
|
|
`,
|
|
},
|
|
{
|
|
name: "expose with strategic override type should add port before existing port",
|
|
overrides: `{"spec": {"ports": [{"protocol": "TCP", "port": 1111, "targetPort": 2222}]}}`,
|
|
overrideType: "strategic",
|
|
expected: `apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
creationTimestamp: null
|
|
labels:
|
|
svc: test
|
|
name: foo
|
|
namespace: test
|
|
spec:
|
|
ports:
|
|
- port: 1111
|
|
protocol: TCP
|
|
targetPort: 2222
|
|
- port: 14
|
|
protocol: UDP
|
|
targetPort: 14
|
|
selector:
|
|
app: go
|
|
status:
|
|
loadBalancer: {}
|
|
`,
|
|
},
|
|
{
|
|
name: "expose with json override type should add port before existing port",
|
|
overrides: `[
|
|
{"op": "add", "path": "/spec/ports/0", "value": {"port": 1111, "protocol": "TCP", "targetPort": 2222}}
|
|
]`,
|
|
overrideType: "json",
|
|
expected: `apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
creationTimestamp: null
|
|
labels:
|
|
svc: test
|
|
name: foo
|
|
namespace: test
|
|
spec:
|
|
ports:
|
|
- port: 1111
|
|
protocol: TCP
|
|
targetPort: 2222
|
|
- port: 14
|
|
protocol: UDP
|
|
targetPort: 14
|
|
selector:
|
|
app: go
|
|
status:
|
|
loadBalancer: {}
|
|
`,
|
|
},
|
|
{
|
|
name: "expose with json override type should add port after existing port",
|
|
overrides: `[
|
|
{"op": "add", "path": "/spec/ports/1", "value": {"port": 1111, "protocol": "TCP", "targetPort": 2222}}
|
|
]`,
|
|
overrideType: "json",
|
|
expected: `apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
creationTimestamp: null
|
|
labels:
|
|
svc: test
|
|
name: foo
|
|
namespace: test
|
|
spec:
|
|
ports:
|
|
- port: 14
|
|
protocol: UDP
|
|
targetPort: 14
|
|
- port: 1111
|
|
protocol: TCP
|
|
targetPort: 2222
|
|
selector:
|
|
app: go
|
|
status:
|
|
loadBalancer: {}
|
|
`,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
|
defer tf.Cleanup()
|
|
|
|
codec := runtime.NewCodec(scheme.DefaultJSONEncoder(), scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...))
|
|
ns := scheme.Codecs.WithoutConversion()
|
|
|
|
tf.Client = &fake.RESTClient{
|
|
GroupVersion: schema.GroupVersion{Version: "v1"},
|
|
NegotiatedSerializer: ns,
|
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
|
switch p, m := req.URL.Path, req.Method; {
|
|
case p == "/namespaces/test/services/baz" && m == "GET":
|
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
})}, nil
|
|
case p == "/namespaces/test/services" && m == "POST":
|
|
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Protocol: corev1.ProtocolUDP,
|
|
Port: 14,
|
|
TargetPort: intstr.FromInt32(14),
|
|
},
|
|
},
|
|
Selector: map[string]string{"app": "go"},
|
|
},
|
|
})}, nil
|
|
default:
|
|
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
|
return nil, nil
|
|
}
|
|
}),
|
|
}
|
|
|
|
ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
|
cmd := NewCmdExposeService(tf, ioStreams)
|
|
cmd.SetOut(buf)
|
|
cmd.Flags().Set("protocol", "UDP")
|
|
cmd.Flags().Set("port", "14")
|
|
cmd.Flags().Set("name", "foo")
|
|
cmd.Flags().Set("labels", "svc=test")
|
|
cmd.Flags().Set("dry-run", "client")
|
|
cmd.Flags().Set("overrides", test.overrides)
|
|
cmd.Flags().Set("override-type", test.overrideType)
|
|
cmd.Flags().Set("output", "yaml")
|
|
cmd.Run(cmd, []string{"service", "baz"})
|
|
|
|
out := buf.String()
|
|
|
|
if test.expected == "" {
|
|
t.Errorf("%s: Invalid test case. Specify expected result.\n", test.name)
|
|
}
|
|
|
|
if !strings.Contains(out, test.expected) {
|
|
t.Errorf("%s: Unexpected output! Expected\n%s\ngot\n%s", test.name, test.expected, out)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateService(t *testing.T) {
|
|
tests := map[string]struct {
|
|
selector string
|
|
name string
|
|
port string
|
|
protocol string
|
|
protocols string
|
|
targetPort string
|
|
clusterIP string
|
|
labels string
|
|
externalIP string
|
|
serviceType string
|
|
sessionAffinity string
|
|
setup func(t *testing.T, exposeServiceOptions *ExposeServiceOptions) func()
|
|
|
|
expected *corev1.Service
|
|
expectErr string
|
|
}{
|
|
"test1": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test2": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "UDP",
|
|
targetPort: "foobar",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "UDP",
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test3": {
|
|
selector: "foo=bar,baz=blah",
|
|
labels: "key1=value1,key2=value2",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
Labels: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test4": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "UDP",
|
|
externalIP: "1.2.3.4",
|
|
targetPort: "foobar",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "UDP",
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
ExternalIPs: []string{"1.2.3.4"},
|
|
},
|
|
},
|
|
},
|
|
"test5": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "UDP",
|
|
externalIP: "1.2.3.4",
|
|
serviceType: "LoadBalancer",
|
|
targetPort: "foobar",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "UDP",
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
Type: corev1.ServiceTypeLoadBalancer,
|
|
ExternalIPs: []string{"1.2.3.4"},
|
|
},
|
|
},
|
|
},
|
|
"test6": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "UDP",
|
|
targetPort: "foobar",
|
|
serviceType: string(corev1.ServiceTypeNodePort),
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "UDP",
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
Type: corev1.ServiceTypeNodePort,
|
|
},
|
|
},
|
|
},
|
|
"test7": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "UDP",
|
|
targetPort: "foobar",
|
|
serviceType: string(corev1.ServiceTypeNodePort),
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "UDP",
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
Type: corev1.ServiceTypeNodePort,
|
|
},
|
|
},
|
|
},
|
|
"test8": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test9": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
sessionAffinity: "ClientIP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
SessionAffinity: corev1.ServiceAffinityClientIP,
|
|
},
|
|
},
|
|
},
|
|
"test10": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
clusterIP: "10.10.10.10",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
ClusterIP: "10.10.10.10",
|
|
},
|
|
},
|
|
},
|
|
"test11": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "TCP",
|
|
clusterIP: "None",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "TCP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
"test12": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,443",
|
|
protocol: "TCP",
|
|
targetPort: "foobar",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 443,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test13": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,443",
|
|
protocol: "UDP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 443,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test14": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,443",
|
|
protocol: "TCP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 443,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(443),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test15": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,8080",
|
|
protocols: "8080/UDP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 8080,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test16": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,8080,8081",
|
|
protocols: "8080/UDP,8081/TCP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 8080,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
{
|
|
Name: "port-3",
|
|
Port: 8081,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(8081),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test17": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
protocol: "TCP",
|
|
clusterIP: "None",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
"test18": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
clusterIP: "None",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
"test19": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test20": {
|
|
selector: "foo=bar,baz=blah",
|
|
labels: "key1=value1,key2=value2",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
Labels: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test21": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test22": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
sessionAffinity: "ClientIP",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
SessionAffinity: corev1.ServiceAffinityClientIP,
|
|
},
|
|
},
|
|
},
|
|
"test23": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
clusterIP: "10.10.10.10",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
ClusterIP: "10.10.10.10",
|
|
},
|
|
},
|
|
},
|
|
"test24": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
port: "80",
|
|
protocol: "SCTP",
|
|
clusterIP: "None",
|
|
targetPort: "1234",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
|
|
Port: 80,
|
|
Protocol: "SCTP",
|
|
TargetPort: intstr.FromInt32(1234),
|
|
},
|
|
},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
"test25": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,443",
|
|
protocol: "SCTP",
|
|
targetPort: "foobar",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 443,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromString("foobar"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test26": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,443",
|
|
protocol: "SCTP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 443,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromInt32(443),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test27": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,8080",
|
|
protocols: "8080/SCTP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 8080,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test28": {
|
|
selector: "foo=bar",
|
|
name: "test",
|
|
port: "80,8080,8081,8082",
|
|
protocols: "8080/UDP,8081/TCP,8082/SCTP",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1",
|
|
Port: 80,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(80),
|
|
},
|
|
{
|
|
Name: "port-2",
|
|
Port: 8080,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(8080),
|
|
},
|
|
{
|
|
Name: "port-3",
|
|
Port: 8081,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(8081),
|
|
},
|
|
{
|
|
Name: "port-4",
|
|
Port: 8082,
|
|
Protocol: corev1.ProtocolSCTP,
|
|
TargetPort: intstr.FromInt32(8082),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"test 29": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
clusterIP: "None",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
// Fixed #114402 kubectl expose fails for apps with same-port, different-protocol
|
|
"test #114402": {
|
|
selector: "foo=bar,baz=blah",
|
|
name: "test",
|
|
clusterIP: "None",
|
|
protocols: "53/TCP,53/UDP",
|
|
port: "53",
|
|
expected: &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"foo": "bar",
|
|
"baz": "blah",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "port-1-tcp",
|
|
Port: 53,
|
|
Protocol: corev1.ProtocolTCP,
|
|
TargetPort: intstr.FromInt32(53),
|
|
},
|
|
{
|
|
Name: "port-1-udp",
|
|
Port: 53,
|
|
Protocol: corev1.ProtocolUDP,
|
|
TargetPort: intstr.FromInt32(53),
|
|
},
|
|
},
|
|
ClusterIP: corev1.ClusterIPNone,
|
|
},
|
|
},
|
|
},
|
|
"check selector": {
|
|
name: "test",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
clusterIP: "None",
|
|
expectErr: `selector must be specified`,
|
|
},
|
|
"check name": {
|
|
selector: "foo=bar,baz=blah",
|
|
protocol: "SCTP",
|
|
targetPort: "1234",
|
|
clusterIP: "None",
|
|
expectErr: `name must be specified`,
|
|
},
|
|
}
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
exposeServiceOptions := ExposeServiceOptions{
|
|
Selector: test.selector,
|
|
Name: test.name,
|
|
Protocol: test.protocol,
|
|
Protocols: test.protocols,
|
|
Port: test.port,
|
|
ClusterIP: test.clusterIP,
|
|
TargetPort: test.targetPort,
|
|
Labels: test.labels,
|
|
ExternalIP: test.externalIP,
|
|
Type: test.serviceType,
|
|
SessionAffinity: test.sessionAffinity,
|
|
}
|
|
|
|
service, err := exposeServiceOptions.createService()
|
|
if test.expectErr == "" {
|
|
require.NoError(t, err)
|
|
if !apiequality.Semantic.DeepEqual(service, test.expected) {
|
|
t.Errorf("\nexpected:\n%#v\ngot:\n%#v", test.expected, service)
|
|
}
|
|
} else {
|
|
require.Error(t, err)
|
|
require.EqualError(t, err, test.expectErr)
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
}
|