Add named port support to Kubernetes IngressRoute CRDs
This commit is contained in:
parent
b1ddd0e038
commit
bbee63fcf3
10 changed files with 154 additions and 57 deletions
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -323,18 +324,18 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
|||
return conf
|
||||
}
|
||||
|
||||
func getServicePort(svc *corev1.Service, port int32) (*corev1.ServicePort, error) {
|
||||
func getServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.ServicePort, error) {
|
||||
if svc == nil {
|
||||
return nil, errors.New("service is not defined")
|
||||
}
|
||||
|
||||
if port == 0 {
|
||||
if (port.Type == intstr.Int && port.IntVal == 0) || (port.Type == intstr.String && port.StrVal == "") {
|
||||
return nil, errors.New("ingressRoute service port not defined")
|
||||
}
|
||||
|
||||
hasValidPort := false
|
||||
for _, p := range svc.Spec.Ports {
|
||||
if p.Port == port {
|
||||
if (port.Type == intstr.Int && port.IntVal == p.Port) || (port.Type == intstr.String && port.StrVal == p.Name) {
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
|
@ -343,8 +344,8 @@ func getServicePort(svc *corev1.Service, port int32) (*corev1.ServicePort, error
|
|||
}
|
||||
}
|
||||
|
||||
if svc.Spec.Type != corev1.ServiceTypeExternalName {
|
||||
return nil, fmt.Errorf("service port not found: %d", port)
|
||||
if svc.Spec.Type != corev1.ServiceTypeExternalName || port.Type == intstr.String {
|
||||
return nil, fmt.Errorf("service port not found: %s", &port)
|
||||
}
|
||||
|
||||
if hasValidPort {
|
||||
|
@ -352,7 +353,7 @@ func getServicePort(svc *corev1.Service, port int32) (*corev1.ServicePort, error
|
|||
Warning("The port %d from IngressRoute doesn't match with ports defined in the ExternalName service %s/%s.", port, svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
return &corev1.ServicePort{Port: port}, nil
|
||||
return &corev1.ServicePort{Port: port.IntVal}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) createErrorPageMiddleware(client Client, namespace string, errorPage *v1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -399,7 +400,7 @@ func (c configBuilder) nameAndService(ctx context.Context, parentNamespace strin
|
|||
|
||||
return fullName, serversLB, nil
|
||||
case service.Kind == "TraefikService":
|
||||
return fullServiceName(svcCtx, namespace, service, 0), nil, nil
|
||||
return fullServiceName(svcCtx, namespace, service, intstr.FromInt(0)), nil, nil
|
||||
default:
|
||||
return "", nil, fmt.Errorf("unsupported service kind %s", service.Kind)
|
||||
}
|
||||
|
@ -414,9 +415,9 @@ func splitSvcNameProvider(name string) (string, string) {
|
|||
return svc, pvd
|
||||
}
|
||||
|
||||
func fullServiceName(ctx context.Context, namespace string, service v1alpha1.LoadBalancerSpec, port int32) string {
|
||||
if port != 0 {
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s-%d", namespace, service.Name, port))
|
||||
func fullServiceName(ctx context.Context, namespace string, service v1alpha1.LoadBalancerSpec, port intstr.IntOrString) string {
|
||||
if (port.Type == intstr.Int && port.IntVal != 0) || (port.Type == intstr.String && port.StrVal != "") {
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s-%s", namespace, service.Name, &port))
|
||||
}
|
||||
|
||||
if !strings.Contains(service.Name, providerNamespaceSeparator) {
|
||||
|
|
|
@ -71,7 +71,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
|||
break
|
||||
}
|
||||
|
||||
serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
|
||||
serviceKey := fmt.Sprintf("%s-%s-%s", serviceName, service.Name, &service.Port)
|
||||
conf.Services[serviceKey] = balancerServerTCP
|
||||
|
||||
srv := dynamic.TCPWRRService{Name: serviceKey}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
|
@ -3738,7 +3739,7 @@ func TestGetServicePort(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
svc *corev1.Service
|
||||
port int32
|
||||
port intstr.IntOrString
|
||||
expected *corev1.ServicePort
|
||||
expectError bool
|
||||
}{
|
||||
|
@ -3757,7 +3758,7 @@ func TestGetServicePort(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
port: 80,
|
||||
port: intstr.FromInt(80),
|
||||
expected: &corev1.ServicePort{
|
||||
Port: 80,
|
||||
},
|
||||
|
@ -3785,12 +3786,57 @@ func TestGetServicePort(t *testing.T) {
|
|||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
desc: "Matching named port",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
port: intstr.FromString("http"),
|
||||
expected: &corev1.ServicePort{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Matching named port (with external name)",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeExternalName,
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
port: intstr.FromString("http"),
|
||||
expected: &corev1.ServicePort{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Mismatching, only port(Ingress) defined",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{},
|
||||
},
|
||||
port: 80,
|
||||
port: intstr.FromInt(80),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
desc: "Mismatching, only named port(Ingress) defined",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{},
|
||||
},
|
||||
port: intstr.FromString("http"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
|
@ -3800,11 +3846,21 @@ func TestGetServicePort(t *testing.T) {
|
|||
Type: corev1.ServiceTypeExternalName,
|
||||
},
|
||||
},
|
||||
port: 80,
|
||||
port: intstr.FromInt(80),
|
||||
expected: &corev1.ServicePort{
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Mismatching, only named port(Ingress) defined with external name",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeExternalName,
|
||||
},
|
||||
},
|
||||
port: intstr.FromString("http"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
desc: "Mismatching, only Service port defined",
|
||||
svc: &corev1.Service{
|
||||
|
@ -3843,7 +3899,22 @@ func TestGetServicePort(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
port: 443,
|
||||
port: intstr.FromInt(443),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
desc: "Two different named ports defined",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "foo",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
port: intstr.FromString("bar"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
|
@ -3858,11 +3929,27 @@ func TestGetServicePort(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
port: 443,
|
||||
port: intstr.FromInt(443),
|
||||
expected: &corev1.ServicePort{
|
||||
Port: 443,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Two different named ports defined (with external name)",
|
||||
svc: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeExternalName,
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "foo",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
port: intstr.FromString("bar"),
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client Client) *dynamic.UDPConfiguration {
|
||||
|
@ -52,7 +53,7 @@ func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client
|
|||
break
|
||||
}
|
||||
|
||||
serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
|
||||
serviceKey := fmt.Sprintf("%s-%s-%s", serviceName, service.Name, &service.Port)
|
||||
conf.Services[serviceKey] = balancerServerUDP
|
||||
|
||||
srv := dynamic.UDPWRRService{Name: serviceKey}
|
||||
|
@ -114,7 +115,7 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([
|
|||
var portSpec *corev1.ServicePort
|
||||
for _, p := range service.Spec.Ports {
|
||||
p := p
|
||||
if svc.Port == p.Port {
|
||||
if (svc.Port.Type == intstr.Int && svc.Port.IntVal == p.Port) || (svc.Port.Type == intstr.String && svc.Port.StrVal == p.Name) {
|
||||
portSpec = &p
|
||||
break
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// IngressRouteSpec is a specification for a IngressRouteSpec resource.
|
||||
|
@ -67,7 +68,7 @@ type LoadBalancerSpec struct {
|
|||
|
||||
// Port and all the fields below are related to a servers load-balancer,
|
||||
// and therefore should only be specified when Name references a Kubernetes Service.
|
||||
Port int32 `json:"port"`
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
Scheme string `json:"scheme,omitempty"`
|
||||
Strategy string `json:"strategy,omitempty"`
|
||||
PassHostHeader *bool `json:"passHostHeader,omitempty"`
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// IngressRouteTCPSpec is a specification for a IngressRouteTCPSpec resource.
|
||||
|
@ -56,7 +57,7 @@ type TLSStoreTCPRef struct {
|
|||
type ServiceTCP struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Port int32 `json:"port"`
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
Weight *int `json:"weight,omitempty"`
|
||||
TerminationDelay *int `json:"terminationDelay,omitempty"`
|
||||
ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"`
|
||||
|
|
|
@ -2,6 +2,7 @@ package v1alpha1
|
|||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// IngressRouteUDPSpec is a specification for a IngressRouteUDPSpec resource.
|
||||
|
@ -23,10 +24,10 @@ type TLSOptionUDPRef struct {
|
|||
|
||||
// ServiceUDP defines an upstream to proxy traffic.
|
||||
type ServiceUDP struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Port int32 `json:"port"`
|
||||
Weight *int `json:"weight,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
Weight *int `json:"weight,omitempty"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
|
|
|
@ -507,6 +507,7 @@ func (in *LoadBalancerSpec) DeepCopyInto(out *LoadBalancerSpec) {
|
|||
*out = new(dynamic.Sticky)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.Port = in.Port
|
||||
if in.PassHostHeader != nil {
|
||||
in, out := &in.PassHostHeader, &out.PassHostHeader
|
||||
*out = new(bool)
|
||||
|
@ -1001,6 +1002,7 @@ func (in *ServiceSpec) DeepCopy() *ServiceSpec {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) {
|
||||
*out = *in
|
||||
out.Port = in.Port
|
||||
if in.Weight != nil {
|
||||
in, out := &in.Weight, &out.Weight
|
||||
*out = new(int)
|
||||
|
@ -1032,6 +1034,7 @@ func (in *ServiceTCP) DeepCopy() *ServiceTCP {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ServiceUDP) DeepCopyInto(out *ServiceUDP) {
|
||||
*out = *in
|
||||
out.Port = in.Port
|
||||
if in.Weight != nil {
|
||||
in, out := &in.Weight, &out.Weight
|
||||
*out = new(int)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue