blob: b18e6fdbe04924c118ef0edb76855ae3aaa33072 [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 adc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/incubator4/go-resty-expr/expr"
"k8s.io/apimachinery/pkg/util/intstr"
)
const (
TypeRoute = "route"
TypeService = "service"
TypeConsumer = "consumer"
TypeSSL = "ssl"
TypeGlobalRule = "global_rule"
TypePluginMetadata = "plugin_metadata"
)
type Object interface {
GetLabels() map[string]string
}
// +k8s:deepcopy-gen=true
type Metadata struct {
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Desc string `json:"description,omitempty" yaml:"description,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
func (m *Metadata) GetLabels() map[string]string { return m.Labels }
type Resources struct {
ConsumerGroups []*ConsumerGroup `json:"consumer_groups,omitempty" yaml:"consumer_groups,omitempty"`
Consumers []*Consumer `json:"consumers,omitempty" yaml:"consumers,omitempty"`
GlobalRules GlobalRule `json:"global_rules,omitempty" yaml:"global_rules,omitempty"`
PluginMetadata PluginMetadata `json:"plugin_metadata,omitempty" yaml:"plugin_metadata,omitempty"`
Services []*Service `json:"services,omitempty" yaml:"services,omitempty"`
SSLs []*SSL `json:"ssls,omitempty" yaml:"ssls,omitempty"`
}
type GlobalRule Plugins
func (g *GlobalRule) DeepCopy() GlobalRule {
original := Plugins(*g)
copied := original.DeepCopy()
return GlobalRule(copied)
}
// +k8s:deepcopy-gen=true
type GlobalRuleItem struct {
Metadata `json:",inline" yaml:",inline"`
Plugins Plugins `json:"plugins" yaml:"plugins"`
}
type PluginMetadata Plugins
func (p *PluginMetadata) DeepCopy() PluginMetadata {
original := Plugins(*p)
copied := original.DeepCopy()
return PluginMetadata(copied)
}
// +k8s:deepcopy-gen=true
type ConsumerGroup struct {
Metadata `json:",inline" yaml:",inline"`
Consumers []Consumer `json:"consumers,omitempty" yaml:"consumers,omitempty"`
Name string `json:"name" yaml:"name"`
Plugins Plugins `json:"plugins" yaml:"plugins"`
}
// +k8s:deepcopy-gen=true
type Consumer struct {
Metadata `json:",inline" yaml:",inline"`
Credentials []Credential `json:"credentials,omitempty" yaml:"credentials,omitempty"`
Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"`
Username string `json:"username" yaml:"username"`
}
// +k8s:deepcopy-gen=true
type Credential struct {
Metadata `json:",inline" yaml:",inline"`
Config Plugins `json:"config" yaml:"config"`
Type string `json:"type" yaml:"type"`
}
// +k8s:deepcopy-gen=true
type Service struct {
Metadata `json:",inline" yaml:",inline"`
Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
PathPrefix string `json:"path_prefix,omitempty" yaml:"path_prefix,omitempty"`
Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"`
Routes []*Route `json:"routes,omitempty" yaml:"routes,omitempty"`
StreamRoutes []*StreamRoute `json:"stream_routes,omitempty" yaml:"stream_routes,omitempty"`
StripPathPrefix *bool `json:"strip_path_prefix,omitempty" yaml:"strip_path_prefix,omitempty"`
Upstream *Upstream `json:"upstream,omitempty" yaml:"upstream,omitempty"`
Upstreams []*Upstream `json:"upstreams,omitempty" yaml:"upstreams,omitempty"`
}
// +k8s:deepcopy-gen=true
type Route struct {
Metadata `json:",inline" yaml:",inline"`
EnableWebsocket *bool `json:"enable_websocket,omitempty" yaml:"enable_websocket,omitempty"`
FilterFunc string `json:"filter_func,omitempty" yaml:"filter_func,omitempty"`
Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"`
Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"`
Priority *int64 `json:"priority,omitempty" yaml:"priority,omitempty"`
RemoteAddrs []string `json:"remote_addrs,omitempty" yaml:"remote_addrs,omitempty"`
Timeout *Timeout `json:"timeout,omitempty" yaml:"timeout,omitempty"`
Uris []string `json:"uris" yaml:"uris"`
Vars Vars `json:"vars,omitempty" yaml:"vars,omitempty"`
}
type Timeout struct {
Connect int `json:"connect"`
Read int `json:"read"`
Send int `json:"send"`
}
// +k8s:deepcopy-gen=true
type StreamRoute struct {
Metadata `json:",inline" yaml:",inline"`
Plugins Plugins `json:"plugins,omitempty"`
RemoteAddr string `json:"remote_addr,omitempty"`
ServerAddr string `json:"server_addr,omitempty"`
ServerPort int32 `json:"server_port,omitempty"`
SNI string `json:"sni,omitempty"`
}
// +k8s:deepcopy-gen=true
type Upstream struct {
Metadata `json:",inline" yaml:",inline"`
HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Nodes UpstreamNodes `json:"nodes" yaml:"nodes"`
PassHost string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"`
Retries *int64 `json:"retries,omitempty" yaml:"retries,omitempty"`
RetryTimeout *float64 `json:"retry_timeout,omitempty" yaml:"retry_timeout,omitempty"`
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
ServiceName string `json:"service_name,omitempty" yaml:"service_name,omitempty"`
Timeout *Timeout `json:"timeout,omitempty" yaml:"timeout,omitempty"`
Type UpstreamType `json:"type,omitempty" yaml:"type,omitempty"`
UpstreamHost string `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"`
Checks *UpstreamHealthCheck `json:"checks,omitempty" yaml:"checks,omitempty"`
TLS *ClientTLS `json:"tls,omitempty" yaml:"tls,omitempty"`
// for Service Discovery
DiscoveryType string `json:"discovery_type,omitempty" yaml:"discovery_type,omitempty"`
DiscoveryArgs map[string]string `json:"discovery_args,omitempty" yaml:"discovery_args,omitempty"`
}
// UpstreamHealthCheck defines the active and/or passive health check for an Upstream,
// with the upstream health check feature, pods can be kicked out or joined in quickly,
// if the feedback of Kubernetes liveness/readiness probe is long.
// +k8s:deepcopy-gen=true
type UpstreamHealthCheck struct {
Active *UpstreamActiveHealthCheck `json:"active" yaml:"active"`
Passive *UpstreamPassiveHealthCheck `json:"passive,omitempty" yaml:"passive,omitempty"`
}
// ClientTLS is tls cert and key use in mTLS
// +k8s:deepcopy-gen=true
type ClientTLS struct {
Cert string `json:"client_cert,omitempty" yaml:"client_cert,omitempty"`
Key string `json:"client_key,omitempty" yaml:"client_key,omitempty"`
}
// UpstreamActiveHealthCheck defines the active upstream health check configuration.
// +k8s:deepcopy-gen=true
type UpstreamActiveHealthCheck struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"`
Concurrency int `json:"concurrency,omitempty" yaml:"concurrency,omitempty"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
Port int32 `json:"port,omitempty" yaml:"port,omitempty"`
HTTPPath string `json:"http_path,omitempty" yaml:"http_path,omitempty"`
HTTPSVerifyCert bool `json:"https_verify_cert,omitempty" yaml:"https_verify_cert,omitempty"`
HTTPRequestHeaders []string `json:"req_headers,omitempty" yaml:"req_headers,omitempty"`
Healthy UpstreamActiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"`
Unhealthy UpstreamActiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"`
}
// UpstreamPassiveHealthCheck defines the passive health check configuration for an upstream.
// Passive health checks rely on analyzing live traffic to determine the health status of upstream nodes.
// +k8s:deepcopy-gen=true
type UpstreamPassiveHealthCheck struct {
// Type is the passive health check type. For example: `http`.
Type string `json:"type,omitempty" yaml:"type,omitempty"`
// Healthy defines the conditions under which an upstream node is considered healthy.
Healthy UpstreamPassiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"`
// Unhealthy defines the conditions under which an upstream node is considered unhealthy.
Unhealthy UpstreamPassiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"`
}
// UpstreamActiveHealthCheckHealthy defines the conditions used to actively determine whether an upstream node is healthy.
// +k8s:deepcopy-gen=true
type UpstreamActiveHealthCheckHealthy struct {
UpstreamPassiveHealthCheckHealthy `json:",inline" yaml:",inline"`
// Interval defines the time interval for checking targets, in seconds.
Interval int `json:"interval,omitempty" yaml:"interval,omitempty"`
}
// UpstreamPassiveHealthCheckHealthy defines the conditions used to passively determine whether an upstream node is healthy.
// +k8s:deepcopy-gen=true
type UpstreamPassiveHealthCheckHealthy struct {
HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"`
Successes int `json:"successes,omitempty" yaml:"successes,omitempty"`
}
// UpstreamPassiveHealthCheckUnhealthy defines the conditions used to passively determine whether an upstream node is unhealthy.
// +k8s:deepcopy-gen=true
type UpstreamPassiveHealthCheckUnhealthy struct {
HTTPStatuses []int `json:"http_statuses,omitempty" yaml:"http_statuses,omitempty"`
HTTPFailures int `json:"http_failures,omitempty" yaml:"http_failures,omitempty"`
TCPFailures int `json:"tcp_failures,omitempty" yaml:"tcp_failures,omitempty"`
Timeouts int `json:"timeouts,omitempty" yaml:"timeouts,omitempty"`
}
// UpstreamActiveHealthCheckHealthy defines the conditions used to actively determine whether an upstream node is unhealthy.
// +k8s:deepcopy-gen=true
type UpstreamActiveHealthCheckUnhealthy struct {
UpstreamPassiveHealthCheckUnhealthy `json:",inline" yaml:",inline"`
// Interval defines the time interval for checking targets, in seconds.
Interval int `json:"interval,omitempty" yaml:"interval,omitempty"`
}
// TrafficSplitConfig is the config of traffic-split plugin.
// +k8s:deepcopy-gen=true
type TrafficSplitConfig struct {
Rules []TrafficSplitConfigRule `json:"rules"`
}
// TrafficSplitConfigRule is the rule config in traffic-split plugin config.
// +k8s:deepcopy-gen=true
type TrafficSplitConfigRule struct {
WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream `json:"weighted_upstreams"`
}
// TrafficSplitConfigRuleWeightedUpstream defines a weighted backend in a traffic split rule.
// This is used by the APISIX traffic-split plugin to distribute traffic
// across multiple upstreams based on weight.
// +k8s:deepcopy-gen=true
type TrafficSplitConfigRuleWeightedUpstream struct {
// UpstreamID is the identifier of a pre-defined upstream.
UpstreamID string `json:"upstream_id,omitempty"`
// Upstream specifies upstream configuration.
// If provided, it overrides UpstreamID.
Upstream *Upstream `json:"upstream,omitempty"`
// Weight defines the percentage of traffic routed to this upstream.
// The final routing decision is based on relative weights.
Weight int `json:"weight"`
}
// TLSClass defines the client TLS configuration for mutual TLS (mTLS) authentication.
// +k8s:deepcopy-gen=true
type TLSClass struct {
// ClientCERT is the PEM-encoded client certificate.
ClientCERT string `json:"client_cert,omitempty"`
// ClientCERTID is the reference ID to a stored client certificate.
ClientCERTID string `json:"client_cert_id,omitempty"`
// ClientKey is the PEM-encoded private key for the client certificate.
ClientKey string `json:"client_key,omitempty"`
// Verify indicates whether the server's certificate should be verified.
// If false, TLS verification is skipped.
Verify *bool `json:"verify,omitempty"`
}
// +k8s:deepcopy-gen=true
type SSL struct {
Metadata `json:",inline" yaml:",inline"`
Certificates []Certificate `json:"certificates" yaml:"certificates"`
Client *ClientClass `json:"client,omitempty" yaml:"client,omitempty"`
Snis []string `json:"snis" yaml:"snis"`
SSLProtocols []SSLProtocol `json:"ssl_protocols,omitempty" yaml:"ssl_protocols,omitempty"`
Type *SSLType `json:"type,omitempty" yaml:"type,omitempty"`
}
// +k8s:deepcopy-gen=true
type Certificate struct {
Certificate string `json:"certificate" yaml:"certificate"`
Key string `json:"key" yaml:"key"`
}
// +k8s:deepcopy-gen=true
type ClientClass struct {
CA string `json:"ca" yaml:"ca"`
Depth *int64 `json:"depth,omitempty" yaml:"depth,omitempty"`
SkipMtlsURIRegex []string `json:"skip_mtls_uri_regex,omitempty" yaml:"skip_mtls_uri_regex,omitempty"`
}
type Method string
const (
Connect Method = "CONNECT"
Delete Method = "DELETE"
Get Method = "GET"
Head Method = "HEAD"
Options Method = "OPTIONS"
Patch Method = "PATCH"
Post Method = "POST"
Purge Method = "PURGE"
Put Method = "PUT"
Trace Method = "TRACE"
)
type Scheme string
type UpstreamType string
const (
Chash UpstreamType = "chash"
Ewma UpstreamType = "ewma"
LeastConn UpstreamType = "least_conn"
Roundrobin UpstreamType = "roundrobin"
)
type SSLProtocol string
const (
TLSv11 SSLProtocol = "TLSv1.1"
TLSv12 SSLProtocol = "TLSv1.2"
TLSv13 SSLProtocol = "TLSv1.3"
)
type SSLType string
const (
Client SSLType = "client"
Server SSLType = "server"
)
type Plugins map[string]any
func (p *Plugins) DeepCopyInto(out *Plugins) {
b, _ := json.Marshal(&p)
_ = json.Unmarshal(b, out)
}
func (p Plugins) DeepCopy() Plugins {
if p == nil {
return nil
}
out := make(Plugins)
p.DeepCopyInto(&out)
return out
}
// UpstreamNode is the node in upstream
type UpstreamNode struct {
Host string `json:"host" yaml:"host"`
Port int `json:"port" yaml:"port"`
Weight int `json:"weight" yaml:"weight"`
Priority int `json:"priority,omitempty" yaml:"priority,omitempty"`
}
// UpstreamNodes is the upstream node list.
type UpstreamNodes []UpstreamNode
func mapKV2Node(key string, val float64) (*UpstreamNode, error) {
hp := strings.Split(key, ":")
host := hp[0]
// according to APISIX upstream nodes policy, port is required
port := "80"
if len(hp) > 2 {
return nil, errors.New("invalid upstream node")
} else if len(hp) == 2 {
port = hp[1]
}
portInt, err := strconv.Atoi(port)
if err != nil {
return nil, fmt.Errorf("parse port to int fail: %s", err.Error())
}
node := &UpstreamNode{
Host: host,
Port: portInt,
Weight: int(val),
}
return node, nil
}
// UnmarshalJSON implements json.Unmarshaler interface.
// lua-cjson doesn't distinguish empty array and table,
// and by default empty array will be encoded as '{}'.
// We have to maintain the compatibility.
func (n *UpstreamNodes) UnmarshalJSON(p []byte) error {
var data []UpstreamNode
if p[0] == '{' {
value := map[string]float64{}
if err := json.Unmarshal(p, &value); err != nil {
return err
}
for k, v := range value {
node, err := mapKV2Node(k, v)
if err != nil {
return err
}
data = append(data, *node)
}
*n = data
return nil
}
if err := json.Unmarshal(p, &data); err != nil {
return err
}
*n = data
return nil
}
func (n Upstream) MarshalJSON() ([]byte, error) {
type Alias Upstream
// APISIX does not allow discovery_type and nodes to exist at the same time.
// https://github.com/apache/apisix/blob/01b4b49eb2ba642b337f7a1fbe1894a77942910b/apisix/schema_def.lua#L501-L504
if n.DiscoveryType != "" {
aux := struct {
Alias
Nodes UpstreamNodes `json:"nodes,omitempty" yaml:"nodes,omitempty"`
}{
Alias: (Alias)(n),
}
aux.Nodes = nil
return json.Marshal(&aux)
}
// By default Go serializes a nil slice as JSON null.
// For APISIX compatibility, nil UpstreamNodes should be encoded as [] instead.
// https://github.com/apache/apisix/blob/77dacda31277a31d6014b4970e36bae2a5c30907/apisix/schema_def.lua#L295-L338
if n.Nodes == nil {
n.Nodes = UpstreamNodes{}
}
return json.Marshal((Alias)(n))
}
func ComposeSSLName(kind, namespace, name string) string {
p := make([]byte, 0, len(kind)+len(namespace)+len(name)+2)
buf := bytes.NewBuffer(p)
buf.WriteString(kind)
buf.WriteByte('_')
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
return buf.String()
}
// ComposeRouteName uses namespace, name and rule name to compose
// the route name.
func ComposeRouteName(namespace, name string, rule string) string {
// FIXME Use sync.Pool to reuse this buffer if the upstream
// name composing code path is hot.
p := make([]byte, 0, len(namespace)+len(name)+len(rule)+2)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
buf.WriteByte('_')
buf.WriteString(rule)
return buf.String()
}
// ComposeStreamRouteName uses namespace, name and rule name to compose
// the stream_route name.
func ComposeStreamRouteName(namespace, name string, rule string, typ string) string {
if typ == "" {
typ = "TCP"
}
// FIXME Use sync.Pool to reuse this buffer if the upstream
// name composing code path is hot.
p := make([]byte, 0, len(namespace)+len(name)+len(rule)+len(typ)+3)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
buf.WriteByte('_')
buf.WriteString(rule)
buf.WriteByte('_')
buf.WriteString(typ)
return buf.String()
}
func ComposeServiceNameWithRule(namespace, name string, rule string) string {
// FIXME Use sync.Pool to reuse this buffer if the upstream
// name composing code path is hot.
var p []byte
plen := len(namespace) + len(name) + 2
p = make([]byte, 0, plen)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
buf.WriteByte('_')
buf.WriteString(rule)
return buf.String()
}
func ComposeGRPCServiceNameWithRule(namespace, name string, rule string) string {
return ComposeServicesNameWithScheme(namespace, name, rule, "grpc")
}
func ComposeServicesNameWithScheme(namespace, name string, rule string, scheme string) string {
var p []byte
plen := len(namespace) + len(name) + len(rule) + len(scheme) + 3
p = make([]byte, 0, plen)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
buf.WriteByte('_')
buf.WriteString(rule)
buf.WriteByte('_')
buf.WriteString(scheme)
return buf.String()
}
func ComposeServiceNameWithStream(namespace, name string, rule, typ string) string {
return ComposeServicesNameWithScheme(namespace, name, rule, typ)
}
func ComposeConsumerName(namespace, name string) string {
// FIXME Use sync.Pool to reuse this buffer if the upstream
// name composing code path is hot.
p := make([]byte, 0, len(namespace)+len(name)+1)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
return buf.String()
}
// NewDefaultUpstream returns an empty Upstream with default values.
func NewDefaultService() *Service {
return &Service{
Metadata: Metadata{
Labels: map[string]string{
"managed-by": "apisix-ingress-controller",
},
},
Plugins: make(Plugins),
}
}
func NewDefaultUpstream() *Upstream {
return &Upstream{
Metadata: Metadata{
Labels: map[string]string{
"managed-by": "apisix-ingress-controller",
},
},
Type: Roundrobin,
}
}
// NewDefaultRoute returns an empty Route with default values.
func NewDefaultRoute() *Route {
return &Route{
Metadata: Metadata{
Desc: "Created by apisix-ingress-controller, DO NOT modify it manually",
Labels: map[string]string{
"managed-by": "apisix-ingress-controller",
},
},
}
}
// NewDefaultStreamRoute returns an empty StreamRoute with default values.
func NewDefaultStreamRoute() *StreamRoute {
return &StreamRoute{
Metadata: Metadata{
Desc: "Created by apisix-ingress-controller, DO NOT modify it manually",
Labels: map[string]string{
"managed-by": "apisix-ingress-controller",
},
},
}
}
const (
PluginProxyRewrite string = "proxy-rewrite"
PluginRedirect string = "redirect"
PluginResponseRewrite string = "response-rewrite"
PluginProxyMirror string = "proxy-mirror"
PluginCORS string = "cors"
)
// RewriteConfig is the rule config for proxy-rewrite plugin.
type RewriteConfig struct {
RewriteTarget string `json:"uri,omitempty" yaml:"uri,omitempty"`
RewriteTargetRegex []string `json:"regex_uri,omitempty" yaml:"regex_uri,omitempty"`
Headers *Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
}
type Headers struct {
Set map[string]string `json:"set,omitempty" yaml:"set,omitempty"`
Add map[string]string `json:"add,omitempty" yaml:"add,omitempty"`
Remove []string `json:"remove,omitempty" yaml:"remove,omitempty"`
}
// ResponseRewriteConfig is the rule config for response-rewrite plugin.
type ResponseRewriteConfig struct {
StatusCode int `json:"status_code,omitempty" yaml:"status_code,omitempty"`
Body string `json:"body,omitempty" yaml:"body,omitempty"`
BodyBase64 bool `json:"body_base64,omitempty" yaml:"body_base64,omitempty"`
Headers *ResponseHeaders `json:"headers,omitempty" yaml:"headers,omitempty"`
LuaRestyExpr []expr.Expr `json:"vars,omitempty" yaml:"vars,omitempty"`
Filters []map[string]string `json:"filters,omitempty" yaml:"filters,omitempty"`
}
type FaultInjectionConfig struct {
Abort *FaultInjectionAbortConfig `json:"abort,omitempty" yaml:"abort,omitempty"`
}
type FaultInjectionAbortConfig struct {
HTTPStatus int `json:"http_status" yaml:"http_status"`
Vars [][]expr.Expr `json:"vars,omitempty" yaml:"vars,omitempty"`
}
type ResponseHeaders struct {
Set map[string]string `json:"set,omitempty" yaml:"set,omitempty"`
Add []string `json:"add,omitempty" yaml:"add,omitempty"`
Remove []string `json:"remove,omitempty" yaml:"remove,omitempty"`
}
// RequestMirror is the rule config for proxy-mirror plugin.
type RequestMirror struct {
Host string `json:"host" yaml:"host"`
}
// RedirectConfig is the rule config for redirect plugin.
type RedirectConfig struct {
HttpToHttps bool `json:"http_to_https,omitempty" yaml:"http_to_https,omitempty"`
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
RetCode int `json:"ret_code,omitempty" yaml:"ret_code,omitempty"`
}
const (
StatusSuccess = "success"
StatusFailed = "failed"
StatusPartialFailed = "partial_failed"
)
type SyncResult struct {
Status string `json:"status"`
TotalResources int `json:"total_resources"`
SuccessCount int `json:"success_count"`
FailedCount int `json:"failed_count"`
Success []SyncStatus `json:"success"`
Failed []SyncStatus `json:"failed"`
}
type SyncStatus struct {
Event StatusEvent `json:"event"`
FailedAt time.Time `json:"failed_at,omitempty"`
SyncedAt time.Time `json:"synced_at,omitempty"`
Reason string `json:"reason,omitempty"`
Response ResponseDetails `json:"response,omitempty"`
}
type StatusEvent struct {
ResourceType string `json:"resourceType"`
Type string `json:"type"`
ResourceID string `json:"resourceId"`
ResourceName string `json:"resourceName"`
ParentID string `json:"parentId,omitempty"`
}
type ResponseDetails struct {
Status int `json:"status"`
Headers map[string]string `json:"headers"`
}
type ResponseData struct {
Value map[string]any `json:"value"`
ErrorMsg string `json:"error_msg"`
}
// Vars represents the route match expressions of APISIX.
type Vars [][]StringOrSlice
// UnmarshalJSON implements json.Unmarshaler interface.
// lua-cjson doesn't distinguish empty array and table,
// and by default empty array will be encoded as '{}'.
// We have to maintain the compatibility.
func (vars *Vars) UnmarshalJSON(p []byte) error {
if p[0] == '{' {
if len(p) != 2 {
return errors.New("unexpected non-empty object")
}
return nil
}
var data [][]StringOrSlice
if err := json.Unmarshal(p, &data); err != nil {
return err
}
*vars = data
return nil
}
// StringOrSlice represents a string or a string slice.
// TODO Do not use interface{} to avoid the reflection overheads.
// +k8s:deepcopy-gen=true
type StringOrSlice struct {
StrVal string `json:"-"`
SliceVal []StringOrSlice `json:"-"`
}
func (s *StringOrSlice) MarshalJSON() ([]byte, error) {
if s.SliceVal != nil {
return json.Marshal(s.SliceVal)
}
return json.Marshal(s.StrVal)
}
func (s *StringOrSlice) UnmarshalJSON(p []byte) error {
if len(p) == 0 {
return errors.New("empty object")
}
if p[0] == '[' {
return json.Unmarshal(p, &s.SliceVal)
}
return json.Unmarshal(p, &s.StrVal)
}
type Config struct {
Name string
ServerAddrs []string
Token string
TlsVerify bool
}
// MarshalJSON implements custom JSON marshaling for adcConfig
// It excludes the Token field for security reasons
func (c Config) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Name string `json:"name"`
ServerAddrs []string `json:"serverAddrs"`
TlsVerify bool `json:"tlsVerify"`
}{
Name: c.Name,
ServerAddrs: c.ServerAddrs,
TlsVerify: c.TlsVerify,
})
}
var (
ResolveGranularity = struct {
Endpoint string
Service string
}{
Endpoint: "endpoint",
Service: "service",
}
)
// ComposeUpstreamName uses namespace, name, subset (optional), port, resolveGranularity info to compose
// the upstream name.
// the resolveGranularity is not composited in the upstream name when it is endpoint.
// ref: https://github.com/apache/apisix-ingress-controller/blob/10059afe3e84b693cc61e6df7a0040890a9d16eb/pkg/types/apisix/v1/types.go#L595-L598
func ComposeUpstreamName(namespace, name, subset string, port intstr.IntOrString, resolveGranularity string) string {
pstr := port.String()
// FIXME Use sync.Pool to reuse this buffer if the upstream
// name composing code path is hot.
var p []byte
plen := len(namespace) + len(name) + len(pstr) + 2
if subset != "" {
plen = plen + len(subset) + 1
}
if resolveGranularity == ResolveGranularity.Service {
plen = plen + len(resolveGranularity) + 1
}
p = make([]byte, 0, plen)
buf := bytes.NewBuffer(p)
buf.WriteString(namespace)
buf.WriteByte('_')
buf.WriteString(name)
buf.WriteByte('_')
if subset != "" {
buf.WriteString(subset)
buf.WriteByte('_')
}
buf.WriteString(pstr)
if resolveGranularity == ResolveGranularity.Service {
buf.WriteByte('_')
buf.WriteString(resolveGranularity)
}
return buf.String()
}
// ComposeExternalUpstreamName uses ApisixUpstream namespace, name to compose the upstream name.
func ComposeExternalUpstreamName(namespace, name string) string {
return namespace + "_" + name
}
// ComposeUpstreamNameForBackendRef composes upstream name using kind, namespace, name and port.
func ComposeUpstreamNameForBackendRef(kind, namespace, name string, port int32) string {
return fmt.Sprintf("%s_%s_%s_%d", kind, namespace, name, port)
}