blob: 376a39075bfe7f4c3f890df7e77f253a927c0852 [file] [log] [blame]
// 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 v2
import (
"strings"
"github.com/pkg/errors"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/apache/apisix-ingress-controller/api/adc"
)
// ApisixRouteSpec is the spec definition for ApisixRoute.
// It defines routing rules for both HTTP and stream traffic.
type ApisixRouteSpec struct {
// IngressClassName is the name of the IngressClass this route belongs to.
// It allows multiple controllers to watch and reconcile different routes.
IngressClassName string `json:"ingressClassName,omitempty" yaml:"ingressClassName,omitempty"`
// HTTP defines a list of HTTP route rules.
// Each rule specifies conditions to match HTTP requests and how to forward them.
//
// +listType=map
// +listMapKey=name
HTTP []ApisixRouteHTTP `json:"http,omitempty" yaml:"http,omitempty"`
// Stream defines a list of stream route rules.
// Each rule specifies conditions to match TCP/UDP traffic and how to forward them.
//
// +listType=map
// +listMapKey=name
Stream []ApisixRouteStream `json:"stream,omitempty" yaml:"stream,omitempty"`
}
// ApisixRouteStatus defines the observed state of ApisixRoute.
type ApisixRouteStatus = ApisixStatus
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=ar
// +kubebuilder:printcolumn:name="Hosts",type="string",JSONPath=".spec.http[].match.hosts",description="HTTP Hosts",priority=0
// +kubebuilder:printcolumn:name="URIs",type="string",JSONPath=".spec.http[].match.paths",description="HTTP Paths",priority=0
// +kubebuilder:printcolumn:name="Target Service (HTTP)",type="string",JSONPath=".spec.http[].backends[].serviceName",description="Backend Service for HTTP",priority=1
// +kubebuilder:printcolumn:name="Ingress Port (TCP)",type="integer",JSONPath=".spec.tcp[].match.ingressPort",description="TCP Ingress Port",priority=1
// +kubebuilder:printcolumn:name="Target Service (TCP)",type="string",JSONPath=".spec.tcp[].match.backend.serviceName",description="Backend Service for TCP",priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Creation time",priority=0
// ApisixRoute is defines configuration for HTTP and stream routes.
type ApisixRoute struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// ApisixRouteSpec defines HTTP and stream route configuration.
Spec ApisixRouteSpec `json:"spec,omitempty"`
Status ApisixRouteStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ApisixRouteList contains a list of ApisixRoute.
type ApisixRouteList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ApisixRoute `json:"items"`
}
// ApisixRouteHTTP represents a single HTTP route configuration.
type ApisixRouteHTTP struct {
// Name is the unique rule name and cannot be empty.
Name string `json:"name" yaml:"name"`
// Priority defines the route priority when multiple routes share the same URI path.
// Higher values mean higher priority in route matching.
Priority int `json:"priority,omitempty" yaml:"priority,omitempty"`
// Timeout specifies upstream timeout settings.
Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"`
// Match defines the HTTP request matching criteria.
Match ApisixRouteHTTPMatch `json:"match,omitempty" yaml:"match,omitempty"`
// Backends lists potential backend services to proxy requests to.
// If more than one backend is specified, the `traffic-split` plugin is used
// to distribute traffic according to backend weights.
Backends []ApisixRouteHTTPBackend `json:"backends,omitempty" yaml:"backends,omitempty"`
// Upstreams references ApisixUpstream CRDs.
Upstreams []ApisixRouteUpstreamReference `json:"upstreams,omitempty" yaml:"upstreams,omitempty"`
// Websocket enables or disables websocket support for this route.
// +kubebuilder:validation:Optional
Websocket bool `json:"websocket" yaml:"websocket"`
// PluginConfigName specifies the name of the plugin config to apply.
PluginConfigName string `json:"plugin_config_name,omitempty" yaml:"plugin_config_name,omitempty"`
// PluginConfigNamespace specifies the namespace of the plugin config.
// Defaults to the namespace of the ApisixRoute if not set.
PluginConfigNamespace string `json:"plugin_config_namespace,omitempty" yaml:"plugin_config_namespace,omitempty"`
// Plugins lists additional plugins applied to this route.
Plugins []ApisixRoutePlugin `json:"plugins,omitempty" yaml:"plugins,omitempty"`
// Authentication holds authentication-related configuration for this route.
Authentication ApisixRouteAuthentication `json:"authentication,omitempty" yaml:"authentication,omitempty"`
}
// ApisixRouteStream defines the configuration for a Layer 4 (TCP/UDP) route. Currently not supported.
type ApisixRouteStream struct {
// Name is a unique identifier for the route. This field must not be empty.
Name string `json:"name" yaml:"name"`
// Protocol specifies the L4 protocol to match. Can be `TCP` or `UDP`.
//
// +kubebuilder:validation:Enum=TCP;UDP
Protocol string `json:"protocol" yaml:"protocol"`
// Match defines the criteria used to match incoming TCP or UDP connections.
Match ApisixRouteStreamMatch `json:"match" yaml:"match"`
// Backend specifies the destination service to which traffic should be forwarded.
Backend ApisixRouteStreamBackend `json:"backend" yaml:"backend"`
// Plugins defines a list of plugins to apply to this route.
Plugins []ApisixRoutePlugin `json:"plugins,omitempty" yaml:"plugins,omitempty"`
}
// UpstreamTimeout defines timeout settings for connecting, sending, and reading from the upstream.
type UpstreamTimeout struct {
// Connect timeout for establishing a connection to the upstream.
Connect metav1.Duration `json:"connect,omitempty" yaml:"connect,omitempty"`
// Send timeout for sending data to the upstream.
Send metav1.Duration `json:"send,omitempty" yaml:"send,omitempty"`
// Read timeout for reading data from the upstream.
Read metav1.Duration `json:"read,omitempty" yaml:"read,omitempty"`
}
// ApisixRouteHTTPMatch defines the conditions used to match incoming HTTP requests.
type ApisixRouteHTTPMatch struct {
// Paths is a list of URI path patterns to match.
// At least one path must be specified.
// Supports exact matches and prefix matches.
// For prefix matches, append `*` to the path, such as `/foo*`.
Paths []string `json:"paths" yaml:"paths"`
// Methods specifies the HTTP methods to match.
Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"`
// Hosts specifies Host header values to match.
// Supports exact and wildcard domains.
// Only one level of wildcard is allowed (e.g., `*.example.com` is valid,
// but `*.*.example.com` is not).
Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
// RemoteAddrs is a list of source IP addresses or CIDR ranges to match.
// Supports both IPv4 and IPv6 formats.
RemoteAddrs []string `json:"remoteAddrs,omitempty" yaml:"remoteAddrs,omitempty"`
// NginxVars defines match conditions based on Nginx variables.
NginxVars ApisixRouteHTTPMatchExprs `json:"exprs,omitempty" yaml:"exprs,omitempty"`
// FilterFunc is a user-defined function for advanced request filtering.
// The function can use Nginx variables through the `vars` parameter.
FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"`
}
// ApisixRoutePlugin represents an APISIX plugin.
type ApisixRoutePlugin struct {
// The plugin name.
Name string `json:"name" yaml:"name"`
// Whether this plugin is in use, default is true.
// +kubebuilder:default=true
Enable bool `json:"enable" yaml:"enable"`
// Plugin configuration.
// +kubebuilder:validation:Optional
Config apiextensionsv1.JSON `json:"config" yaml:"config"`
// Plugin configuration secretRef.
// +kubebuilder:validation:Optional
SecretRef string `json:"secretRef" yaml:"secretRef"`
}
// ApisixRouteHTTPBackend represents an HTTP backend (Kubernetes Service).
type ApisixRouteHTTPBackend struct {
// ServiceName is the name of the Kubernetes Service.
// Cross-namespace references are not supported—ensure the ApisixRoute
// and the Service are in the same namespace.
ServiceName string `json:"serviceName" yaml:"serviceName"`
// ServicePort is the port of the Kubernetes Service.
// This can be either the port name or port number.
ServicePort intstr.IntOrString `json:"servicePort" yaml:"servicePort"`
// ResolveGranularity determines how the backend service is resolved.
// Valid values are `endpoints` and `service`. When set to `endpoints`,
// individual pod IPs will be used; otherwise, the Service's ClusterIP or ExternalIP is used.
// The default is `endpoints`.
ResolveGranularity string `json:"resolveGranularity,omitempty" yaml:"resolveGranularity,omitempty"`
// Weight specifies the relative traffic weight for this backend.
// +kubebuilder:validation:Optional
Weight *int `json:"weight" yaml:"weight"`
// Subset specifies a named subset of the target Service.
// The subset must be pre-defined in the corresponding ApisixUpstream resource.
Subset string `json:"subset,omitempty" yaml:"subset,omitempty"`
}
// ApisixRouteUpstreamReference references an ApisixUpstream CRD to be used as a backend.
// It can be used in traffic-splitting scenarios or to select a specific upstream configuration.
type ApisixRouteUpstreamReference struct {
// Name is the name of the ApisixUpstream resource.
Name string `json:"name,omitempty" yaml:"name"`
// Weight is the weight assigned to this upstream.
// +kubebuilder:validation:Optional
Weight *int `json:"weight,omitempty" yaml:"weight"`
}
// ApisixRouteAuthentication represents authentication-related configuration in ApisixRoute.
type ApisixRouteAuthentication struct {
// Enable toggles authentication on or off.
Enable bool `json:"enable" yaml:"enable"`
// Type specifies the authentication type.
Type string `json:"type" yaml:"type"`
// KeyAuth defines configuration for key authentication.
KeyAuth ApisixRouteAuthenticationKeyAuth `json:"keyAuth,omitempty" yaml:"keyAuth,omitempty"`
// JwtAuth defines configuration for JWT authentication.
JwtAuth ApisixRouteAuthenticationJwtAuth `json:"jwtAuth,omitempty" yaml:"jwtAuth,omitempty"`
// LDAPAuth defines configuration for LDAP authentication.
LDAPAuth ApisixRouteAuthenticationLDAPAuth `json:"ldapAuth,omitempty" yaml:"ldapAuth,omitempty"`
}
// ApisixRouteStreamMatch represents the matching conditions for a stream route.
type ApisixRouteStreamMatch struct {
// IngressPort is the port on which the APISIX Ingress proxy server listens.
// This must be a statically configured port, as APISIX does not support dynamic port binding.
//
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=65535
IngressPort int32 `json:"ingressPort" yaml:"ingressPort"`
// Host is the destination host address used to match the incoming TCP/UDP traffic.
Host string `json:"host,omitempty" yaml:"host,omitempty"`
}
// ApisixRouteStreamBackend represents the backend service for a TCP or UDP stream route.
type ApisixRouteStreamBackend struct {
// ServiceName is the name of the Kubernetes Service.
// Cross-namespace references are not supported—ensure the ApisixRoute
// and the Service are in the same namespace.
ServiceName string `json:"serviceName" yaml:"serviceName"`
// ServicePort is the port of the Kubernetes Service.
// This can be either the port name or port number.
ServicePort intstr.IntOrString `json:"servicePort" yaml:"servicePort"`
// ResolveGranularity determines how the backend service is resolved.
// Valid values are `endpoint` and `service`. When set to `endpoint`,
// individual pod IPs will be used; otherwise, the Service's ClusterIP or ExternalIP is used.
// The default is `endpoint`.
//
// +kubebuilder:default=endpoint
// +kubebuilder:validation:Enum=endpoint;service
ResolveGranularity string `json:"resolveGranularity,omitempty" yaml:"resolveGranularity,omitempty"`
// Subset specifies a named subset of the target Service.
// The subset must be pre-defined in the corresponding ApisixUpstream resource.
Subset string `json:"subset,omitempty" yaml:"subset,omitempty"`
}
// ApisixRouteHTTPMatchExpr represents a binary expression used to match requests based on Nginx variables.
type ApisixRouteHTTPMatchExpr struct {
// Subject defines the left-hand side of the expression.
// It can be any [APISIX variable](https://apisix.apache.org/docs/apisix/apisix-variable) or string literal.
Subject ApisixRouteHTTPMatchExprSubject `json:"subject" yaml:"subject"`
// Op specifies the operator used in the expression.
// Can be `Equal`, `NotEqual`, `GreaterThan`, `GreaterThanEqual`, `LessThan`, `LessThanEqual`, `RegexMatch`,
// `RegexNotMatch`, `RegexMatchCaseInsensitive`, `RegexNotMatchCaseInsensitive`, `In`, or `NotIn`.
Op string `json:"op" yaml:"op"`
// Set provides a list of acceptable values for the expression.
// This should be used when Op is `In` or `NotIn`.
// +kubebuilder:validation:Optional
Set []string `json:"set" yaml:"set"`
// Value defines a single value to compare against the subject.
// This should be used when Op is not `In` or `NotIn`.
// Set and Value are mutually exclusive—only one should be set at a time.
// +kubebuilder:validation:Optional
Value *string `json:"value" yaml:"value"`
}
type ApisixRouteHTTPMatchExprs []ApisixRouteHTTPMatchExpr
func (exprs ApisixRouteHTTPMatchExprs) ToVars() (result adc.Vars, err error) {
for _, expr := range exprs {
if expr.Subject.Name == "" && expr.Subject.Scope != ScopePath {
return result, errors.New("empty subject.name")
}
// process key
var (
subj string
this adc.StringOrSlice
)
switch expr.Subject.Scope {
case ScopeQuery:
subj = "arg_" + expr.Subject.Name
case ScopeHeader:
subj = "http_" + strings.ReplaceAll(strings.ToLower(expr.Subject.Name), "-", "_")
case ScopeCookie:
subj = "cookie_" + expr.Subject.Name
case ScopePath:
subj = "uri"
case ScopeVariable:
subj = expr.Subject.Name
default:
return result, errors.New("invalid http match expr: subject.scope should be one of [query, header, cookie, path, variable]")
}
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: subj})
// process operator
var (
op string
)
switch expr.Op {
case OpEqual:
op = "=="
case OpGreaterThan:
op = ">"
case OpGreaterThanEqual:
op = ">="
case OpIn:
op = "in"
case OpLessThan:
op = "<"
case OpLessThanEqual:
op = "<="
case OpNotEqual:
op = "~="
case OpNotIn:
op = "in"
case OpRegexMatch:
op = "~~"
case OpRegexMatchCaseInsensitive:
op = "~*"
case OpRegexNotMatch:
op = "~~"
case OpRegexNotMatchCaseInsensitive:
op = "~*"
default:
return result, errors.New("unknown operator")
}
if expr.Op == OpNotIn || expr.Op == OpRegexNotMatch || expr.Op == OpRegexNotMatchCaseInsensitive {
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: "!"})
}
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: op})
// process value
switch expr.Op {
case OpIn, OpNotIn:
if expr.Set == nil {
return result, errors.New("empty set value")
}
var value adc.StringOrSlice
for _, item := range expr.Set {
value.SliceVal = append(value.SliceVal, adc.StringOrSlice{StrVal: item})
}
this.SliceVal = append(this.SliceVal, value)
default:
if expr.Value == nil {
return result, errors.New("empty value")
}
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: *expr.Value})
}
// append to result
result = append(result, this.SliceVal)
}
return result, nil
}
// ApisixRoutePluginConfig is the configuration for
// any plugins.
type ApisixRoutePluginConfig map[string]apiextensionsv1.JSON
// ApisixRouteAuthenticationKeyAuth defines key authentication configuration in ApisixRouteAuthentication.
type ApisixRouteAuthenticationKeyAuth struct {
// Header specifies the HTTP header name to look for the key authentication token.
Header string `json:"header,omitempty" yaml:"header,omitempty"`
}
// ApisixRouteAuthenticationJwtAuth defines JWT authentication configuration in ApisixRouteAuthentication.
type ApisixRouteAuthenticationJwtAuth struct {
// Header specifies the HTTP header name to look for the JWT token.
Header string `json:"header,omitempty" yaml:"header,omitempty"`
// Query specifies the URL query parameter name to look for the JWT token.
Query string `json:"query,omitempty" yaml:"query,omitempty"`
// Cookie specifies the cookie name to look for the JWT token.
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty"`
}
// ApisixRouteAuthenticationLDAPAuth defines LDAP authentication configuration in ApisixRouteAuthentication.
type ApisixRouteAuthenticationLDAPAuth struct {
// BaseDN is the base distinguished name (DN) for LDAP searches.
BaseDN string `json:"base_dn,omitempty" yaml:"base_dn,omitempty"`
// LDAPURI is the URI of the LDAP server.
LDAPURI string `json:"ldap_uri,omitempty" yaml:"ldap_uri,omitempty"`
// UseTLS indicates whether to use TLS for the LDAP connection.
UseTLS bool `json:"use_tls,omitempty" yaml:"use_tls,omitempty"`
// UID is the user identifier attribute in LDAP.
UID string `json:"uid,omitempty" yaml:"uid,omitempty"`
}
// ApisixRouteHTTPMatchExprSubject describes the subject of a route matching expression.
type ApisixRouteHTTPMatchExprSubject struct {
// Scope specifies the subject scope and can be `Header`, `Query`, or `Path`.
// When Scope is `Path`, Name will be ignored.
Scope string `json:"scope" yaml:"scope"`
// Name is the name of the header or query parameter.
Name string `json:"name" yaml:"name"`
}
func init() {
SchemeBuilder.Register(&ApisixRoute{}, &ApisixRouteList{})
}