pkg/providers/gateway/translation/gateway.go (161 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 translation
import (
"errors"
"go.uber.org/zap"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
"github.com/apache/apisix-ingress-controller/pkg/log"
"github.com/apache/apisix-ingress-controller/pkg/providers/gateway/types"
)
func (t *translator) TranslateGatewayV1beta1(gateway *gatewayv1beta1.Gateway) (map[string]*types.ListenerConf, error) {
listeners := make(map[string]*types.ListenerConf)
for i, listener := range gateway.Spec.Listeners {
allowedKinds, err := getAllowedKinds(listener)
if err != nil {
return nil, err
}
if len(allowedKinds) == 0 {
log.Warnw("listener allowed kinds is empty",
zap.String("gateway", gateway.Name),
zap.String("namespace", gateway.Namespace),
zap.Int("listener_index", i),
)
continue
}
err = validateListenerConfigurations(gateway, i, allowedKinds, listener)
if err != nil {
// TODO: Update CRD status
log.Warnw("invalid listener conf",
zap.Error(err),
zap.String("gateway", gateway.Name),
zap.String("namespace", gateway.Namespace),
zap.Int("listener_index", i),
)
continue
}
conf := &types.ListenerConf{
Namespace: gateway.Namespace,
Name: gateway.Name,
SectionName: string(listener.Name),
Protocol: listener.Protocol,
Port: listener.Port,
Hostname: listener.Hostname,
RouteNamespace: nil,
AllowedKinds: allowedKinds,
}
if listener.AllowedRoutes.Namespaces != nil {
conf.RouteNamespace = listener.AllowedRoutes.Namespaces
} else {
// This is restricted to the namespace of this Gateway by default.
sameNamespace := gatewayv1beta1.NamespacesFromSame
conf.RouteNamespace = &gatewayv1beta1.RouteNamespaces{
From: &sameNamespace,
}
}
listeners[conf.SectionName] = conf
}
return listeners, nil
}
func validateListenerConfigurations(gateway *gatewayv1beta1.Gateway, idx int, allowedKinds []gatewayv1beta1.RouteGroupKind,
listener gatewayv1beta1.Listener) error {
// Check protocols and allowedKinds
protocol := listener.Protocol
if protocol == gatewayv1beta1.HTTPProtocolType || protocol == gatewayv1beta1.TCPProtocolType || protocol == gatewayv1beta1.UDPProtocolType {
// Non-TLS
if listener.TLS != nil {
return errors.New("non-empty TLS conf for protocol " + string(protocol))
}
if protocol == gatewayv1beta1.HTTPProtocolType {
if len(allowedKinds) != 1 || allowedKinds[0].Kind != types.KindHTTPRoute {
return errors.New("HTTP protocol must allow route type HTTPRoute")
}
} else if protocol == gatewayv1beta1.TCPProtocolType {
if len(allowedKinds) != 1 || allowedKinds[0].Kind != types.KindTCPRoute {
return errors.New("TCP protocol must allow route type TCPRoute")
}
} else if protocol == gatewayv1beta1.UDPProtocolType {
if len(allowedKinds) != 1 || allowedKinds[0].Kind != types.KindUDPRoute {
return errors.New("UDP protocol must allow route type UDPRoute")
}
}
} else if protocol == gatewayv1beta1.HTTPSProtocolType || protocol == gatewayv1beta1.TLSProtocolType {
// TLS
if listener.TLS == nil {
return errors.New("empty TLS conf for protocol " + string(protocol))
}
if *listener.TLS.Mode == gatewayv1beta1.TLSModeTerminate {
if len(listener.TLS.CertificateRefs) == 0 {
return errors.New("TLS mode Terminate requires CertificateRefs")
}
if len(listener.TLS.CertificateRefs) > 1 {
log.Warnw("only the first CertificateRefs take effect",
zap.String("gateway", gateway.Name),
zap.String("namespace", gateway.Namespace),
zap.Int("listener_index", idx),
)
}
} else {
if len(listener.TLS.CertificateRefs) != 0 {
log.Warnw("no CertificateRefs will take effect in non-terminate TLS mode",
zap.String("gateway", gateway.Name),
zap.String("namespace", gateway.Namespace),
zap.Int("listener_index", idx),
)
}
}
if protocol == gatewayv1beta1.HTTPSProtocolType {
if *listener.TLS.Mode != gatewayv1beta1.TLSModeTerminate {
return errors.New("TLS mode for HTTPS protocol must be Terminate")
}
if len(allowedKinds) != 1 || allowedKinds[0].Kind != types.KindHTTPRoute {
return errors.New("HTTP protocol must allow route type HTTPRoute")
}
} else if protocol == gatewayv1beta1.TLSProtocolType {
for _, kind := range allowedKinds {
if kind.Kind != types.KindTLSRoute && kind.Kind != types.KindTCPRoute {
return errors.New("TLS protocol only support route type TLSRoute and TCPRoute")
}
}
}
}
return nil
}
func getAllowedKinds(listener gatewayv1beta1.Listener) ([]gatewayv1beta1.RouteGroupKind, error) {
var expectedKinds []gatewayv1beta1.RouteGroupKind
group := gatewayv1beta1.Group(gatewayv1beta1.GroupName)
var kind gatewayv1beta1.Kind
switch listener.Protocol {
case gatewayv1beta1.HTTPProtocolType, gatewayv1beta1.HTTPSProtocolType:
kind = types.KindHTTPRoute
case gatewayv1beta1.TLSProtocolType:
kind = types.KindTLSRoute
case gatewayv1beta1.TCPProtocolType:
kind = types.KindTCPRoute
case gatewayv1beta1.UDPProtocolType:
kind = types.KindUDPRoute
default:
// TODO: If an implementation does not support or recognize this resource type,
// it MUST set the “ResolvedRefs” condition to False for this Listener with
// the “InvalidRouteKinds” reason.
return nil, errors.New("unknown protocol " + string(listener.Protocol))
}
expectedKinds = []gatewayv1beta1.RouteGroupKind{
{
Group: &group,
Kind: kind,
},
}
if listener.AllowedRoutes == nil || len(listener.AllowedRoutes.Kinds) == 0 {
return expectedKinds, nil
}
uniqueAllowedKinds := make(map[gatewayv1beta1.Kind]struct{})
var allowedKinds []gatewayv1beta1.RouteGroupKind
for _, kind := range listener.AllowedRoutes.Kinds {
expected := false
for _, expectedKind := range expectedKinds {
if kind.Kind == expectedKind.Kind &&
kind.Group != nil && *kind.Group == *expectedKind.Group {
expected = true
break
}
}
if expected {
if _, ok := uniqueAllowedKinds[kind.Kind]; !ok {
uniqueAllowedKinds[kind.Kind] = struct{}{}
allowedKinds = append(allowedKinds, kind)
}
}
}
return allowedKinds, nil
}