diff --git a/pkg/handler/tcp-services.go b/pkg/handler/tcp-services.go index f6d65386..a216c2cf 100644 --- a/pkg/handler/tcp-services.go +++ b/pkg/handler/tcp-services.go @@ -25,6 +25,7 @@ type tcpSvcParser struct { service *store.Service port int64 sslOffload bool + annList map[string]string } func (handler TCPServices) Update(k store.K8s, h haproxy.HAProxy, a annotations.Annotations) (reload bool, err error) { @@ -65,6 +66,7 @@ func (handler TCPServices) parseTCPService(store store.K8s, input string) (p tcp // parts[0]: Service Name // parts[1]: Service Port // parts[2]: SSL option + // parts[3]: Annotations separated by commas ex: annotation1=value,annotation2=value parts := strings.Split(input, ":") if len(parts) < 2 { err = fmt.Errorf("incorrect format '%s', 'ServiceName:ServicePort' is required", input) @@ -72,11 +74,25 @@ func (handler TCPServices) parseTCPService(store store.K8s, input string) (p tcp } svcName := strings.Split(parts[0], "/") svcPort := parts[1] - if len(parts) > 2 { + if len(parts) == 3 { if parts[2] == "ssl" { p.sslOffload = true } } + if len(parts) > 3 { + if parts[2] == "ssl" { + p.sslOffload = true + } + svcOpts := strings.Split(parts[3], ",") + annList := map[string]string{} + for _, v := range svcOpts { + ann := strings.Split(v, "=") + if len(ann) > 0 { + annList[ann[0]] = ann[1] + } + } + p.annList = annList + } if len(svcName) != 2 { err = fmt.Errorf("incorrect Service Name '%s'. Should be in 'ServiceNS/ServiceName' format", parts[0]) return @@ -198,7 +214,7 @@ func (handler TCPServices) updateTCPFrontend(k store.K8s, h haproxy.HAProxy, fro SvcPortInt: p.port, IsDefaultBackend: true, } - if svc, err = service.New(k, path, nil, true); err == nil { + if svc, err = service.New(k, path, nil, true, p.annList); err == nil { r, err = svc.SetDefaultBackend(k, h, []string{frontend.Name}, a) } return reload || r, err diff --git a/pkg/handler/tcp-services_test.go b/pkg/handler/tcp-services_test.go new file mode 100644 index 00000000..507ab413 --- /dev/null +++ b/pkg/handler/tcp-services_test.go @@ -0,0 +1,122 @@ +package handler + +import ( + "reflect" + "testing" + + "github.com/haproxytech/kubernetes-ingress/pkg/store" +) + +var ( + tcpServivesTest = TCPServices{} + k8sStoreTest = store.K8s{ + Namespaces: map[string]*store.Namespace{}, + } +) + +func TestParseTCPService(t *testing.T) { + type ioTest struct { + input string + output tcpSvcParser + } + // initialize + k8sStoreTest.Namespaces["ns"] = &store.Namespace{ + Name: "ns", + Relevant: false, + Ingresses: map[string]*store.Ingress{}, + Endpoints: map[string]map[string]*store.Endpoints{}, + Services: map[string]*store.Service{}, + Secret: map[string]*store.Secret{}, + HAProxyRuntime: map[string]map[string]*store.RuntimeBackend{}, + CRs: &store.CustomResources{}, + Gateways: map[string]*store.Gateway{}, + TCPRoutes: map[string]*store.TCPRoute{}, + ReferenceGrants: map[string]*store.ReferenceGrant{}, + Labels: map[string]string{}, + Status: "", + } + k8sStoreTest.Namespaces["ns"].Services["svc"] = &store.Service{ + Namespace: "ns", + Name: "svc", + Ports: []store.ServicePort{}, + Addresses: []string{}, + DNS: "", + Annotations: map[string]string{}, + Status: "", + } + ioParserTest := [...]ioTest{ + { + "ns/svc:8888", + tcpSvcParser{ + service: &store.Service{ + Namespace: "ns", + Name: "svc", + }, + port: 8888, + sslOffload: false, + annList: map[string]string{}, + }, + }, + { + "ns/svc:8888:ssl", + tcpSvcParser{ + service: &store.Service{ + Namespace: "ns", + Name: "svc", + }, + port: 8888, + sslOffload: true, + annList: map[string]string{}, + }, + }, + { + "ns/svc:8888:ssl:load-balance=leastconn,default-server=check", + tcpSvcParser{ + service: &store.Service{ + Namespace: "ns", + Name: "svc", + }, + port: 8888, + sslOffload: true, + annList: map[string]string{ + "load-balance": "leastconn", + "default-server": "check", + }, + }, + }, + { + "ns/svc:8888:ssl:load-balance=leastconn,default-server=check:todo", + tcpSvcParser{ + service: &store.Service{ + Namespace: "ns", + Name: "svc", + }, + port: 8888, + sslOffload: true, + annList: map[string]string{ + "load-balance": "leastconn", + "default-server": "check", + }, + }, + }, + } + + for _, v := range ioParserTest { + p, err := tcpServivesTest.parseTCPService(k8sStoreTest, v.input) + if err != nil { + t.Errorf("got error %v", err) + } + if !reflect.DeepEqual(p.annList, v.output.annList) && (len(p.annList) != 0 && len(v.output.annList) != 0) { + t.Errorf("got %v, wanted %v", p.annList, v.output.annList) + } + if p.port != v.output.port { + t.Errorf("got %v, wanted %v", p.port, v.output.port) + } + if p.service.Name != v.output.service.Name || p.service.Namespace != v.output.service.Namespace { + t.Errorf("got %v, wanted %v", p.port, v.output.service.Ports[0]) + } + if p.sslOffload != v.output.sslOffload { + t.Errorf("got %v, wanted %v", p.sslOffload, v.output.sslOffload) + } + } +}