diff --git a/adapter/config/default_config.go b/adapter/config/default_config.go index ac8691917e..8ed335badb 100644 --- a/adapter/config/default_config.go +++ b/adapter/config/default_config.go @@ -273,4 +273,36 @@ var defaultConfig = &Config{ "endpoint": "/api/v2/spans", }, }, + Deployment: deployment{ + //todo(amali) add validation + Gateway: gateway{ + Namespace: "apk", + AdapterHostName: "", + AdapterHost: "", + CommonControllerHostName: "", + CommonControllerHost: "", + EnforcerPrivateKeyPath: "/home/wso2/security/keystore/enforcer.key", + EnforcerPublicCertPath: "/home/wso2/security/keystore/enforcer.crt", + EnforcerServerName: "", + AdapterTrustedCAPath: "/home/wso2/security/truststore", + AdapterXDSPort: "18000", + CommonControllerXDSPort: "18002", + CommonControllerRestPort: "18003", + EnforcerLabel: "wso2-apk-default", + EnforcerRegion: "UNKNOWN", + EnforcerXDSMaxMsgSize: "4194304", + EnforcerXDSMaxRetries: "3", + JavaOpts: "-Dhttpclient.hostnameVerifier=AllowAll -Xms512m -Xmx512m -XX:MaxRAMFraction=2", + Volumes: volumes{ + RatelimiterTruststoreSecretVolume: "", + EnforcerKeystoreSecretVolume: "", + RouterKeystoreSecretVolume: "", + AdapterTruststoreSecretVolume: "", + EnforcerJwtSecretVolume: "", + EnforcerTrustedCerts: "", + EnforcerApikeyCert: "", + IDPCertificateSecretVolume: "", + }, + }, + }, } diff --git a/adapter/config/types.go b/adapter/config/types.go index 7b6c81740a..9638af9ef7 100644 --- a/adapter/config/types.go +++ b/adapter/config/types.go @@ -79,6 +79,7 @@ type Config struct { PartitionServer partitionServer `toml:"partitionServer"` Analytics analytics `toml:"analytics"` Tracing tracing + Deployment deployment } // Adapter related Configurations @@ -101,7 +102,6 @@ type adapter struct { // Envoy Listener Component related configurations. type envoy struct { - // ListenerCodecType Default to AUTO where both http1 and http2 connections are handled // It can be specifically set to either HTTP1 or HTTP2 ListenerCodecType string @@ -315,6 +315,42 @@ type tracing struct { ConfigProperties map[string]string } +type deployment struct { + Gateway gateway +} + +type gateway struct { + Namespace string + AdapterHostName string + AdapterHost string + CommonControllerHostName string + CommonControllerHost string + EnforcerPrivateKeyPath string + EnforcerPublicCertPath string + EnforcerServerName string + AdapterTrustedCAPath string + AdapterXDSPort string + CommonControllerXDSPort string + CommonControllerRestPort string + EnforcerLabel string + EnforcerRegion string + EnforcerXDSMaxMsgSize string + EnforcerXDSMaxRetries string + JavaOpts string + Volumes volumes +} + +type volumes struct { + RatelimiterTruststoreSecretVolume string + EnforcerKeystoreSecretVolume string + RouterKeystoreSecretVolume string + AdapterTruststoreSecretVolume string + EnforcerJwtSecretVolume string + EnforcerTrustedCerts string + EnforcerApikeyCert string + IDPCertificateSecretVolume string +} + // Metrics defines the configuration for metrics collection. type Metrics struct { Enabled bool diff --git a/adapter/go.mod b/adapter/go.mod index a43b82e128..cebff372f5 100644 --- a/adapter/go.mod +++ b/adapter/go.mod @@ -4,23 +4,29 @@ go 1.22 require ( github.com/envoyproxy/go-control-plane v0.12.0 + github.com/evanphx/json-patch v5.9.0+incompatible github.com/fsnotify/fsnotify v1.7.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 + github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/onsi/ginkgo/v2 v2.14.0 github.com/onsi/gomega v1.30.0 github.com/pelletier/go-toml v1.9.5 - github.com/prometheus/client_golang v1.18.0 - github.com/sirupsen/logrus v1.9.0 + github.com/prometheus/client_golang v1.19.0 + github.com/sirupsen/logrus v1.9.3 + github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 github.com/wso2/apk/common-go-libs v0.0.0-20231208100153-24bee7b4bd81 golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb - google.golang.org/grpc v1.62.0 + google.golang.org/grpc v1.62.1 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.29.2 + k8s.io/apiextensions-apiserver v0.29.2 k8s.io/apimachinery v0.29.2 k8s.io/client-go v0.29.2 + k8s.io/utils v0.0.0-20240102154912-e7106e64919e sigs.k8s.io/controller-runtime v0.17.2 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -28,11 +34,10 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect - github.com/evanphx/json-patch/v5 v5.8.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -43,7 +48,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/imdario/mergo v0.3.16 // indirect @@ -51,15 +55,14 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.50.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/shirou/gopsutil/v3 v3.24.2 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect @@ -69,13 +72,13 @@ require ( github.com/vektah/gqlparser v1.3.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/term v0.17.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.17.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect @@ -83,14 +86,11 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.29.2 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) replace github.com/wso2/apk/adapter => ../adapter @@ -98,9 +98,11 @@ replace github.com/wso2/apk/adapter => ../adapter replace github.com/wso2/apk/common-go-libs => ../common-go-libs require ( + github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa github.com/ghodss/yaml v1.0.0 - github.com/stretchr/testify v1.8.4 - golang.org/x/sys v0.17.0 // indirect + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/proto/otlp v1.1.0 + golang.org/x/sys v0.18.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 sigs.k8s.io/gateway-api v1.0.0 ) diff --git a/adapter/go.sum b/adapter/go.sum index d92a48c78a..851996d0fb 100644 --- a/adapter/go.sum +++ b/adapter/go.sum @@ -17,6 +17,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/datawire/dlib v1.3.0 h1:KkmyXU1kwm3oPBk1ypR70YbcOlEXWzEbx5RE0iRXTGk= +github.com/datawire/dlib v1.3.0/go.mod h1:NiGDmetmbkBvtznpWSx6C0vA0s0LK9aHna3LJDqjruk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -29,10 +31,10 @@ github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjl github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= -github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= -github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -59,8 +61,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -95,8 +97,6 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -117,12 +117,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ= +github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -136,8 +136,8 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -149,8 +149,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/telepresenceio/telepresence/rpc/v2 v2.6.8 h1:q5V85LBT9bA/c4YPa/kMvJGyKZDgBPJTftlAMqJx7j4= +github.com/telepresenceio/telepresence/rpc/v2 v2.6.8/go.mod h1:VlgfRoXaW6Tl8IZbHmMWhITne8HY09/wOFtABHGj3ic= +github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 h1:GMw3nEaOVyi+tNiGko5kAeRtoiEIpXNHmISyZ7fpw14= +github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7/go.mod h1:ihJ97e2gsd8GuzFF/I3B1qcik3XZLpXjumQifXi8Slg= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -162,12 +167,14 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -183,10 +190,10 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -204,12 +211,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -224,8 +232,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -240,8 +248,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= diff --git a/adapter/internal/adapter/adapter.go b/adapter/internal/adapter/adapter.go index 39f9c9be20..7c46efcd97 100644 --- a/adapter/internal/adapter/adapter.go +++ b/adapter/internal/adapter/adapter.go @@ -26,7 +26,12 @@ import ( xdsv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3" enforcerCallbacks "github.com/wso2/apk/adapter/internal/discovery/xds/enforcercallbacks" routercb "github.com/wso2/apk/adapter/internal/discovery/xds/routercallbacks" - "github.com/wso2/apk/adapter/internal/operator" + xdstranslatorrunner "github.com/wso2/apk/adapter/internal/operator/gateway-api/translator/runner" + xdsserverrunner "github.com/wso2/apk/adapter/internal/operator/gateway-api/xds/runner" + infrarunner "github.com/wso2/apk/adapter/internal/operator/infrastructure/runner" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/operator/provider-resources/runner" + providerrunner "github.com/wso2/apk/adapter/internal/operator/provider/runner" apiservice "github.com/wso2/apk/adapter/pkg/discovery/api/wso2/discovery/service/api" configservice "github.com/wso2/apk/adapter/pkg/discovery/api/wso2/discovery/service/config" subscriptionservice "github.com/wso2/apk/adapter/pkg/discovery/api/wso2/discovery/service/subscription" @@ -50,6 +55,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" + ctrl "sigs.k8s.io/controller-runtime" ) var ( @@ -70,7 +76,7 @@ const ( func init() { flag.BoolVar(&debug, "debug", true, "Use debug logging") flag.BoolVar(&onlyLogging, "onlyLogging", false, "Only demo AccessLogging Service") - flag.UintVar(&port, "port", 18000, "Management server port") + flag.UintVar(&port, "port", 18001, "Management server port") flag.UintVar(&alsPort, "als", 18090, "Accesslog server port") flag.StringVar(&mode, "ads", ads, "Management server type (ads, xds, rest)") } @@ -132,6 +138,72 @@ func runManagementServer(conf *config.Config, server xdsv3.Server, enforcerServe } +func SetupRunners(conf *config.Config) { + ctx := ctrl.SetupSignalHandler() + + // Step 1: Start the Kubernetes Provider Service + // It fetches the resources from the kubernetes + // and publishes it + // It also subscribes to status resources and once it receives + // a status resource back, it writes it out. + // Final processed crs will be stored in following pResources. + pResources := new(message.ProviderResources) + providerRunner := providerrunner.New(&providerrunner.Config{ + ProviderResources: pResources, + }) + if err := providerRunner.Start(ctx); err != nil { + logger.LoggerAPKOperator.Error("Error while starting provider service ", err) + } + + // Step 2: Start the GatewayAPI Translator Runner + // It subscribes to the provider resources, translates it to xDS IR + // and infra IR resources and publishes them. + // Final processed structs will be in pResources, xdsIR, and infraIR + xdsIR := new(message.XdsIR) + infraIR := new(message.InfraIR) + gwRunner := runner.New(&runner.Config{ + ProviderResources: pResources, + XdsIR: xdsIR, + InfraIR: infraIR, + }) + if err := gwRunner.Start(ctx); err != nil { + logger.LoggerAPKOperator.Error("Error while starting translation service ", err) + } + + // Step 3: Start the Xds Translator Service + // It subscribes to the xdsIR, translates it into xds Resources and publishes it. + // Final xds configs are in xds. + xds := new(message.Xds) + xdsTranslatorRunner := xdstranslatorrunner.New(&xdstranslatorrunner.Config{ + XdsIR: xdsIR, + Xds: xds, + ProviderResources: pResources, + }) + if err := xdsTranslatorRunner.Start(ctx); err != nil { + logger.LoggerAPKOperator.Error("Error while starting xds translator service ", err) + } + + // Step 4: Start the Infra Manager Runner + // It subscribes to the infraIR, translates it into Envoy Proxy infrastructure + // resources such as K8s deployment and services. + infraRunner := infrarunner.New(&infrarunner.Config{ + InfraIR: infraIR, + }) + if err := infraRunner.Start(ctx); err != nil { + logger.LoggerAPKOperator.Error("Error while starting infrastructure service ", err) + } + + // Step 5: Start the xDS Server + // It subscribes to the xds Resources and configures the remote Envoy Proxy + // via the xDS Protocol. + xdsServerRunner := xdsserverrunner.New(&xdsserverrunner.Config{ + Xds: xds, + }) + if err := xdsServerRunner.Start(ctx); err != nil { + logger.LoggerAPKOperator.Error("Error while starting xds service ", err) + } +} + // Run starts the XDS server and Rest API server. func Run(conf *config.Config) { sig := make(chan os.Signal, 2) @@ -180,7 +252,8 @@ func Run(conf *config.Config) { // Set enforcer startup configs xds.UpdateEnforcerConfig(conf) - go operator.InitOperator(conf.Adapter.Metrics) + // go operator.InitOperator(conf.Adapter.Metrics) + SetupRunners(conf) OUTER: for { diff --git a/adapter/internal/oasparser/envoyconf/constants.go b/adapter/internal/oasparser/envoyconf/constants.go index 1798d6d19a..ca09e482b2 100644 --- a/adapter/internal/oasparser/envoyconf/constants.go +++ b/adapter/internal/oasparser/envoyconf/constants.go @@ -22,7 +22,6 @@ const ( accessLoggerClusterName string = "access-logger" grpcAccessLogLogName string = "apk_access_logs" tracingClusterName string = "wso2_apk_trace" - extAuthzHTTPCluster string = "ext_authz_http_cluster" rateLimitClusterName string = "ratelimit" ) diff --git a/adapter/internal/operator/constants/constants.go b/adapter/internal/operator/constants/constants.go index 872c3491e3..c7deda3fb1 100644 --- a/adapter/internal/operator/constants/constants.go +++ b/adapter/internal/operator/constants/constants.go @@ -21,6 +21,7 @@ package constants const ( APIController string = "APIController" GatewayController string = "GatewayController" + GatewayControllerNew string = "GatewayControllerNew" ApplicationController string = "ApplicationController" SubscriptionController string = "SubscriptionController" TokenIssuerController string = "TokenIssuerController" @@ -39,8 +40,10 @@ const ( OperatorPodNamespaceDefaultValue string = "default" ) +// todo(amali) move to translators // CRD Kinds const ( + GroupName = "dp.wso2.com" KindAuthentication = "Authentication" KindAPI = "API" KindService = "Service" diff --git a/adapter/internal/operator/controllers/dp/tokenissuer_controller.go b/adapter/internal/operator/controllers/dp/tokenissuer_controller.go index 7f46e52efb..75517b4bdc 100644 --- a/adapter/internal/operator/controllers/dp/tokenissuer_controller.go +++ b/adapter/internal/operator/controllers/dp/tokenissuer_controller.go @@ -30,6 +30,8 @@ import ( "github.com/wso2/apk/adapter/pkg/logging" dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -38,10 +40,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" - corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "k8s.io/apimachinery/pkg/fields" + "sigs.k8s.io/controller-runtime/pkg/source" ) const ( @@ -139,7 +139,7 @@ func (r *TokenssuerReconciler) populateTokenReconcileRequestsForConfigMap(ctx co }) requests := []reconcile.Request{} if err == nil && len(tokenIssuerList.Items) > 0 { - + for _, tokenIssuer := range tokenIssuerList.Items { req := reconcile.Request{ NamespacedName: types.NamespacedName{ @@ -168,7 +168,7 @@ func (r *TokenssuerReconciler) populateTokenReconcileRequestsForSecret(ctx conte }) requests := []reconcile.Request{} if err == nil && len(tokenIssuerList.Items) > 0 { - + for _, tokenIssuer := range tokenIssuerList.Items { req := reconcile.Request{ NamespacedName: types.NamespacedName{ diff --git a/adapter/internal/operator/gateway-api/address.go b/adapter/internal/operator/gateway-api/address.go new file mode 100644 index 0000000000..3342bdc523 --- /dev/null +++ b/adapter/internal/operator/gateway-api/address.go @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +var _ AddressesTranslator = (*Translator)(nil) + +type AddressesTranslator interface { + ProcessAddresses(gateways []*GatewayContext, infraIR InfraIRMap) +} + +func (t *Translator) ProcessAddresses(gateways []*GatewayContext, infraIR InfraIRMap) { + for _, gateway := range gateways { + // Infra IR already exist + irKey := t.getIRKey(gateway.Gateway) + gwInfraIR := infraIR[irKey] + + var ips []string + for _, gwadr := range gateway.Spec.Addresses { + if *gwadr.Type == v1.IPAddressType { + ips = append(ips, gwadr.Value) + } + } + gwInfraIR.Proxy.Addresses = ips + } +} diff --git a/adapter/internal/operator/gateway-api/bootstrap/bootstrap.go b/adapter/internal/operator/gateway-api/bootstrap/bootstrap.go new file mode 100644 index 0000000000..1e51b63e1a --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/bootstrap.go @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package bootstrap + +import ( + // Register embed + _ "embed" + "fmt" + "strconv" + "strings" + "text/template" + + "github.com/wso2/apk/adapter/config" + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + "github.com/wso2/apk/adapter/pkg/utils/regex" + "k8s.io/apimachinery/pkg/util/sets" +) + +const ( + // envoyCfgFileName is the name of the Envoy configuration file. + envoyCfgFileName = "bootstrap.yaml" + // EnvoyAdminAddress is the listening address of the envoy admin interface. + EnvoyAdminAddress = "0.0.0.0" + // EnvoyAdminPort is the port used to expose admin interface. + EnvoyAdminPort = 19000 + // envoyAdminAccessLogPath is the path used to expose admin access log. + envoyAdminAccessLogPath = "/dev/null" + + envoyReadinessAddress = "0.0.0.0" + EnvoyReadinessPort = 19001 + EnvoyReadinessPath = "/ready" +) + +//go:embed bootstrap.yaml.tpl +var bootstrapTmplStr string + +var bootstrapTmpl = template.Must(template.New(envoyCfgFileName).Parse(bootstrapTmplStr)) + +// envoyBootstrap defines the envoy Bootstrap configuration. +type bootstrapConfig struct { + // parameters defines configurable bootstrap configuration parameters. + parameters bootstrapParameters + // rendered is the rendered bootstrap configuration. + rendered string +} + +// envoyBootstrap defines the envoy Bootstrap configuration. +type bootstrapParameters struct { + // XdsServer defines the configuration of the XDS server. + XdsServer xdsServerParameters + // AdminServer defines the configuration of the Envoy admin interface. + AdminServer adminServerParameters + // ReadyServer defines the configuration for health check ready listener + ReadyServer readyServerParameters + // EnablePrometheus defines whether to enable metrics endpoint for prometheus. + EnablePrometheus bool + // OtelMetricSinks defines the configuration of the OpenTelemetry sinks. + OtelMetricSinks []metricSink + // EnableStatConfig defines whether to to customize the Envoy proxy stats. + EnableStatConfig bool + // StatsMatcher is to control creation of custom Envoy stats with prefix, + // suffix, and regex expressions match on the name of the stats. + StatsMatcher *StatsMatcherParameters +} + +type xdsServerParameters struct { + // Address is the address of the XDS Server that Envoy is managed by. + Address string + // Port is the port of the XDS Server that Envoy is managed by. + Port int32 +} + +type metricSink struct { + // Address is the address of the XDS Server that Envoy is managed by. + Address string + // Port is the port of the XDS Server that Envoy is managed by. + Port int32 +} + +type adminServerParameters struct { + // Address is the address of the Envoy admin interface. + Address string + // Port is the port of the Envoy admin interface. + Port int32 + // AccessLogPath is the path of the Envoy admin access log. + AccessLogPath string +} + +type readyServerParameters struct { + // Address is the address of the Envoy readiness probe + Address string + // Port is the port of envoy readiness probe + Port int32 + // ReadinessPath is the path for the envoy readiness probe + ReadinessPath string +} + +type StatsMatcherParameters struct { + Exacts []string + Prefixs []string + Suffixs []string + RegularExpressions []string +} + +// render the stringified bootstrap config in yaml format. +func (b *bootstrapConfig) render() error { + buf := new(strings.Builder) + if err := bootstrapTmpl.Execute(buf, b.parameters); err != nil { + return fmt.Errorf("failed to render bootstrap config: %w", err) + } + b.rendered = buf.String() + + return nil +} + +// GetRenderedBootstrapConfig renders the bootstrap YAML string +func GetRenderedBootstrapConfig(proxyMetrics *egv1a1.ProxyMetrics) (string, error) { + var ( + enablePrometheus = true + metricSinks []metricSink + StatsMatcher StatsMatcherParameters + ) + + if proxyMetrics != nil { + if proxyMetrics.Prometheus != nil { + enablePrometheus = !proxyMetrics.Prometheus.Disable + } + + addresses := sets.NewString() + for _, sink := range proxyMetrics.Sinks { + if sink.OpenTelemetry == nil { + continue + } + + // skip duplicate sinks + addr := fmt.Sprintf("%s:%d", sink.OpenTelemetry.Host, sink.OpenTelemetry.Port) + if addresses.Has(addr) { + continue + } + addresses.Insert(addr) + + metricSinks = append(metricSinks, metricSink{ + Address: sink.OpenTelemetry.Host, + Port: sink.OpenTelemetry.Port, + }) + } + + if proxyMetrics.Matches != nil { + // Add custom envoy proxy stats + for _, match := range proxyMetrics.Matches { + // matchType default to exact + matchType := egv1a1.StringMatchExact + if match.Type != nil { + matchType = *match.Type + } + switch matchType { + case egv1a1.StringMatchExact: + StatsMatcher.Exacts = append(StatsMatcher.Exacts, match.Value) + case egv1a1.StringMatchPrefix: + StatsMatcher.Prefixs = append(StatsMatcher.Prefixs, match.Value) + case egv1a1.StringMatchSuffix: + StatsMatcher.Suffixs = append(StatsMatcher.Suffixs, match.Value) + case egv1a1.StringMatchRegularExpression: + if err := regex.Validate(match.Value); err != nil { + return "", err + } + StatsMatcher.RegularExpressions = append(StatsMatcher.RegularExpressions, match.Value) + } + } + } + } + conf := config.ReadConfigs() + xdsport, _ := strconv.Atoi(conf.Deployment.Gateway.AdapterXDSPort) + cfg := &bootstrapConfig{ + parameters: bootstrapParameters{ + XdsServer: xdsServerParameters{ + Address: conf.Deployment.Gateway.AdapterHost, + Port: int32(xdsport), + }, + AdminServer: adminServerParameters{ + Address: EnvoyAdminAddress, + Port: EnvoyAdminPort, + AccessLogPath: envoyAdminAccessLogPath, + }, + ReadyServer: readyServerParameters{ + Address: envoyReadinessAddress, + Port: EnvoyReadinessPort, + ReadinessPath: EnvoyReadinessPath, + }, + EnablePrometheus: enablePrometheus, + OtelMetricSinks: metricSinks, + }, + } + if proxyMetrics != nil && proxyMetrics.Matches != nil { + cfg.parameters.StatsMatcher = &StatsMatcher + } + + if err := cfg.render(); err != nil { + return "", err + } + + return cfg.rendered, nil +} diff --git a/adapter/internal/operator/gateway-api/bootstrap/bootstrap.yaml.tpl b/adapter/internal/operator/gateway-api/bootstrap/bootstrap.yaml.tpl new file mode 100644 index 0000000000..bad77c930d --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/bootstrap.yaml.tpl @@ -0,0 +1,191 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: {{ .AdminServer.AccessLogPath }} + address: + socket_address: + address: {{ .AdminServer.Address }} + port_value: {{ .AdminServer.Port }} +{{- if .StatsMatcher }} +stats_config: + stats_matcher: + inclusion_list: + patterns: + {{- range $_, $item := .StatsMatcher.Exacts }} + - exact: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.Prefixs }} + - prefix: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.Suffixs }} + - suffix: {{$item}} + {{- end}} + {{- range $_, $item := .StatsMatcher.RegularExpressions }} + - safe_regex: + google_re2: {} + regex: {{js $item}} + {{- end}} +{{- end }} +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +{{- if .OtelMetricSinks }} +stats_sinks: +{{- range $idx, $sink := .OtelMetricSinks }} +- name: "envoy.stat_sinks.open_telemetry" + typed_config: + "@type": type.googleapis.com/envoy.extensions.stat_sinks.open_telemetry.v3.SinkConfig + grpc_service: + envoy_grpc: + cluster_name: otel_metric_sink_{{ $idx }} +{{- end }} +{{- end }} +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-{{ .ReadyServer.Address }}-{{ .ReadyServer.Port }} + address: + socket_address: + address: {{ .ReadyServer.Address }} + port_value: {{ .ReadyServer.Port }} + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: {{ .ReadyServer.ReadinessPath }} + - name: envoy.filters.http.ext_authz + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + clear_route_cache: true + include_peer_certificate: true + transport_api_version: 2 + grpc_service: + envoy_grpc: + cluster_name: ext-authz + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + {{- range $idx, $sink := .OtelMetricSinks }} + - name: otel_metric_sink_{{ $idx }} + connect_timeout: 0.250s + type: STRICT_DNS + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: {} + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: otel_metric_sink_{{ $idx }} + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: {{ $sink.Address }} + port_value: {{ $sink.Port }} + {{- end }} + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: apk-test-wso2-apk-adapter-service.apk.svc + port_value: {{ .XdsServer.Port }} + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificates: + private_key: + filename: '/home/wso2/security/keystore/router.key' + certificate_chain: + filename: '/home/wso2/security/keystore/router.crt' + validation_context: + trusted_ca: + filename: '/home/wso2/security/truststore/adapter.crt' + - name: ext-authz + type: STRICT_DNS + connect_timeout: 20s + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: {} + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + tls_certificates: + private_key: + filename: "/home/wso2/security/keystore/router.key" + certificate_chain: + filename: "/home/wso2/security/keystore/router.crt" + validation_context: + trusted_ca: + filename: "/home/wso2/security/truststore/enforcer.crt" + load_assignment: + cluster_name: ext-authz + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: enforcer + port_value: 8081 + \ No newline at end of file diff --git a/adapter/internal/operator/gateway-api/bootstrap/bootstrap_test.go b/adapter/internal/operator/gateway-api/bootstrap/bootstrap_test.go new file mode 100644 index 0000000000..ae41185297 --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/bootstrap_test.go @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package bootstrap + +import ( + "flag" + "fmt" + "os" + "path" +) + +var ( + overrideTestData = flag.Bool("override-testdata", false, "if override the test output data.") +) + +// func TestGetRenderedBootstrapConfig(t *testing.T) { +// cases := []struct { +// name string +// proxyMetrics *egv1a1.ProxyMetrics +// }{ +// { +// name: "disable-prometheus", +// proxyMetrics: &egv1a1.ProxyMetrics{ +// Prometheus: &egv1a1.ProxyPrometheusProvider{ +// Disable: true, +// }, +// }, +// }, +// { +// name: "enable-prometheus", +// proxyMetrics: &egv1a1.ProxyMetrics{ +// Prometheus: &egv1a1.ProxyPrometheusProvider{}, +// }, +// }, +// { +// name: "otel-metrics", +// proxyMetrics: &egv1a1.ProxyMetrics{ +// Prometheus: &egv1a1.ProxyPrometheusProvider{ +// Disable: true, +// }, +// Sinks: []egv1a1.ProxyMetricSink{ +// { +// Type: egv1a1.MetricSinkTypeOpenTelemetry, +// OpenTelemetry: &egv1a1.ProxyOpenTelemetrySink{ +// Host: "otel-collector.monitoring.svc", +// Port: 4317, +// }, +// }, +// }, +// }, +// }, +// { +// name: "custom-stats-matcher", +// proxyMetrics: &egv1a1.ProxyMetrics{ +// Matches: []egv1a1.StringMatch{ +// { +// Type: ptr.To(egv1a1.StringMatchExact), +// Value: "http.foo.bar.cluster.upstream_rq", +// }, +// { +// Type: ptr.To(egv1a1.StringMatchPrefix), +// Value: "http", +// }, +// { +// Type: ptr.To(egv1a1.StringMatchSuffix), +// Value: "upstream_rq", +// }, +// { +// Type: ptr.To(egv1a1.StringMatchRegularExpression), +// Value: "virtual.*", +// }, +// { +// Type: ptr.To(egv1a1.StringMatchPrefix), +// Value: "cluster", +// }, +// }, +// }, +// }, +// } + +// for _, tc := range cases { +// t.Run(tc.name, func(t *testing.T) { +// got, err := GetRenderedBootstrapConfig(tc.proxyMetrics) +// require.NoError(t, err) + +// if *overrideTestData { +// // nolint:gosec +// err = os.WriteFile(path.Join("testdata", "render", fmt.Sprintf("%s.yaml", tc.name)), []byte(got), 0644) +// require.NoError(t, err) +// return +// } + +// expected, err := readTestData(tc.name) +// require.NoError(t, err) +// assert.Equal(t, expected, got) +// }) +// } +// } + +func readTestData(caseName string) (string, error) { + filename := path.Join("testdata", "render", fmt.Sprintf("%s.yaml", caseName)) + + b, err := os.ReadFile(filename) + if err != nil { + return "", err + } + return string(b), nil +} diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.in.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.in.yaml new file mode 100644 index 0000000000..3a18cfed3b --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.in.yaml @@ -0,0 +1,13 @@ +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 20000 +layered_runtime: + layers: + - name: runtime-0 + rtds_layer: + rtds_config: + ads: {} + resource_api_version: V3 + name: runtime-0 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.out.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.out.yaml new file mode 100644 index 0000000000..528105b3b7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/default.out.yaml @@ -0,0 +1,118 @@ +admin: + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socketAddress: + address: 0.0.0.0 + portValue: 20000 +dynamicResources: + adsConfig: + apiType: DELTA_GRPC + grpcServices: + - envoyGrpc: + clusterName: xds_cluster + setNodeOnFirstMessageOnly: true + transportApiVersion: V3 + cdsConfig: + ads: {} + resourceApiVersion: V3 + ldsConfig: + ads: {} + resourceApiVersion: V3 +layeredRuntime: + layers: + - name: global_config + staticLayer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + - name: runtime-0 + rtdsLayer: + name: runtime-0 + rtdsConfig: + ads: {} + resourceApiVersion: V3 +staticResources: + clusters: + # - connectTimeout: 0.250s + # loadAssignment: + # clusterName: prometheus_stats + # endpoints: + # - lbEndpoints: + # - endpoint: + # address: + # socketAddress: + # address: 0.0.0.0 + # portValue: 19000 + # name: prometheus_stats + # type: STATIC + - connectTimeout: 10s + loadAssignment: + clusterName: xds_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-gateway + portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + name: xds_cluster + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificateSdsSecretConfigs: + - name: xds_certificate + sdsConfig: + pathConfigSource: + path: /sds/xds-certificate.json + resourceApiVersion: V3 + tlsParams: + tlsMaximumProtocolVersion: TLSv1_3 + validationContextSdsSecretConfig: + name: xds_trusted_ca + sdsConfig: + pathConfigSource: + path: /sds/xds-trusted-ca.json + resourceApiVersion: V3 + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s + listeners: + - address: + socketAddress: + address: 0.0.0.0 + portValue: 19001 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.health_check + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + headers: + - name: :path + stringMatch: + exact: /ready + passThroughMode: false + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + routeConfig: + name: local_route + statPrefix: eg-ready-http + name: envoy-gateway-proxy-ready-0.0.0.0-19001 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.in.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.in.yaml new file mode 100644 index 0000000000..8b4f9363f9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.in.yaml @@ -0,0 +1,34 @@ +stats_sinks: + - name: envoy.stat_sinks.metrics_service + typed_config: + "@type": type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig + transport_api_version: V3 + grpc_service: + envoy_grpc: + cluster_name: metrics_cluster +static_resources: + clusters: + - connect_timeout: 1s + dns_lookup_family: V4_ONLY + dns_refresh_rate: 30s + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: metrics_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: skywalking-oap.skywalking + portValue: 11800 + name: metrics_cluster + respect_dns_ttl: true + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.out.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.out.yaml new file mode 100644 index 0000000000..ccfff8e1fb --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/merge/stats_sinks.out.yaml @@ -0,0 +1,140 @@ +admin: + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socketAddress: + address: 0.0.0.0 + portValue: 19000 +dynamicResources: + adsConfig: + apiType: DELTA_GRPC + grpcServices: + - envoyGrpc: + clusterName: xds_cluster + setNodeOnFirstMessageOnly: true + transportApiVersion: V3 + cdsConfig: + ads: {} + resourceApiVersion: V3 + ldsConfig: + ads: {} + resourceApiVersion: V3 +layeredRuntime: + layers: + - name: global_config + staticLayer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +staticResources: + clusters: + # - connectTimeout: 0.250s + # loadAssignment: + # clusterName: prometheus_stats + # endpoints: + # - lbEndpoints: + # - endpoint: + # address: + # socketAddress: + # address: 0.0.0.0 + # portValue: 19000 + # name: prometheus_stats + # type: STATIC + - connectTimeout: 10s + loadAssignment: + clusterName: xds_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-gateway + portValue: 18000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + name: xds_cluster + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsParams: + tlsMinimumProtocolVersion: TLSv1_2 + tlsMaximumProtocolVersion: TLSv1_2 + tlsCertificates: + privateKey: + filename: '/home/wso2/security/keystore/router.key' + certificateChain: + filename: '/home/wso2/security/keystore/router.crt' + validationContext: + trustedCA: + filename: '/home/wso2/security/truststore/adapter.crt' + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s + - connectTimeout: 1s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + loadAssignment: + clusterName: metrics_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: skywalking-oap.skywalking + portValue: 11800 + name: metrics_cluster + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + connectionKeepalive: + interval: 30s + timeout: 5s + listeners: + - address: + socketAddress: + address: 0.0.0.0 + portValue: 19001 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.health_check + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + headers: + - name: :path + stringMatch: + exact: /ready + passThroughMode: false + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + routeConfig: + name: local_route + statPrefix: eg-ready-http + name: envoy-gateway-proxy-ready-0.0.0.0-19001 +statsSinks: +- name: envoy.stat_sinks.metrics_service + typedConfig: + '@type': type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig + grpcService: + envoyGrpc: + clusterName: metrics_cluster + transportApiVersion: V3 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/render/custom-stats-matcher.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/custom-stats-matcher.yaml new file mode 100644 index 0000000000..7364bc85a7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/custom-stats-matcher.yaml @@ -0,0 +1,112 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 +stats_config: + stats_matcher: + inclusion_list: + patterns: + - exact: http.foo.bar.cluster.upstream_rq + - prefix: http + - prefix: cluster + - suffix: upstream_rq + - safe_regex: + google_re2: {} + regex: virtual.* +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/render/disable-prometheus.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/disable-prometheus.yaml new file mode 100644 index 0000000000..ca8a5c09ca --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/disable-prometheus.yaml @@ -0,0 +1,101 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/render/enable-prometheus.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/enable-prometheus.yaml new file mode 100644 index 0000000000..ca8a5c09ca --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/enable-prometheus.yaml @@ -0,0 +1,101 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/adapter/internal/operator/gateway-api/bootstrap/testdata/render/otel-metrics.yaml b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/otel-metrics.yaml new file mode 100644 index 0000000000..82fbd38ad6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/bootstrap/testdata/render/otel-metrics.yaml @@ -0,0 +1,126 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +stats_sinks: +- name: "envoy.stat_sinks.open_telemetry" + typed_config: + "@type": type.googleapis.com/envoy.extensions.stat_sinks.open_telemetry.v3.SinkConfig + grpc_service: + envoy_grpc: + cluster_name: otel_metric_sink_0 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: otel_metric_sink_0 + connect_timeout: 0.250s + type: STRICT_DNS + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: {} + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: otel_metric_sink_0 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: otel-collector.monitoring.svc + port_value: 4317 + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 diff --git a/adapter/internal/operator/gateway-api/contexts.go b/adapter/internal/operator/gateway-api/contexts.go new file mode 100644 index 0000000000..40ab07009f --- /dev/null +++ b/adapter/internal/operator/gateway-api/contexts.go @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "reflect" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// GatewayContext wraps a Gateway and provides helper methods for setting conditions, accessing Listeners, etc. +type GatewayContext struct { + *gwapiv1.Gateway + listeners []*ListenerContext +} + +// ResetListeners resets the listener statuses and re-generates the GatewayContext +// ListenerContexts from the Gateway spec. +func (g *GatewayContext) ResetListeners() { + lenListener := len(g.Spec.Listeners) + g.Status.Listeners = make([]gwapiv1.ListenerStatus, lenListener) + g.listeners = make([]*ListenerContext, lenListener) + for i := range g.Spec.Listeners { + listener := &g.Spec.Listeners[i] + g.Status.Listeners[i] = gwapiv1.ListenerStatus{Name: listener.Name} + g.listeners[i] = &ListenerContext{ + listenerStatusID: i, + Listener: listener, + gateway: g.Gateway, + } + } +} + +// ListenerContext wraps a Listener and provides helper methods for +// setting conditions and other status information on the associated +// Gateway, etc. +type ListenerContext struct { + *gwapiv1.Listener + + gateway *gwapiv1.Gateway + listenerStatusID int + namespaceSelector labels.Selector + tlsSecrets []*v1.Secret +} + +func (l *ListenerContext) SetCondition(conditionType gwapiv1.ListenerConditionType, status metav1.ConditionStatus, reason gwapiv1.ListenerConditionReason, message string) { + cond := metav1.Condition{ + Type: string(conditionType), + Status: status, + Reason: string(reason), + Message: message, + ObservedGeneration: l.gateway.Generation, + LastTransitionTime: metav1.NewTime(time.Now()), + } + + index := -1 + for i, existing := range l.gateway.Status.Listeners[l.listenerStatusID].Conditions { + if existing.Type == cond.Type { + // return early if the condition is unchanged + if existing.Status == cond.Status && + existing.Reason == cond.Reason && + existing.Message == cond.Message && + existing.ObservedGeneration == cond.ObservedGeneration { + return + } + index = i + break + } + } + + if index > -1 { + l.gateway.Status.Listeners[l.listenerStatusID].Conditions[index] = cond + } else { + l.gateway.Status.Listeners[l.listenerStatusID].Conditions = append(l.gateway.Status.Listeners[l.listenerStatusID].Conditions, cond) + } +} + +func (l *ListenerContext) SetSupportedKinds(kinds ...gwapiv1.RouteGroupKind) { + l.gateway.Status.Listeners[l.listenerStatusID].SupportedKinds = kinds +} + +func (l *ListenerContext) IncrementAttachedRoutes() { + l.gateway.Status.Listeners[l.listenerStatusID].AttachedRoutes++ +} + +func (l *ListenerContext) AttachedRoutes() int32 { + return l.gateway.Status.Listeners[l.listenerStatusID].AttachedRoutes +} + +func (l *ListenerContext) AllowsKind(kind gwapiv1.RouteGroupKind) bool { + for _, allowed := range l.gateway.Status.Listeners[l.listenerStatusID].SupportedKinds { + if GroupDerefOr(allowed.Group, "") == GroupDerefOr(kind.Group, "") && + allowed.Kind == kind.Kind { + return true + } + } + + return false +} + +func (l *ListenerContext) AllowsNamespace(namespace *v1.Namespace) bool { + if namespace == nil { + return false + } + + if l.AllowedRoutes == nil || l.AllowedRoutes.Namespaces == nil || l.AllowedRoutes.Namespaces.From == nil { + return l.gateway.Namespace == namespace.Name + } + + switch *l.AllowedRoutes.Namespaces.From { + case gwapiv1.NamespacesFromAll: + return true + case gwapiv1.NamespacesFromSelector: + if l.namespaceSelector == nil { + return false + } + return l.namespaceSelector.Matches(labels.Set(namespace.Labels)) + default: + // NamespacesFromSame is the default + return l.gateway.Namespace == namespace.Name + } +} + +func (l *ListenerContext) IsReady() bool { + for _, cond := range l.gateway.Status.Listeners[l.listenerStatusID].Conditions { + if cond.Type == string(gwapiv1.ListenerConditionProgrammed) && cond.Status == metav1.ConditionTrue { + return true + } + } + + return false +} + +func (l *ListenerContext) GetConditions() []metav1.Condition { + return l.gateway.Status.Listeners[l.listenerStatusID].Conditions +} + +func (l *ListenerContext) SetTLSSecrets(tlsSecrets []*v1.Secret) { + l.tlsSecrets = tlsSecrets +} + +// RouteContext represents a generic Route object (HTTPRoute, TLSRoute, etc.) +// that can reference Gateway objects. +type RouteContext interface { + client.Object +} + +// HTTPRouteContext wraps an HTTPRoute and provides helper methods for +// accessing the route's parents. +type HTTPRouteContext struct { + // GatewayControllerName is the name of the Gateway API controller. + GatewayControllerName string + + *gwapiv1.HTTPRoute + + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext +} + +// GRPCRouteContext wraps a GRPCRoute and provides helper methods for +// accessing the route's parents. +type GRPCRouteContext struct { + // GatewayControllerName is the name of the Gateway API controller. + GatewayControllerName string + + *v1alpha2.GRPCRoute + + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext +} + +// TLSRouteContext wraps a TLSRoute and provides helper methods for +// accessing the route's parents. +type TLSRouteContext struct { + // GatewayControllerName is the name of the Gateway API controller. + GatewayControllerName string + + *v1alpha2.TLSRoute + + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext +} + +// UDPRouteContext wraps a UDPRoute and provides helper methods for +// accessing the route's parents. +type UDPRouteContext struct { + // GatewayControllerName is the name of the Gateway API controller. + GatewayControllerName string + + *v1alpha2.UDPRoute + + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext +} + +// TCPRouteContext wraps a TCPRoute and provides helper methods for +// accessing the route's parents. +type TCPRouteContext struct { + // GatewayControllerName is the name of the Gateway API controller. + GatewayControllerName string + + *v1alpha2.TCPRoute + + ParentRefs map[gwapiv1.ParentReference]*RouteParentContext +} + +// GetRouteType returns the Kind of the Route object, HTTPRoute, +// TLSRoute, TCPRoute, UDPRoute etc. +func GetRouteType(route RouteContext) gwapiv1.Kind { + rv := reflect.ValueOf(route).Elem() + return gwapiv1.Kind(rv.FieldByName("Kind").String()) +} + +// TODO: [v1alpha2-gwapiv1] This should not be required once all Route +// objects being implemented are of type gwapiv1. +// GetHostnames returns the hosts targeted by the Route object. +func GetHostnames(route RouteContext) []string { + rv := reflect.ValueOf(route).Elem() + kind := rv.FieldByName("Kind").String() + if kind == KindTCPRoute || kind == KindUDPRoute { + return nil + } + + hs := rv.FieldByName("Spec").FieldByName("Hostnames") + hostnames := make([]string, hs.Len()) + for i := 0; i < len(hostnames); i++ { + hostnames[i] = hs.Index(i).String() + } + return hostnames +} + +// TODO: [v1alpha2-gwapiv1] This should not be required once all Route +// objects being implemented are of type gwapiv1. +// GetParentReferences returns the ParentReference of the Route object. +func GetParentReferences(route RouteContext) []gwapiv1.ParentReference { + rv := reflect.ValueOf(route).Elem() + kind := rv.FieldByName("Kind").String() + pr := rv.FieldByName("Spec").FieldByName("ParentRefs") + if kind == KindHTTPRoute || kind == KindGRPCRoute { + return pr.Interface().([]gwapiv1.ParentReference) + } + + parentReferences := make([]gwapiv1.ParentReference, pr.Len()) + for i := 0; i < len(parentReferences); i++ { + p := pr.Index(i).Interface().(gwapiv1.ParentReference) + parentReferences[i] = UpgradeParentReference(p) + } + return parentReferences +} + +// GetRouteStatus returns the RouteStatus object associated with the Route. +func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus { + rv := reflect.ValueOf(route).Elem() + rs := rv.FieldByName("Status").FieldByName("RouteStatus").Interface().(gwapiv1.RouteStatus) + return &rs +} + +// GetRouteParentContext returns RouteParentContext by using the Route +// objects' ParentReference. +func GetRouteParentContext(route RouteContext, forParentRef gwapiv1.ParentReference) *RouteParentContext { + rv := reflect.ValueOf(route).Elem() + pr := rv.FieldByName("ParentRefs") + if pr.IsNil() { + mm := reflect.MakeMap(reflect.TypeOf(map[gwapiv1.ParentReference]*RouteParentContext{})) + pr.Set(mm) + } + + if p := pr.MapIndex(reflect.ValueOf(forParentRef)); p.IsValid() && !p.IsZero() { + ctx := p.Interface().(*RouteParentContext) + return ctx + } + + isHTTPRoute := false + if rv.FieldByName("Kind").String() == KindHTTPRoute { + isHTTPRoute = true + } + + var parentRef *gwapiv1.ParentReference + specParentRefs := rv.FieldByName("Spec").FieldByName("ParentRefs") + for i := 0; i < specParentRefs.Len(); i++ { + p := specParentRefs.Index(i).Interface().(gwapiv1.ParentReference) + up := p + if !isHTTPRoute { + up = UpgradeParentReference(p) + } + if reflect.DeepEqual(up, forParentRef) { + if isHTTPRoute { + parentRef = &p + } else { + upgraded := UpgradeParentReference(p) + parentRef = &upgraded + } + break + } + } + if parentRef == nil { + panic("parentRef not found") + } + + routeParentStatusIdx := -1 + statusParents := rv.FieldByName("Status").FieldByName("Parents") + for i := 0; i < statusParents.Len(); i++ { + p := statusParents.Index(i).FieldByName("ParentRef").Interface().(gwapiv1.ParentReference) + if !isHTTPRoute { + p = UpgradeParentReference(p) + defaultNamespace := gwapiv1.Namespace(metav1.NamespaceDefault) + if forParentRef.Namespace == nil { + forParentRef.Namespace = &defaultNamespace + } + if p.Namespace == nil { + p.Namespace = &defaultNamespace + } + } + if reflect.DeepEqual(p, forParentRef) { + routeParentStatusIdx = i + break + } + } + if routeParentStatusIdx == -1 { + tmpPR := forParentRef + if !isHTTPRoute { + tmpPR = DowngradeParentReference(tmpPR) + } + rParentStatus := v1alpha2.RouteParentStatus{ + ControllerName: v1alpha2.GatewayController(rv.FieldByName("GatewayControllerName").String()), + ParentRef: tmpPR, + } + statusParents.Set(reflect.Append(statusParents, reflect.ValueOf(rParentStatus))) + routeParentStatusIdx = statusParents.Len() - 1 + } + + ctx := &RouteParentContext{ + ParentReference: parentRef, + routeParentStatusIdx: routeParentStatusIdx, + } + rctx := reflect.ValueOf(ctx) + rctx.Elem().FieldByName(string(GetRouteType(route))).Set(rv.Field(1)) + pr.SetMapIndex(reflect.ValueOf(forParentRef), rctx) + return ctx +} + +// RouteParentContext wraps a ParentReference and provides helper methods for +// setting conditions and other status information on the associated +// HTTPRoute, TLSRoute etc. +type RouteParentContext struct { + *gwapiv1.ParentReference + + // TODO: [v1alpha2-gwapiv1] This can probably be replaced with + // a single field pointing to *gwapiv1.RouteStatus. + HTTPRoute *gwapiv1.HTTPRoute + GRPCRoute *v1alpha2.GRPCRoute + TLSRoute *v1alpha2.TLSRoute + TCPRoute *v1alpha2.TCPRoute + UDPRoute *v1alpha2.UDPRoute + + routeParentStatusIdx int + listeners []*ListenerContext +} + +func (r *RouteParentContext) SetListeners(listeners ...*ListenerContext) { + r.listeners = append(r.listeners, listeners...) +} + +func (r *RouteParentContext) SetCondition(route RouteContext, conditionType gwapiv1.RouteConditionType, status metav1.ConditionStatus, reason gwapiv1.RouteConditionReason, message string) { + cond := metav1.Condition{ + Type: string(conditionType), + Status: status, + Reason: string(reason), + Message: message, + ObservedGeneration: route.GetGeneration(), + LastTransitionTime: metav1.NewTime(time.Now()), + } + + idx := -1 + routeStatus := GetRouteStatus(route) + for i, existing := range routeStatus.Parents[r.routeParentStatusIdx].Conditions { + if existing.Type == cond.Type { + // return early if the condition is unchanged + if existing.Status == cond.Status && + existing.Reason == cond.Reason && + existing.Message == cond.Message && + existing.ObservedGeneration == cond.ObservedGeneration { + return + } + idx = i + break + } + } + + if idx > -1 { + routeStatus.Parents[r.routeParentStatusIdx].Conditions[idx] = cond + } else { + routeStatus.Parents[r.routeParentStatusIdx].Conditions = append(routeStatus.Parents[r.routeParentStatusIdx].Conditions, cond) + } +} + +func (r *RouteParentContext) ResetConditions(route RouteContext) { + routeStatus := GetRouteStatus(route) + routeStatus.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0) +} + +func (r *RouteParentContext) HasCondition(route RouteContext, condType gwapiv1.RouteConditionType, status metav1.ConditionStatus) bool { + var conditions []metav1.Condition + routeStatus := GetRouteStatus(route) + conditions = routeStatus.Parents[r.routeParentStatusIdx].Conditions + for _, c := range conditions { + if c.Type == string(condType) && c.Status == status { + return true + } + } + return false +} + +// BackendRefContext represents a generic BackendRef object (HTTPBackendRef, GRPCBackendRef or BackendRef itself) +type BackendRefContext any + +func GetBackendRef(b BackendRefContext) *gwapiv1.BackendRef { + rv := reflect.ValueOf(b) + br := rv.FieldByName("BackendRef") + if br.IsValid() { + backendRef := br.Interface().(gwapiv1.BackendRef) + return &backendRef + + } + backendRef := b.(gwapiv1.BackendRef) + return &backendRef +} + +func GetFilters(b BackendRefContext) any { + return reflect.ValueOf(b).FieldByName("Filters").Interface() +} diff --git a/adapter/internal/operator/gateway-api/crypto/certgen.go b/adapter/internal/operator/gateway-api/crypto/certgen.go new file mode 100644 index 0000000000..dfc2bd3055 --- /dev/null +++ b/adapter/internal/operator/gateway-api/crypto/certgen.go @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package crypto + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha1" // nolint:gosec + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "time" + + "github.com/wso2/apk/adapter/config" +) + +const ( + // DefaultEnvoyGatewayDNSPrefix defines the default Envoy Gateway DNS prefix. + DefaultEnvoyGatewayDNSPrefix = "envoy-gateway" + + // DefaultEnvoyDNSPrefix defines the default Envoy DNS prefix. + DefaultEnvoyDNSPrefix = "*" + + // DefaultCertificateLifetime holds the default certificate lifetime (in days). + DefaultCertificateLifetime = 365 * 5 + + // keySize sets the RSA key size to 2048 bits. This is minimum recommended size + // for RSA keys. + keySize = 2048 +) + +// Configuration holds config parameters used for generating certificates. +type Configuration struct { + // Provider defines the desired cert provider and provider-specific + // configuration. + Provider *CertProvider +} + +func (c *Configuration) getProvider() { + if c.Provider == nil { + c.Provider = &CertProvider{ + Type: ProviderTypeEnvoyGateway, + } + } +} + +// CertProvider defines the provider of certificates. +type CertProvider struct { + // Type is the type of provider to use for managing certificates. + Type ProviderType `json:"type"` +} + +// ProviderType defines the types of supported certificate providers. +type ProviderType string + +const ( + // ProviderTypeEnvoyGateway defines the "EnvoyGateway" provider. + // EnvoyGateway implements a self-signed CA and generates server + // certs for Envoy Gateway and Envoy. + ProviderTypeEnvoyGateway ProviderType = "EnvoyGateway" +) + +// Certificates contains a set of Certificates as []byte each holding +// the CA Cert along with Envoy Gateway & Envoy certificates. +type Certificates struct { + CACertificate []byte + EnvoyGatewayCertificate []byte + EnvoyGatewayPrivateKey []byte + EnvoyCertificate []byte + EnvoyPrivateKey []byte + EnvoyRateLimitCertificate []byte + EnvoyRateLimitPrivateKey []byte + OIDCHMACSecret []byte +} + +// certificateRequest defines a certificate request. +type certificateRequest struct { + caCertPEM []byte + caKeyPEM []byte + expiry time.Time + commonName string + altNames []string +} + +// GenerateCerts generates a CA Certificate along with certificates for Envoy Gateway +// and Envoy returning them as a *Certificates struct or error if encountered. +func GenerateCerts() (*Certificates, error) { + conf := config.ReadConfigs() + gatewayNs := conf.Deployment.Gateway.Namespace + certCfg := new(Configuration) + + certCfg.getProvider() + switch certCfg.Provider.Type { + case ProviderTypeEnvoyGateway: + now := time.Now() + expiry := now.Add(24 * time.Duration(DefaultCertificateLifetime) * time.Hour) + caCertPEM, caKeyPEM, err := newCA(DefaultEnvoyGatewayDNSPrefix, expiry) + if err != nil { + return nil, err + } + + var egDNSNames, envoyDNSNames []string + egDNSNames = kubeServiceNames(DefaultEnvoyGatewayDNSPrefix, gatewayNs, "cluster.local") + envoyDNSNames = append(envoyDNSNames, fmt.Sprintf("*.%s", gatewayNs)) + + egCertReq := &certificateRequest{ + caCertPEM: caCertPEM, + caKeyPEM: caKeyPEM, + expiry: expiry, + commonName: DefaultEnvoyGatewayDNSPrefix, + altNames: egDNSNames, + } + + egCert, egKey, err := newCert(egCertReq) + if err != nil { + return nil, err + } + + envoyCertReq := &certificateRequest{ + caCertPEM: caCertPEM, + caKeyPEM: caKeyPEM, + expiry: expiry, + commonName: DefaultEnvoyDNSPrefix, + altNames: envoyDNSNames, + } + + envoyCert, envoyKey, err := newCert(envoyCertReq) + if err != nil { + return nil, err + } + + envoyRateLimitCertReq := &certificateRequest{ + caCertPEM: caCertPEM, + caKeyPEM: caKeyPEM, + expiry: expiry, + commonName: DefaultEnvoyDNSPrefix, + altNames: envoyDNSNames, + } + + envoyRateLimitCert, envoyRateLimitKey, err := newCert(envoyRateLimitCertReq) + if err != nil { + return nil, err + } + + oidcHMACSecret, err := generateHMACSecret() + if err != nil { + return nil, err + } + + return &Certificates{ + CACertificate: caCertPEM, + EnvoyGatewayCertificate: egCert, + EnvoyGatewayPrivateKey: egKey, + EnvoyCertificate: envoyCert, + EnvoyPrivateKey: envoyKey, + EnvoyRateLimitCertificate: envoyRateLimitCert, + EnvoyRateLimitPrivateKey: envoyRateLimitKey, + OIDCHMACSecret: oidcHMACSecret, + }, nil + default: + // Envoy Gateway, e.g. self-signed CA, is the only supported certificate provider. + return nil, fmt.Errorf("unsupported certificate provider type %v", certCfg.Provider.Type) + } +} + +// newCert generates a new keypair based on the given the request. +// The return values are cert, key, err. +func newCert(request *certificateRequest) ([]byte, []byte, error) { + caKeyPair, err := tls.X509KeyPair(request.caCertPEM, request.caKeyPEM) + if err != nil { + return nil, nil, err + } + caCert, err := x509.ParseCertificate(caKeyPair.Certificate[0]) + if err != nil { + return nil, nil, err + } + caKey, ok := caKeyPair.PrivateKey.(*rsa.PrivateKey) + if !ok { + return nil, nil, fmt.Errorf("CA private key has unexpected type %T", caKeyPair.PrivateKey) + } + + newKey, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + return nil, nil, fmt.Errorf("cannot generate key: %w", err) + } + + now := time.Now() + template := &x509.Certificate{ + SerialNumber: newSerial(now), + Subject: pkix.Name{ + CommonName: request.commonName, + }, + NotBefore: now.UTC().AddDate(0, 0, -1), + NotAfter: request.expiry.UTC(), + SubjectKeyId: bigIntHash(newKey.N), + KeyUsage: x509.KeyUsageDigitalSignature | + x509.KeyUsageDataEncipherment | + x509.KeyUsageKeyEncipherment | + x509.KeyUsageContentCommitment, + DNSNames: request.altNames, + } + newCert, err := x509.CreateCertificate(rand.Reader, template, caCert, &newKey.PublicKey, caKey) + if err != nil { + return nil, nil, err + } + + newKeyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(newKey), + }) + newCertPEM := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: newCert, + }) + return newCertPEM, newKeyPEM, nil + +} + +// newCA generates a new CA, given the CA's CN and an expiry time. +// The return order is cacert, cakey, error. +func newCA(cn string, expiry time.Time) ([]byte, []byte, error) { + key, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + return nil, nil, err + } + + now := time.Now() + serial := newSerial(now) + template := &x509.Certificate{ + SerialNumber: serial, + Subject: pkix.Name{ + CommonName: cn, + SerialNumber: serial.String(), + }, + NotBefore: now.UTC().AddDate(0, 0, -1), + NotAfter: expiry.UTC(), + SubjectKeyId: bigIntHash(key.N), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + IsCA: true, + BasicConstraintsValid: true, + } + certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + return nil, nil, err + } + certPEMData := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certDER, + }) + keyPEMData := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + return certPEMData, keyPEMData, nil +} + +func newSerial(now time.Time) *big.Int { + return big.NewInt(int64(now.Nanosecond())) +} + +// bigIntHash generates a SubjectKeyId by hashing the modulus of the private +// key. This isn't one of the methods listed in RFC 5280 4.2.1.2, but that also +// notes that other methods are acceptable. +// +// gosec makes a blanket claim that SHA-1 is unacceptable, which is +// false here. The core Go method of generations the SubjectKeyId (see +// https://github.com/golang/go/issues/26676) also uses SHA-1, as recommended +// by RFC 5280. +func bigIntHash(n *big.Int) []byte { + h := sha1.New() // nolint:gosec + h.Write(n.Bytes()) // nolint:errcheck + return h.Sum(nil) +} + +func kubeServiceNames(service, namespace, dnsName string) []string { + return []string{ + service, + fmt.Sprintf("%s.%s", service, namespace), + fmt.Sprintf("%s.%s.svc", service, namespace), + fmt.Sprintf("%s.%s.svc.%s", service, namespace, dnsName), + } +} + +func generateHMACSecret() ([]byte, error) { + // Set the desired length of the secret key in bytes + keyLength := 32 + + // Create a byte slice to hold the random bytes + key := make([]byte, keyLength) + + // Read random bytes from the cryptographically secure random number generator + _, err := rand.Read(key) + if err != nil { + return nil, fmt.Errorf("failed to generate hmack secret key: %w", err) + } + + return key, nil +} diff --git a/adapter/internal/operator/gateway-api/crypto/certgen_test.go b/adapter/internal/operator/gateway-api/crypto/certgen_test.go new file mode 100644 index 0000000000..df4d5c399f --- /dev/null +++ b/adapter/internal/operator/gateway-api/crypto/certgen_test.go @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package crypto + +import ( + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestGenerateCerts(t *testing.T) { + type testcase struct { + certConfig *Configuration + wantEnvoyGatewayDNSName string + wantEnvoyDNSName string + } + + run := func(t *testing.T, name string, tc testcase) { + t.Helper() + + t.Run(name, func(t *testing.T) { + t.Helper() + + got, err := GenerateCerts() + require.NoError(t, err) + + roots := x509.NewCertPool() + ok := roots.AppendCertsFromPEM(got.CACertificate) + require.Truef(t, ok, "Failed to set up CA cert for testing, maybe it's an invalid PEM") + + currentTime := time.Now() + + err = verifyCert(got.EnvoyGatewayCertificate, roots, tc.wantEnvoyGatewayDNSName, currentTime) + require.NoErrorf(t, err, "Validating %s failed", name) + + err = verifyCert(got.EnvoyCertificate, roots, tc.wantEnvoyDNSName, currentTime) + require.NoErrorf(t, err, "Validating %s failed", name) + }) + } + + run(t, "no configuration - use defaults", testcase{ + certConfig: &Configuration{}, + wantEnvoyGatewayDNSName: DefaultEnvoyGatewayDNSPrefix, + wantEnvoyDNSName: fmt.Sprintf("*.%s", "apk"), + }) +} + +func TestGeneratedValidKubeCerts(t *testing.T) { + now := time.Now() + expiry := now.Add(24 * 365 * time.Hour) + + caCert, caKey, err := newCA("envoy-gateway", expiry) + require.NoErrorf(t, err, "Failed to generate CA cert") + + egCertReq := &certificateRequest{ + caCertPEM: caCert, + caKeyPEM: caKey, + expiry: expiry, + commonName: "envoy-gateway", + altNames: kubeServiceNames("envoy-gateway", "apk", "cluster.local"), + } + egCert, _, err := newCert(egCertReq) + require.NoErrorf(t, err, "Failed to generate Envoy Gateway cert") + + roots := x509.NewCertPool() + ok := roots.AppendCertsFromPEM(caCert) + require.Truef(t, ok, "Failed to set up CA cert for testing, maybe it's an invalid PEM") + + envoyCertReq := &certificateRequest{ + caCertPEM: caCert, + caKeyPEM: caKey, + expiry: expiry, + commonName: "envoy", + altNames: kubeServiceNames("envoy", "apk", "cluster.local"), + } + envoyCert, _, err := newCert(envoyCertReq) + require.NoErrorf(t, err, "Failed to generate Envoy cert") + + envoyRateLimitCertReq := &certificateRequest{ + caCertPEM: caCert, + caKeyPEM: caKey, + expiry: expiry, + commonName: "envoy", + altNames: kubeServiceNames("envoy", "apk", "cluster.local"), + } + + envoyRateLimitCert, _, err := newCert(envoyRateLimitCertReq) + require.NoErrorf(t, err, "Failed to generate Envoy Rate Limit Client cert") + + tests := []struct { + name string + cert []byte + dnsName string + }{ + { + name: "envoy gateway cert", + cert: egCert, + dnsName: "envoy-gateway", + }, + { + name: "envoy cert", + cert: envoyCert, + dnsName: "envoy", + }, + { + name: "envoy rate limit client cert", + cert: envoyRateLimitCert, + dnsName: "envoy", + }, + } + + for i := range tests { + tc := tests[i] + t.Run(tc.name, func(t *testing.T) { + err := verifyCert(tc.cert, roots, tc.dnsName, now) + require.NoErrorf(t, err, "Validating %s failed", tc.name) + }) + } + +} + +func verifyCert(certPEM []byte, roots *x509.CertPool, dnsname string, currentTime time.Time) error { + block, _ := pem.Decode(certPEM) + if block == nil { + return fmt.Errorf("failed to decode %s certificate from PEM form", dnsname) + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return err + } + + opts := x509.VerifyOptions{ + DNSName: dnsname, + Roots: roots, + CurrentTime: currentTime, + } + if _, err = cert.Verify(opts); err != nil { + return fmt.Errorf("certificate verification failed: %w", err) + } + + return nil +} + +func TestGenerateHMACSecret(t *testing.T) { + bytes, _ := generateHMACSecret() + encodedSecret := base64.StdEncoding.EncodeToString(bytes) + fmt.Println("Base64 encoded secret:", encodedSecret) +} diff --git a/adapter/internal/operator/gateway-api/envoy/shutdown_manager.go b/adapter/internal/operator/gateway-api/envoy/shutdown_manager.go new file mode 100644 index 0000000000..94dc8b8c37 --- /dev/null +++ b/adapter/internal/operator/gateway-api/envoy/shutdown_manager.go @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package envoy + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "golang.org/x/sys/unix" + + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/bootstrap" +) + +const ( + // ShutdownManagerPort is the port Envoy shutdown manager will listen on. + ShutdownManagerPort = 19002 + // ShutdownManagerHealthCheckPath is the path used for health checks. + ShutdownManagerHealthCheckPath = "/healthz" + // ShutdownManagerReadyPath is the path used to indicate shutdown readiness. + ShutdownManagerReadyPath = "/shutdown/ready" + // ShutdownReadyFile is the file used to indicate shutdown readiness. + ShutdownReadyFile = "/tmp/shutdown-ready" +) + +// ShutdownManager serves shutdown manager process for Envoy proxies. +func ShutdownManager(readyTimeout time.Duration) error { + // Setup HTTP handler + handler := http.NewServeMux() + handler.HandleFunc(ShutdownManagerHealthCheckPath, func(_ http.ResponseWriter, _ *http.Request) {}) + handler.HandleFunc(ShutdownManagerReadyPath, func(w http.ResponseWriter, _ *http.Request) { + shutdownReadyHandler(w, readyTimeout, ShutdownReadyFile) + }) + + // Setup HTTP server + srv := http.Server{ + Handler: handler, + Addr: fmt.Sprintf(":%d", ShutdownManagerPort), + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 15 * time.Second, + } + + // Setup signal handling + c := make(chan struct{}) + go func() { + s := make(chan os.Signal, 1) + signal.Notify(s, os.Interrupt, syscall.SIGTERM) + + r := <-s + loggers.LoggerAPKOperator.Info(fmt.Sprintf("received %s", unix.SignalName(r.(syscall.Signal)))) + + // Shutdown HTTP server without interrupting active connections + if err := srv.Shutdown(context.Background()); err != nil { + loggers.LoggerAPKOperator.Error(err, "server shutdown error") + } + close(c) + }() + + // Start HTTP server + loggers.LoggerAPKOperator.Info("starting shutdown manager") + if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { + loggers.LoggerAPKOperator.Error(err, "starting shutdown manager failed") + } + + // Wait until done + <-c + return nil +} + +// shutdownReadyHandler handles the endpoint used by a preStop hook on the Envoy +// container to block until ready to terminate. After the graceful drain process +// has completed a file will be written to indicate shutdown readiness. +func shutdownReadyHandler(w http.ResponseWriter, readyTimeout time.Duration, readyFile string) { + var startTime = time.Now() + + loggers.LoggerAPKOperator.Info("received shutdown ready request") + + // Poll for shutdown readiness + for { + // Check if ready timeout is exceeded + elapsedTime := time.Since(startTime) + if elapsedTime > readyTimeout { + loggers.LoggerAPKOperator.Info("shutdown readiness timeout exceeded") + w.WriteHeader(http.StatusInternalServerError) + return + } + + _, err := os.Stat(readyFile) + switch { + case os.IsNotExist(err): + time.Sleep(1 * time.Second) + case err != nil: + loggers.LoggerAPKOperator.Error(err, "error checking for shutdown readiness") + case err == nil: + loggers.LoggerAPKOperator.Info("shutdown readiness detected") + return + } + } +} + +// Shutdown is called from a preStop hook on the shutdown-manager container where +// it will initiate a graceful drain sequence on the Envoy proxy and block until +// connections are drained or a timeout is exceeded. +func Shutdown(drainTimeout time.Duration, minDrainDuration time.Duration, exitAtConnections int) error { + var startTime = time.Now() + var allowedToExit = false + + loggers.LoggerAPKOperator.Info(fmt.Sprintf("initiating graceful drain with %.0f second minimum drain period and %.0f second timeout", + minDrainDuration.Seconds(), drainTimeout.Seconds())) + + // Start failing active health checks + if err := postEnvoyAdminAPI("healthcheck/fail"); err != nil { + loggers.LoggerAPKOperator.Error(err, "error failing active health checks") + } + + // Initiate graceful drain sequence + if err := postEnvoyAdminAPI("drain_listeners?graceful&skip_exit"); err != nil { + loggers.LoggerAPKOperator.Error(err, "error initiating graceful drain") + } + + // Poll total connections from Envoy admin API until minimum drain period has + // been reached and total connections reaches threshold or timeout is exceeded + for { + elapsedTime := time.Since(startTime) + + conn, err := getTotalConnections() + if err != nil { + loggers.LoggerAPKOperator.Error(err, "error getting total connections") + } + + if elapsedTime > minDrainDuration && !allowedToExit { + loggers.LoggerAPKOperator.Info(fmt.Sprintf("minimum drain period reached; will exit when total connections reaches %d", exitAtConnections)) + allowedToExit = true + } + + if elapsedTime > drainTimeout { + loggers.LoggerAPKOperator.Info("graceful drain sequence timeout exceeded") + break + } else if allowedToExit && conn != nil && *conn <= exitAtConnections { + loggers.LoggerAPKOperator.Info("graceful drain sequence completed") + break + } + + time.Sleep(1 * time.Second) + } + + // Signal to shutdownReadyHandler that drain process is complete + if _, err := os.Create(ShutdownReadyFile); err != nil { + loggers.LoggerAPKOperator.Error(err, "error creating shutdown ready file") + return err + } + + return nil +} + +// postEnvoyAdminAPI sends a POST request to the Envoy admin API +func postEnvoyAdminAPI(path string) error { + resp, err := http.Post(fmt.Sprintf("http://%s:%d/%s", + bootstrap.EnvoyAdminAddress, bootstrap.EnvoyAdminPort, path), "application/json", nil) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected response status: %s", resp.Status) + } + return nil + +} + +// getTotalConnections retrieves the total number of open connections from Envoy's server.total_connections stat +func getTotalConnections() (*int, error) { + // Send request to Envoy admin API to retrieve server.total_connections stat + resp, err := http.Get(fmt.Sprintf("http://%s:%d//stats?filter=^server\\.total_connections$&format=json", + bootstrap.EnvoyAdminAddress, bootstrap.EnvoyAdminPort)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + // Define struct to decode JSON response into; expecting a single stat in the response in the format: + // {"stats":[{"name":"server.total_connections","value":123}]} + var r *struct { + Stats []struct { + Name string + Value int + } + } + + // Decode JSON response into struct + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, err + } + + // Defensive check for empty stats + if len(r.Stats) == 0 { + return nil, fmt.Errorf("no stats found") + } + + // Log and return total connections + c := r.Stats[0].Value + loggers.LoggerAPKOperator.Info(fmt.Sprintf("total connections: %d", c)) + return &c, nil + +} diff --git a/adapter/internal/operator/gateway-api/filters.go b/adapter/internal/operator/gateway-api/filters.go new file mode 100644 index 0000000000..305574d7eb --- /dev/null +++ b/adapter/internal/operator/gateway-api/filters.go @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "fmt" + "strings" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +type FiltersTranslator interface { + HTTPFiltersTranslator +} + +var _ FiltersTranslator = (*Translator)(nil) + +type HTTPFiltersTranslator interface { + processURLRewriteFilter(rewrite *gwapiv1.HTTPURLRewriteFilter, filterContext *HTTPFiltersContext) + processRedirectFilter(redirect *gwapiv1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) + processRequestHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) + processResponseHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) + processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *Resources) + processExtensionRefHTTPFilter(extRef *gwapiv1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) + processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) +} + +// HTTPFiltersContext is the context of http filters processing. +type HTTPFiltersContext struct { + *HTTPFilterIR + + ParentRef *RouteParentContext + Route RouteContext + RuleIdx int +} + +// HTTPFilterIR contains the ir processing results. +type HTTPFilterIR struct { + DirectResponse *ir.DirectResponse + RedirectResponse *ir.Redirect + + URLRewrite *ir.URLRewrite + + AddRequestHeaders []ir.AddHeader + RemoveRequestHeaders []string + + AddResponseHeaders []ir.AddHeader + RemoveResponseHeaders []string + + Mirrors []*ir.RouteDestination + + ExtensionRefs []*ir.UnstructuredRef +} + +// ProcessHTTPFilters translates gateway api http filters to IRs. +func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, + route RouteContext, + filters []gwapiv1.HTTPRouteFilter, + ruleIdx int, + resources *Resources) *HTTPFiltersContext { + httpFiltersContext := &HTTPFiltersContext{ + ParentRef: parentRef, + Route: route, + RuleIdx: ruleIdx, + HTTPFilterIR: &HTTPFilterIR{}, + } + for i := range filters { + filter := filters[i] + // If an invalid filter type has been configured then skip processing any more filters + if httpFiltersContext.DirectResponse != nil { + break + } + if err := ValidateHTTPRouteFilter(&filter); err != nil { + t.processInvalidHTTPFilter(string(filter.Type), httpFiltersContext, err) + break + } + + switch filter.Type { + case gwapiv1.HTTPRouteFilterURLRewrite: + t.processURLRewriteFilter(filter.URLRewrite, httpFiltersContext) + case gwapiv1.HTTPRouteFilterRequestRedirect: + t.processRedirectFilter(filter.RequestRedirect, httpFiltersContext) + case gwapiv1.HTTPRouteFilterRequestHeaderModifier: + t.processRequestHeaderModifierFilter(filter.RequestHeaderModifier, httpFiltersContext) + case gwapiv1.HTTPRouteFilterResponseHeaderModifier: + t.processResponseHeaderModifierFilter(filter.ResponseHeaderModifier, httpFiltersContext) + case gwapiv1.HTTPRouteFilterRequestMirror: + t.processRequestMirrorFilter(i, filter.RequestMirror, httpFiltersContext, resources) + case gwapiv1.HTTPRouteFilterExtensionRef: + t.processExtensionRefHTTPFilter(filter.ExtensionRef, httpFiltersContext, resources) + default: + t.processUnsupportedHTTPFilter(string(filter.Type), httpFiltersContext) + } + } + + return httpFiltersContext +} + +// ProcessGRPCFilters translates gateway api grpc filters to IRs. +func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext, + route RouteContext, + filters []v1alpha2.GRPCRouteFilter, + resources *Resources) *HTTPFiltersContext { + httpFiltersContext := &HTTPFiltersContext{ + ParentRef: parentRef, + Route: route, + + HTTPFilterIR: &HTTPFilterIR{}, + } + for i := range filters { + filter := filters[i] + // If an invalid filter type has been configured then skip processing any more filters + if httpFiltersContext.DirectResponse != nil { + break + } + if err := ValidateGRPCRouteFilter(&filter); err != nil { + t.processInvalidHTTPFilter(string(filter.Type), httpFiltersContext, err) + break + } + + switch filter.Type { + case v1alpha2.GRPCRouteFilterRequestHeaderModifier: + t.processRequestHeaderModifierFilter(filter.RequestHeaderModifier, httpFiltersContext) + case v1alpha2.GRPCRouteFilterResponseHeaderModifier: + t.processResponseHeaderModifierFilter(filter.ResponseHeaderModifier, httpFiltersContext) + case v1alpha2.GRPCRouteFilterRequestMirror: + t.processRequestMirrorFilter(i, filter.RequestMirror, httpFiltersContext, resources) + case v1alpha2.GRPCRouteFilterExtensionRef: + t.processExtensionRefHTTPFilter(filter.ExtensionRef, httpFiltersContext, resources) + default: + t.processUnsupportedHTTPFilter(string(filter.Type), httpFiltersContext) + } + } + + return httpFiltersContext +} + +func (t *Translator) processURLRewriteFilter( + rewrite *gwapiv1.HTTPURLRewriteFilter, + filterContext *HTTPFiltersContext) { + if filterContext.URLRewrite != nil { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "Cannot configure multiple urlRewrite filters for a single HTTPRouteRule", + ) + return + } + + if rewrite == nil { + return + } + + newURLRewrite := &ir.URLRewrite{} + + if rewrite.Hostname != nil { + if err := t.validateHostname(string(*rewrite.Hostname)); err != nil { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + err.Error(), + ) + return + } + redirectHost := string(*rewrite.Hostname) + newURLRewrite.Hostname = &redirectHost + } + + if rewrite.Path != nil { + switch rewrite.Path.Type { + case gwapiv1.FullPathHTTPPathModifier: + if rewrite.Path.ReplacePrefixMatch != nil { + errMsg := "ReplacePrefixMatch cannot be set when rewrite path type is \"ReplaceFullPath\"" + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + if rewrite.Path.ReplaceFullPath == nil { + errMsg := "ReplaceFullPath must be set when rewrite path type is \"ReplaceFullPath\"" + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + if rewrite.Path.ReplaceFullPath != nil { + newURLRewrite.Path = &ir.HTTPPathModifier{ + FullReplace: rewrite.Path.ReplaceFullPath, + } + } + case gwapiv1.PrefixMatchHTTPPathModifier: + if rewrite.Path.ReplaceFullPath != nil { + errMsg := "ReplaceFullPath cannot be set when rewrite path type is \"ReplacePrefixMatch\"" + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + if rewrite.Path.ReplacePrefixMatch == nil { + errMsg := "ReplacePrefixMatch must be set when rewrite path type is \"ReplacePrefixMatch\"" + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + if rewrite.Path.ReplacePrefixMatch != nil { + newURLRewrite.Path = &ir.HTTPPathModifier{ + PrefixMatchReplace: rewrite.Path.ReplacePrefixMatch, + } + } + default: + errMsg := fmt.Sprintf("Rewrite path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", rewrite.Path.Type) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + } + + filterContext.URLRewrite = newURLRewrite +} + +func (t *Translator) processRedirectFilter( + redirect *gwapiv1.HTTPRequestRedirectFilter, + filterContext *HTTPFiltersContext) { + // Can't have two redirects for the same route + if filterContext.RedirectResponse != nil { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "Cannot configure multiple requestRedirect filters for a single HTTPRouteRule", + ) + return + } + + if redirect == nil { + return + } + + redir := &ir.Redirect{} + if redirect.Scheme != nil { + // Note that gateway API may support additional schemes in the future, but unknown values + // must result in an UnsupportedValue status + if *redirect.Scheme == "http" || *redirect.Scheme == "https" { + redir.Scheme = redirect.Scheme + } else { + errMsg := fmt.Sprintf("Scheme: %s is unsupported, only 'https' and 'http' are supported", *redirect.Scheme) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + } + + if redirect.Hostname != nil { + if err := t.validateHostname(string(*redirect.Hostname)); err != nil { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + err.Error(), + ) + } else { + redirectHost := string(*redirect.Hostname) + redir.Hostname = &redirectHost + } + } + + if redirect.Path != nil { + switch redirect.Path.Type { + case gwapiv1.FullPathHTTPPathModifier: + if redirect.Path.ReplaceFullPath != nil { + redir.Path = &ir.HTTPPathModifier{ + FullReplace: redirect.Path.ReplaceFullPath, + } + } + case gwapiv1.PrefixMatchHTTPPathModifier: + if redirect.Path.ReplacePrefixMatch != nil { + redir.Path = &ir.HTTPPathModifier{ + PrefixMatchReplace: redirect.Path.ReplacePrefixMatch, + } + } + default: + errMsg := fmt.Sprintf("Redirect path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", redirect.Path.Type) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + } + + if redirect.StatusCode != nil { + redirectCode := int32(*redirect.StatusCode) + // Envoy supports 302, 303, 307, and 308, but gateway API only includes 301 and 302 + if redirectCode == 301 || redirectCode == 302 { + redir.StatusCode = &redirectCode + } else { + errMsg := fmt.Sprintf("Status code %d is invalid, only 302 and 301 are supported", redirectCode) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + return + } + } + + if redirect.Port != nil { + redirectPort := uint32(*redirect.Port) + redir.Port = &redirectPort + } + + filterContext.RedirectResponse = redir +} + +func (t *Translator) processRequestHeaderModifierFilter( + headerModifier *gwapiv1.HTTPHeaderFilter, + filterContext *HTTPFiltersContext) { + // Make sure the header modifier config actually exists + if headerModifier == nil { + return + } + emptyFilterConfig := true // keep track of whether the provided config is empty or not + + // Add request headers + if headersToAdd := headerModifier.Add; headersToAdd != nil { + if len(headersToAdd) > 0 { + emptyFilterConfig = false + } + for _, addHeader := range headersToAdd { + emptyFilterConfig = false + if addHeader.Name == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "RequestHeaderModifier Filter cannot add a header with an empty name", + ) + // try to process the rest of the headers and produce a valid config. + continue + } + // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names + if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)), + ) + continue + } + // Check if the header is a duplicate + headerKey := string(addHeader.Name) + canAddHeader := true + for _, h := range filterContext.AddRequestHeaders { + if strings.EqualFold(h.Name, headerKey) { + canAddHeader = false + break + } + } + + if !canAddHeader { + continue + } + + newHeader := ir.AddHeader{ + Name: headerKey, + Append: true, + Value: addHeader.Value, + } + + filterContext.AddRequestHeaders = append(filterContext.AddRequestHeaders, newHeader) + } + } + + // Set headers + if headersToSet := headerModifier.Set; headersToSet != nil { + if len(headersToSet) > 0 { + emptyFilterConfig = false + } + for _, setHeader := range headersToSet { + + if setHeader.Name == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "RequestHeaderModifier Filter cannot set a header with an empty name", + ) + continue + } + // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names + if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)), + ) + continue + } + + // Check if the header to be set has already been configured + headerKey := string(setHeader.Name) + canAddHeader := true + for _, h := range filterContext.AddRequestHeaders { + if strings.EqualFold(h.Name, headerKey) { + canAddHeader = false + break + } + } + if !canAddHeader { + continue + } + newHeader := ir.AddHeader{ + Name: string(setHeader.Name), + Append: false, + Value: setHeader.Value, + } + + filterContext.AddRequestHeaders = append(filterContext.AddRequestHeaders, newHeader) + } + } + + // Remove request headers + // As far as Envoy is concerned, it is ok to configure a header to be added/set and also in the list of + // headers to remove. It will remove the original header if present and then add/set the header after. + if headersToRemove := headerModifier.Remove; headersToRemove != nil { + if len(headersToRemove) > 0 { + emptyFilterConfig = false + } + for _, removedHeader := range headersToRemove { + if removedHeader == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "RequestHeaderModifier Filter cannot remove a header with an empty name", + ) + continue + } + + canRemHeader := true + for _, h := range filterContext.RemoveRequestHeaders { + if strings.EqualFold(h, removedHeader) { + canRemHeader = false + break + } + } + if !canRemHeader { + continue + } + + filterContext.RemoveRequestHeaders = append(filterContext.RemoveRequestHeaders, removedHeader) + } + } + + // Update the status if the filter failed to configure any valid headers to add/remove + if len(filterContext.AddRequestHeaders) == 0 && len(filterContext.RemoveRequestHeaders) == 0 && !emptyFilterConfig { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers", + ) + } +} + +func (t *Translator) processResponseHeaderModifierFilter( + headerModifier *gwapiv1.HTTPHeaderFilter, + filterContext *HTTPFiltersContext) { + // Make sure the header modifier config actually exists + if headerModifier == nil { + return + } + emptyFilterConfig := true // keep track of whether the provided config is empty or not + + // Add response headers + if headersToAdd := headerModifier.Add; headersToAdd != nil { + if len(headersToAdd) > 0 { + emptyFilterConfig = false + } + for _, addHeader := range headersToAdd { + emptyFilterConfig = false + if addHeader.Name == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "ResponseHeaderModifier Filter cannot add a header with an empty name", + ) + // try to process the rest of the headers and produce a valid config. + continue + } + // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names + if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)), + ) + continue + } + // Check if the header is a duplicate + headerKey := string(addHeader.Name) + canAddHeader := true + for _, h := range filterContext.AddResponseHeaders { + if strings.EqualFold(h.Name, headerKey) { + canAddHeader = false + break + } + } + + if !canAddHeader { + continue + } + + newHeader := ir.AddHeader{ + Name: headerKey, + Append: true, + Value: addHeader.Value, + } + + filterContext.AddResponseHeaders = append(filterContext.AddResponseHeaders, newHeader) + } + } + + // Set headers + if headersToSet := headerModifier.Set; headersToSet != nil { + if len(headersToSet) > 0 { + emptyFilterConfig = false + } + for _, setHeader := range headersToSet { + + if setHeader.Name == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "ResponseHeaderModifier Filter cannot set a header with an empty name", + ) + continue + } + // Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names + if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)), + ) + continue + } + + // Check if the header to be set has already been configured + headerKey := string(setHeader.Name) + canAddHeader := true + for _, h := range filterContext.AddResponseHeaders { + if strings.EqualFold(h.Name, headerKey) { + canAddHeader = false + break + } + } + if !canAddHeader { + continue + } + newHeader := ir.AddHeader{ + Name: string(setHeader.Name), + Append: false, + Value: setHeader.Value, + } + + filterContext.AddResponseHeaders = append(filterContext.AddResponseHeaders, newHeader) + } + } + + // Remove response headers + // As far as Envoy is concerned, it is ok to configure a header to be added/set and also in the list of + // headers to remove. It will remove the original header if present and then add/set the header after. + if headersToRemove := headerModifier.Remove; headersToRemove != nil { + if len(headersToRemove) > 0 { + emptyFilterConfig = false + } + for _, removedHeader := range headersToRemove { + if removedHeader == "" { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "ResponseHeaderModifier Filter cannot remove a header with an empty name", + ) + continue + } + + canRemHeader := true + for _, h := range filterContext.RemoveResponseHeaders { + if strings.EqualFold(h, removedHeader) { + canRemHeader = false + break + } + } + if !canRemHeader { + continue + } + + filterContext.RemoveResponseHeaders = append(filterContext.RemoveResponseHeaders, removedHeader) + + } + } + + // Update the status if the filter failed to configure any valid headers to add/remove + if len(filterContext.AddResponseHeaders) == 0 && len(filterContext.RemoveResponseHeaders) == 0 && !emptyFilterConfig { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "ResponseHeaderModifier Filter did not provide valid configuration to add/set/remove any headers", + ) + } +} + +func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjectReference, filterContext *HTTPFiltersContext, resources *Resources) { + // Make sure the config actually exists. + if extFilter == nil { + return + } + + filterNs := filterContext.Route.GetNamespace() + // This list of resources will be empty unless an extension is loaded (and introduces resources) + for _, res := range resources.ExtensionRefFilters { + if res.GetKind() == string(extFilter.Kind) && res.GetName() == string(extFilter.Name) && res.GetNamespace() == filterNs { + apiVers := res.GetAPIVersion() + + // To get only the group we cut off the version. + // This could be a one liner but just to be safe we check that the APIVersion is properly formatted + idx := strings.IndexByte(apiVers, '/') + if idx == -1 { + errMsg := fmt.Sprintf("Unable to translate APIVersion for Extension Filter: kind: %s, %s/%s", res.GetKind(), filterNs, extFilter.Name) + t.processUnresolvedHTTPFilter(errMsg, filterContext) + return + } + group := apiVers[:idx] + if group == string(extFilter.Group) { + resource := res // Capture loop variable + filterContext.ExtensionRefs = append(filterContext.ExtensionRefs, &ir.UnstructuredRef{ + Object: &resource, + }) + return + } + } + } + + // Matching filter not found, so set negative status condition. + errMsg := fmt.Sprintf("Reference %s/%s not found for filter type: %v", filterNs, + extFilter.Name, extFilter.Kind) + t.processUnresolvedHTTPFilter(errMsg, filterContext) +} + +func (t *Translator) processRequestMirrorFilter( + filterIdx int, + mirrorFilter *gwapiv1.HTTPRequestMirrorFilter, + filterContext *HTTPFiltersContext, + resources *Resources) { + + // Make sure the config actually exists + if mirrorFilter == nil { + return + } + + mirrorBackend := mirrorFilter.BackendRef + + // Wrap the filter's BackendObjectReference into a BackendRef so we can use existing tooling to check it + weight := int32(1) + mirrorBackendRef := gwapiv1.HTTPBackendRef{ + BackendRef: gwapiv1.BackendRef{ + BackendObjectReference: mirrorBackend, + Weight: &weight, + }, + } + + // This sets the status on the HTTPRoute, should the usage be changed so that the status message reflects that the backendRef is from the filter? + filterNs := filterContext.Route.GetNamespace() + serviceNamespace := NamespaceDerefOr(mirrorBackend.Namespace, filterNs) + if !t.validateBackendRef(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, + resources, serviceNamespace, KindHTTPRoute) { + return + } + + ds, _ := t.processDestination(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources) + + newMirror := &ir.RouteDestination{ + Name: fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(filterContext.Route, filterContext.RuleIdx), filterIdx), + Settings: []*ir.DestinationSetting{ds}, + } + filterContext.Mirrors = append(filterContext.Mirrors, newMirror) +} + +func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *HTTPFiltersContext) { + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonBackendNotFound, + errMsg, + ) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + filterContext.DirectResponse = &ir.DirectResponse{ + Body: &errMsg, + StatusCode: 500, + } +} + +func (t *Translator) processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) { + errMsg := fmt.Sprintf("Unsupported filter type: %s", filterType) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + filterContext.DirectResponse = &ir.DirectResponse{ + Body: &errMsg, + StatusCode: 500, + } +} + +func (t *Translator) processInvalidHTTPFilter(filterType string, filterContext *HTTPFiltersContext, err error) { + errMsg := fmt.Sprintf("Invalid filter %s: %v", filterType, err) + filterContext.ParentRef.SetCondition(filterContext.Route, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + errMsg, + ) + filterContext.DirectResponse = &ir.DirectResponse{ + Body: &errMsg, + StatusCode: 500, + } +} diff --git a/adapter/internal/operator/gateway-api/helpers.go b/adapter/internal/operator/gateway-api/helpers.go new file mode 100644 index 0000000000..279d8bc43f --- /dev/null +++ b/adapter/internal/operator/gateway-api/helpers.go @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "errors" + "fmt" + "strings" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/utils" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + TCPProtocol = "TCP" + UDPProtocol = "UDP" + L4Protocol = "L4" + L7Protocol = "L7" +) + +type protocolPort struct { + protocol gwapiv1.ProtocolType + port int32 +} + +func GroupPtr(name string) *gwapiv1.Group { + group := gwapiv1.Group(name) + return &group +} + +func KindPtr(name string) *gwapiv1.Kind { + kind := gwapiv1.Kind(name) + return &kind +} + +func NamespacePtr(name string) *gwapiv1.Namespace { + namespace := gwapiv1.Namespace(name) + return &namespace +} + +func FromNamespacesPtr(fromNamespaces gwapiv1.FromNamespaces) *gwapiv1.FromNamespaces { + return &fromNamespaces +} + +func SectionNamePtr(name string) *gwapiv1.SectionName { + sectionName := gwapiv1.SectionName(name) + return §ionName +} + +func PortNumPtr(val int32) *gwapiv1.PortNumber { + portNum := gwapiv1.PortNumber(val) + return &portNum +} + +func ObjectNamePtr(val string) *v1alpha2.ObjectName { + objectName := v1alpha2.ObjectName(val) + return &objectName +} + +var ( + PathMatchTypeDerefOr = ptr.Deref[gwapiv1.PathMatchType] + GRPCMethodMatchTypeDerefOr = ptr.Deref[v1alpha2.GRPCMethodMatchType] + HeaderMatchTypeDerefOr = ptr.Deref[gwapiv1.HeaderMatchType] + QueryParamMatchTypeDerefOr = ptr.Deref[gwapiv1.QueryParamMatchType] +) + +func NamespaceDerefOr(namespace *gwapiv1.Namespace, defaultNamespace string) string { + if namespace != nil && *namespace != "" { + return string(*namespace) + } + return defaultNamespace +} + +func GroupDerefOr(group *gwapiv1.Group, defaultGroup string) string { + if group != nil && *group != "" { + return string(*group) + } + return defaultGroup +} + +func KindDerefOr(kind *gwapiv1.Kind, defaultKind string) string { + if kind != nil && *kind != "" { + return string(*kind) + } + return defaultKind +} + +// IsRefToGateway returns whether the provided parent ref is a reference +// to a Gateway with the given namespace/name, irrespective of whether a +// section/listener name has been specified (i.e. a parent ref to a listener +// on the specified gateway will return "true"). +func IsRefToGateway(parentRef gwapiv1.ParentReference, gateway types.NamespacedName) bool { + if parentRef.Group != nil && string(*parentRef.Group) != gwapiv1.GroupName { + return false + } + + if parentRef.Kind != nil && string(*parentRef.Kind) != KindGateway { + return false + } + + if parentRef.Namespace != nil && string(*parentRef.Namespace) != gateway.Namespace { + return false + } + + return string(parentRef.Name) == gateway.Name +} + +// GetReferencedListeners returns whether a given parent ref references a Gateway +// in the given list, and if so, a list of the Listeners within that Gateway that +// are included by the parent ref (either one specific Listener, or all Listeners +// in the Gateway, depending on whether section name is specified or not). +func GetReferencedListeners(parentRef gwapiv1.ParentReference, gateways []*GatewayContext) (bool, []*ListenerContext) { + var selectsGateway bool + var referencedListeners []*ListenerContext + + for _, gateway := range gateways { + if !IsRefToGateway(parentRef, utils.NamespacedName(gateway)) { + continue + } + + selectsGateway = true + + // The parentRef may be to the entire Gateway, or to a specific listener. + for _, listener := range gateway.listeners { + if (parentRef.SectionName == nil || *parentRef.SectionName == listener.Name) && (parentRef.Port == nil || *parentRef.Port == listener.Port) { + referencedListeners = append(referencedListeners, listener) + } + } + } + + return selectsGateway, referencedListeners +} + +// HasReadyListener returns true if at least one Listener in the +// provided list has a condition of "Ready: true", and false otherwise. +func HasReadyListener(listeners []*ListenerContext) bool { + for _, listener := range listeners { + if listener.IsReady() { + return true + } + } + return false +} + +// ValidateHTTPRouteFilter validates the provided filter within HTTPRoute. +func ValidateHTTPRouteFilter(filter *gwapiv1.HTTPRouteFilter) error { + switch { + case filter == nil: + return errors.New("filter is nil") + case filter.Type == gwapiv1.HTTPRouteFilterRequestMirror || + filter.Type == gwapiv1.HTTPRouteFilterURLRewrite || + filter.Type == gwapiv1.HTTPRouteFilterRequestRedirect || + filter.Type == gwapiv1.HTTPRouteFilterRequestHeaderModifier || + filter.Type == gwapiv1.HTTPRouteFilterResponseHeaderModifier: + return nil + case filter.Type == gwapiv1.HTTPRouteFilterExtensionRef: + switch { + case filter.ExtensionRef == nil: + return errors.New("extensionRef field must be specified for an extended filter") + } + default: + return fmt.Errorf("unsupported filter type %v", filter.Type) + } + return nil +} + +// ValidateGRPCRouteFilter validates the provided filter within GRPCRoute. +func ValidateGRPCRouteFilter(filter *v1alpha2.GRPCRouteFilter, extGKs ...schema.GroupKind) error { + switch { + case filter == nil: + return errors.New("filter is nil") + case filter.Type == v1alpha2.GRPCRouteFilterRequestMirror || + filter.Type == v1alpha2.GRPCRouteFilterRequestHeaderModifier || + filter.Type == v1alpha2.GRPCRouteFilterResponseHeaderModifier: + return nil + case filter.Type == v1alpha2.GRPCRouteFilterExtensionRef: + switch { + case filter.ExtensionRef == nil: + return errors.New("extensionRef field must be specified for an extended filter") + default: + for _, gk := range extGKs { + if filter.ExtensionRef.Group == gwapiv1.Group(gk.Group) && + filter.ExtensionRef.Kind == gwapiv1.Kind(gk.Kind) { + return nil + } + } + return fmt.Errorf("unknown kind %s/%s", string(filter.ExtensionRef.Group), string(filter.ExtensionRef.Kind)) + } + default: + return fmt.Errorf("unsupported filter type %v", filter.Type) + } +} + +// GatewayOwnerLabels returns the Gateway Owner labels using +// the provided namespace and name as the values. +func GatewayOwnerLabels(namespace, name string) map[string]string { + return map[string]string{ + OwningGatewayNamespaceLabel: namespace, + OwningGatewayNameLabel: name, + } +} + +// GatewayClassOwnerLabel returns the GatewayCLass Owner label using +// the provided name as the value. +func GatewayClassOwnerLabel(name string) map[string]string { + return map[string]string{OwningGatewayClassLabel: name} +} + +// servicePortToContainerPort translates a service port into an ephemeral +// container port. +func servicePortToContainerPort(servicePort int32) int32 { + // If the service port is a privileged port (1-1023) + // add a constant to the value converting it into an ephemeral port. + // This allows the container to bind to the port without needing a + // CAP_NET_BIND_SERVICE capability. + if servicePort < minEphemeralPort { + return servicePort + wellKnownPortShift + } + return servicePort +} + +// computeHosts returns a list of the intersecting hostnames between the route +// and the listener. +func computeHosts(routeHostnames []string, listenerHostname *gwapiv1.Hostname) []string { + var listenerHostnameVal string + if listenerHostname != nil { + listenerHostnameVal = string(*listenerHostname) + } + + // No route hostnames specified: use the listener hostname if specified, + // or else match all hostnames. + if len(routeHostnames) == 0 { + if len(listenerHostnameVal) > 0 { + return []string{listenerHostnameVal} + } + + return []string{"*"} + } + + var hostnames []string + + for i := range routeHostnames { + routeHostname := routeHostnames[i] + + // TODO ensure routeHostname is a valid hostname + + switch { + // No listener hostname: use the route hostname. + case len(listenerHostnameVal) == 0: + hostnames = append(hostnames, routeHostname) + + // Listener hostname matches the route hostname: use it. + case listenerHostnameVal == routeHostname: + hostnames = append(hostnames, routeHostname) + + // Listener has a wildcard hostname: check if the route hostname matches. + case strings.HasPrefix(listenerHostnameVal, "*"): + if hostnameMatchesWildcardHostname(routeHostname, listenerHostnameVal) { + hostnames = append(hostnames, routeHostname) + } + + // Route has a wildcard hostname: check if the listener hostname matches. + case strings.HasPrefix(routeHostname, "*"): + if hostnameMatchesWildcardHostname(listenerHostnameVal, routeHostname) { + hostnames = append(hostnames, listenerHostnameVal) + } + + } + } + + return hostnames +} + +// hostnameMatchesWildcardHostname returns true if hostname has the non-wildcard +// portion of wildcardHostname as a suffix, plus at least one DNS label matching the +// wildcard. +func hostnameMatchesWildcardHostname(hostname, wildcardHostname string) bool { + if !strings.HasSuffix(hostname, strings.TrimPrefix(wildcardHostname, "*")) { + return false + } + + wildcardMatch := strings.TrimSuffix(hostname, strings.TrimPrefix(wildcardHostname, "*")) + return len(wildcardMatch) > 0 +} + +func containsPort(ports []*protocolPort, port *protocolPort) bool { + for _, protocolPort := range ports { + curProtocol, curLevel := layer4Protocol(protocolPort) + myProtocol, myLevel := layer4Protocol(port) + if protocolPort.port == port.port && (curProtocol == myProtocol && curLevel == myLevel) { + return true + } + } + return false +} + +// layer4Protocol returns listener L4 protocol and listen protocol level +func layer4Protocol(protocolPort *protocolPort) (string, string) { + switch protocolPort.protocol { + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType: + return TCPProtocol, L7Protocol + case gwapiv1.TCPProtocolType: + return TCPProtocol, L4Protocol + default: + return UDPProtocol, L4Protocol + } +} + +type crossNamespaceFrom struct { + group string + kind string + namespace string +} + +type crossNamespaceTo struct { + group string + kind string + namespace string + name string +} + +func irStringKey(gatewayNs, gatewayName string) string { + if len(gatewayNs) > 5 { + gatewayNs = gatewayNs[:5] + } + return fmt.Sprintf("%s/%s", gatewayNs, gatewayName) +} + +func irHTTPListenerName(listener *ListenerContext) string { + return fmt.Sprintf("%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name) +} + +func irTLSListenerName(listener *ListenerContext, tlsRoute *TLSRouteContext) string { + return fmt.Sprintf("%s/%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name, tlsRoute.Name) +} + +func irTCPListenerName(listener *ListenerContext, tcpRoute *TCPRouteContext) string { + return fmt.Sprintf("%s/%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name, tcpRoute.Name) +} + +func irUDPListenerName(listener *ListenerContext, udpRoute *UDPRouteContext) string { + return fmt.Sprintf("%s/%s/%s/%s", listener.gateway.Namespace, listener.gateway.Name, listener.Name, udpRoute.Name) +} + +func irRoutePrefix(route RouteContext) string { + // add a "/" at the end of the prefix to prevent mismatching routes with the + // same prefix. For example, route prefix "/foo/" should not match a route "/foobar". + return fmt.Sprintf("%s/%s/%s/", strings.ToLower(string(GetRouteType(route))), route.GetNamespace(), route.GetName()) +} + +func irRouteName(route RouteContext, ruleIdx, matchIdx int) string { + return fmt.Sprintf("%srule/%d/match/%d", irRoutePrefix(route), ruleIdx, matchIdx) +} + +func irRouteDestinationName(route RouteContext, ruleIdx int) string { + return fmt.Sprintf("%srule/%d", irRoutePrefix(route), ruleIdx) +} + +func irTLSConfigs(tlsSecrets []*v1.Secret) *ir.TLSConfig { + if len(tlsSecrets) == 0 { + return nil + } + + tlsListenerConfigs := &ir.TLSConfig{ + Certificates: make([]ir.TLSCertificate, len(tlsSecrets)), + } + for i, tlsSecret := range tlsSecrets { + tlsListenerConfigs.Certificates[i] = ir.TLSCertificate{ + Name: irTLSListenerConfigName(tlsSecret), + ServerCertificate: tlsSecret.Data[v1.TLSCertKey], + PrivateKey: tlsSecret.Data[v1.TLSPrivateKeyKey], + } + } + return tlsListenerConfigs +} + +func irTLSListenerConfigName(secret *v1.Secret) string { + return fmt.Sprintf("%s/%s", secret.Namespace, secret.Name) +} + +func protocolSliceToStringSlice(protocols []gwapiv1.ProtocolType) []string { + var protocolStrings []string + for _, protocol := range protocols { + protocolStrings = append(protocolStrings, string(protocol)) + } + return protocolStrings +} + +// func getAncestorRefForPolicy(gatewayNN types.NamespacedName, sectionName *v1alpha2.SectionName) v1alpha2.ParentReference { +// return v1alpha2.ParentReference{ +// Group: GroupPtr(gwapiv1.GroupName), +// Kind: KindPtr(KindGateway), +// Namespace: NamespacePtr(gatewayNN.Namespace), +// Name: gwapiv1.ObjectName(gatewayNN.Name), +// SectionName: sectionName, +// } +// } + +// type policyTargetRouteKey struct { +// Kind string +// Namespace string +// Name string +// } + +// type policyRouteTargetContext struct { +// RouteContext +// attached bool +// } + +// type policyGatewayTargetContext struct { +// *GatewayContext +// attached bool +// } + +// listenersWithSameHTTPPort returns a list of the names of all other HTTP listeners +// that would share the same filter chain as the provided listener when translated +// to XDS +func listenersWithSameHTTPPort(xdsIR *ir.Xds, listener *ir.HTTPListener) []string { + // if TLS is enabled, the listener would have its own filterChain in Envoy, so + // no conflicts are possible + if listener.TLS != nil { + return nil + } + res := []string{} + for _, http := range xdsIR.HTTP { + if http == listener { + continue + } + // Non-TLS listeners can be distinguished by their ports + if http.Port == listener.Port { + res = append(res, http.Name) + } + } + return res +} diff --git a/adapter/internal/operator/gateway-api/helpers_v1alpha2.go b/adapter/internal/operator/gateway-api/helpers_v1alpha2.go new file mode 100644 index 0000000000..c59a7f4f31 --- /dev/null +++ b/adapter/internal/operator/gateway-api/helpers_v1alpha2.go @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1" +) + +// TODO: [gwapiv1a2-gwapiv1] +// This file can be removed once TLSRoute graduates to gwapiv1. + +func GroupPtrV1Alpha2(group string) *gwapiv1a2.Group { + gwGroup := gwapiv1a2.Group(group) + return &gwGroup +} + +func KindPtrV1Alpha2(kind string) *gwapiv1a2.Kind { + gwKind := gwapiv1a2.Kind(kind) + return &gwKind +} + +func NamespacePtrV1Alpha2(namespace string) *gwapiv1a2.Namespace { + gwNamespace := gwapiv1a2.Namespace(namespace) + return &gwNamespace +} + +func SectionNamePtrV1Alpha2(sectionName string) *gwapiv1a2.SectionName { + gwSectionName := gwapiv1a2.SectionName(sectionName) + return &gwSectionName +} + +func PortNumPtrV1Alpha2(port int) *gwapiv1a2.PortNumber { + pn := gwapiv1a2.PortNumber(port) + return &pn +} + +func UpgradeParentReferences(old []gwapiv1a2.ParentReference) []gwapiv1.ParentReference { + newParentReferences := make([]gwapiv1.ParentReference, len(old)) + for i, o := range old { + newParentReferences[i] = UpgradeParentReference(o) + } + return newParentReferences +} + +// UpgradeParentReference converts gwapiv1a2.ParentReference to gwapiv1.ParentReference +func UpgradeParentReference(old gwapiv1a2.ParentReference) gwapiv1.ParentReference { + upgraded := gwapiv1.ParentReference{} + + if old.Group != nil { + upgraded.Group = GroupPtr(string(*old.Group)) + } + + if old.Kind != nil { + upgraded.Kind = KindPtr(string(*old.Kind)) + } + + if old.Namespace != nil { + upgraded.Namespace = NamespacePtr(string(*old.Namespace)) + } + + upgraded.Name = old.Name + + if old.SectionName != nil { + upgraded.SectionName = SectionNamePtr(string(*old.SectionName)) + } + + if old.Port != nil { + upgraded.Port = PortNumPtr(int32(*old.Port)) + } + + return upgraded +} + +func DowngradeParentReference(old gwapiv1.ParentReference) gwapiv1a2.ParentReference { + downgraded := gwapiv1a2.ParentReference{} + + if old.Group != nil { + downgraded.Group = GroupPtrV1Alpha2(string(*old.Group)) + } + + if old.Kind != nil { + downgraded.Kind = KindPtrV1Alpha2(string(*old.Kind)) + } + + if old.Namespace != nil { + downgraded.Namespace = NamespacePtrV1Alpha2(string(*old.Namespace)) + } + + downgraded.Name = old.Name + + if old.SectionName != nil { + downgraded.SectionName = SectionNamePtrV1Alpha2(string(*old.SectionName)) + } + + if old.Port != nil { + downgraded.Port = PortNumPtrV1Alpha2(int(*old.Port)) + } + + return downgraded +} + +func UpgradeRouteParentStatuses(routeParentStatuses []gwapiv1a2.RouteParentStatus) []gwapiv1.RouteParentStatus { + var res []gwapiv1.RouteParentStatus + + for _, rps := range routeParentStatuses { + res = append(res, gwapiv1.RouteParentStatus{ + ParentRef: UpgradeParentReference(rps.ParentRef), + ControllerName: rps.ControllerName, + Conditions: rps.Conditions, + }) + } + + return res +} + +func DowngradeRouteParentStatuses(routeParentStatuses []gwapiv1.RouteParentStatus) []gwapiv1a2.RouteParentStatus { + var res []gwapiv1a2.RouteParentStatus + + for _, rps := range routeParentStatuses { + res = append(res, gwapiv1a2.RouteParentStatus{ + ParentRef: DowngradeParentReference(rps.ParentRef), + ControllerName: rps.ControllerName, + Conditions: rps.Conditions, + }) + } + + return res +} + +// UpgradeBackendRef converts gwapiv1a2.BackendRef to gwapiv1.BackendRef +func UpgradeBackendRef(old gwapiv1a2.BackendRef) gwapiv1.BackendRef { + upgraded := gwapiv1.BackendRef{} + + if old.Group != nil { + upgraded.Group = GroupPtr(string(*old.Group)) + } + + if old.Kind != nil { + upgraded.Kind = KindPtr(string(*old.Kind)) + } + + if old.Namespace != nil { + upgraded.Namespace = NamespacePtr(string(*old.Namespace)) + } + + upgraded.Name = old.Name + + if old.Port != nil { + upgraded.Port = PortNumPtr(int32(*old.Port)) + } + + return upgraded +} + +func DowngradeBackendRef(old gwapiv1.BackendRef) gwapiv1a2.BackendRef { + downgraded := gwapiv1a2.BackendRef{} + + if old.Group != nil { + downgraded.Group = GroupPtrV1Alpha2(string(*old.Group)) + } + + if old.Kind != nil { + downgraded.Kind = KindPtrV1Alpha2(string(*old.Kind)) + } + + if old.Namespace != nil { + downgraded.Namespace = NamespacePtrV1Alpha2(string(*old.Namespace)) + } + + downgraded.Name = old.Name + + if old.Port != nil { + downgraded.Port = PortNumPtrV1Alpha2(int(*old.Port)) + } + + return downgraded +} + +func NamespaceDerefOrAlpha(namespace *gwapiv1a2.Namespace, defaultNamespace string) string { + if namespace != nil && *namespace != "" { + return string(*namespace) + } + return defaultNamespace +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra.go new file mode 100644 index 0000000000..69da438261 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra.go @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/config" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ResourceRender renders Kubernetes infrastructure resources +// based on Infra IR resources. +type ResourceRender interface { + Name() string + ServiceAccount() (*corev1.ServiceAccount, error) + Service() (*corev1.Service, error) + ConfigMap() (*corev1.ConfigMap, error) + Deployment() (*appsv1.Deployment, error) + HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPodAutoscaler, error) +} + +// Infra manages the creation and deletion of Kubernetes infrastructure +// based on Infra IR resources. +type Infra struct { + // Namespace is the Namespace used for managed infra. + Namespace string + + // Client wrap k8s client. + Client *InfraClient +} + +// NewInfra returns a new Infra. +func NewInfra(cli client.Client) *Infra { + conf := config.ReadConfigs() + return &Infra{ + Namespace: conf.Deployment.Gateway.Namespace, + Client: New(cli), + } +} + +// createOrUpdate creates a ServiceAccount/ConfigMap/Deployment/Service in the kube api server based on the +// provided ResourceRender, if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdate(ctx context.Context, r ResourceRender) error { + + if err := i.createOrUpdateServiceAccount(ctx, r); err != nil { + return fmt.Errorf("failed to create or update serviceaccount %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.createOrUpdateDeployment(ctx, r); err != nil { + return fmt.Errorf("failed to create or update deployment %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.createOrUpdateService(ctx, r); err != nil { + return fmt.Errorf("failed to create or update service %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.createOrUpdateHPA(ctx, r); err != nil { + return fmt.Errorf("failed to create or update hpa %s/%s: %w", i.Namespace, r.Name(), err) + } + + return nil +} + +// delete deletes the ServiceAccount/ConfigMap/Deployment/Service in the kube api server, if it exists. +func (i *Infra) delete(ctx context.Context, r ResourceRender) error { + if err := i.deleteServiceAccount(ctx, r); err != nil { + return fmt.Errorf("failed to delete serviceaccount %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.deleteConfigMap(ctx, r); err != nil { + return fmt.Errorf("failed to delete configmap %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.deleteDeployment(ctx, r); err != nil { + return fmt.Errorf("failed to delete deployment %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.deleteService(ctx, r); err != nil { + return fmt.Errorf("failed to delete service %s/%s: %w", i.Namespace, r.Name(), err) + } + + if err := i.deleteHPA(ctx, r); err != nil { + return fmt.Errorf("failed to delete hpa %s/%s: %w", i.Namespace, r.Name(), err) + } + + return nil +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_client.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_client.go new file mode 100644 index 0000000000..afe78efc44 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_client.go @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/internal/loggers" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/util/retry" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type InfraClient struct { + client.Client +} + +func New(cli client.Client) *InfraClient { + return &InfraClient{ + Client: cli, + } +} + +func (cli *InfraClient) CreateOrUpdate(ctx context.Context, key client.ObjectKey, current client.Object, specific client.Object, updateChecker func() bool) error { + return retry.RetryOnConflict(retry.DefaultBackoff, func() error { + if err := cli.Client.Get(ctx, key, current); err != nil { + loggers.LoggerAPI.Debugf("Error while getting resource %+v : %v", key, err) + if kerrors.IsNotFound(err) { + loggers.LoggerAPI.Infof("Creating a new resource %+v", key) + // Create if it does not exist. + if err := cli.Client.Create(ctx, specific); err != nil { + return fmt.Errorf("for Create: %w", err) + } + } + } else { + // Since the client.Object does not have a specific Spec field to compare + // just perform an update for now. + if updateChecker() { + specific.SetUID(current.GetUID()) + if err := cli.Client.Update(ctx, specific); err != nil { + return fmt.Errorf("for Update: %w", err) + } + } + } + + return nil + }) +} + +func (cli *InfraClient) Delete(ctx context.Context, object client.Object) error { + if err := cli.Client.Delete(ctx, object); err != nil { + if kerrors.IsNotFound(err) { + return nil + } + return err + } + + return nil +} + +// GetUID retrieves the uid of one resource. +func (cli *InfraClient) GetUID(ctx context.Context, key client.ObjectKey, current client.Object) (types.UID, error) { + if err := cli.Client.Get(ctx, key, current); err != nil { + return "", err + } + return current.GetUID(), nil +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_resource.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_resource.go new file mode 100644 index 0000000000..ee11d85307 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/infra_resource.go @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource" + "github.com/wso2/apk/adapter/internal/operator/utils" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// createOrUpdateServiceAccount creates a ServiceAccount in the kube api server based on the +// provided ResourceRender, if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdateServiceAccount(ctx context.Context, r ResourceRender) error { + sa, err := r.ServiceAccount() + if err != nil { + return err + } + + current := &corev1.ServiceAccount{} + key := utils.NamespacedName(sa) + + return i.Client.CreateOrUpdate(ctx, key, current, sa, func() bool { + // the service account never changed, does not need to update + // fixes https://github.com/envoyproxy/gateway/issues/1604 + return false + }) +} + +// // createOrUpdateConfigMap creates a ConfigMap in the Kube api server based on the provided +// // ResourceRender, if it doesn't exist and updates it if it does. +// func (i *Infra) createOrUpdateConfigMap(ctx context.Context, r ResourceRender) error { +// cm, err := r.ConfigMap() +// if err != nil { +// return err +// } + +// if cm == nil { +// return nil +// } +// current := &corev1.ConfigMap{} +// key := types.NamespacedName{ +// Namespace: cm.Namespace, +// Name: cm.Name, +// } + +// return i.Client.CreateOrUpdate(ctx, key, current, cm, func() bool { +// return !reflect.DeepEqual(cm.Data, current.Data) +// }) +// } + +// createOrUpdateDeployment creates a Deployment in the kube api server based on the provided +// ResourceRender, if it doesn't exist and updates it if it does. +func (i *Infra) createOrUpdateDeployment(ctx context.Context, r ResourceRender) error { + deployment, err := r.Deployment() + if err != nil { + return err + } + + current := &appsv1.Deployment{} + key := types.NamespacedName{ + Namespace: deployment.Namespace, + Name: deployment.Name, + } + + hpa, err := r.HorizontalPodAutoscaler() + if err != nil { + return err + } + + var opts cmp.Options + if hpa != nil { + opts = append(opts, cmpopts.IgnoreFields(appsv1.DeploymentSpec{}, "Replicas")) + } + + return i.Client.CreateOrUpdate(ctx, key, current, deployment, func() bool { + return !cmp.Equal(current.Spec, deployment.Spec, opts...) + }) +} + +// createOrUpdateHPA creates HorizontalPodAutoscaler object in the kube api server based on +// the provided ResourceRender, if it doesn't exist and updates it if it does, +// and delete hpa if not set. +func (i *Infra) createOrUpdateHPA(ctx context.Context, r ResourceRender) error { + hpa, err := r.HorizontalPodAutoscaler() + if err != nil { + return err + } + + // when HorizontalPodAutoscaler is not set, + // then delete the object in the kube api server if any. + if hpa == nil { + return i.deleteHPA(ctx, r) + } + current := &autoscalingv2.HorizontalPodAutoscaler{} + key := types.NamespacedName{ + Namespace: hpa.Namespace, + Name: hpa.Name, + } + return i.Client.CreateOrUpdate(ctx, key, current, hpa, func() bool { + return !cmp.Equal(hpa.Spec, current.Spec) + }) +} + +// createOrUpdateRateLimitService creates a Service in the kube api server based on the provided ResourceRender, +// if it doesn't exist or updates it if it does. +func (i *Infra) createOrUpdateService(ctx context.Context, r ResourceRender) error { + svc, err := r.Service() + if err != nil { + return err + } + + current := &corev1.Service{} + key := types.NamespacedName{ + Namespace: svc.Namespace, + Name: svc.Name, + } + + return i.Client.CreateOrUpdate(ctx, key, current, svc, func() bool { + return !resource.CompareSvc(svc, current) + }) +} + +// deleteServiceAccount deletes the ServiceAccount in the kube api server, if it exists. +func (i *Infra) deleteServiceAccount(ctx context.Context, r ResourceRender) error { + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, sa) +} + +// deleteDeployment deletes the Envoy Deployment in the kube api server, if it exists. +func (i *Infra) deleteDeployment(ctx context.Context, r ResourceRender) error { + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, deployment) +} + +// deleteConfigMap deletes the ConfigMap in the kube api server, if it exists. +func (i *Infra) deleteConfigMap(ctx context.Context, r ResourceRender) error { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, cm) +} + +// deleteService deletes the Service in the kube api server, if it exists. +func (i *Infra) deleteService(ctx context.Context, r ResourceRender) error { + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, svc) +} + +// deleteHpa deletes the Horizontal Pod Autoscaler associated to its renderer, if it exists. +func (i *Infra) deleteHPA(ctx context.Context, r ResourceRender) error { + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.Namespace, + Name: r.Name(), + }, + } + + return i.Client.Delete(ctx, hpa) +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource.go new file mode 100644 index 0000000000..e87d802f14 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource.go @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package proxy + +import ( + "fmt" + "strings" + + "github.com/wso2/apk/adapter/config" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/bootstrap" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/envoy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/version" + + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + utils "github.com/wso2/apk/adapter/pkg/utils/misc" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" +) + +const ( + SdsCAFilename = "xds-trusted-ca.json" + SdsCertFilename = "xds-certificate.json" + // XdsTLSCertFilename is the fully qualified path of the file containing Envoy's + // xDS server TLS certificate. + XdsTLSCertFilename = "/home/wso2/security/keystore/router.crt" + // XdsTLSCertFilename = "/home/wso2/security/keystore/router.crt" + // XdsTLSKeyFilename is the fully qualified path of the file containing Envoy's + // xDS server TLS key. + XdsTLSKeyFilename = "/home/wso2/security/keystore/router.key" + // XdsTLSKeyFilename = "/home/wso2/security/keystore/router.key" + // XdsTLSCaFilename is the fully qualified path of the file containing Envoy's + // trusted CA certificate. + // XdsTLSCaFilename = "/certs/ca.crt" + XdsTLSCaFilename = "/home/wso2/security/truststore/adapter.crt" + // envoyContainerName is the name of the Envoy container. + envoyContainerName = "envoy" + enforcerContainerName = "enforcer" + // envoyNsEnvVar is the name of the Envoy Gateway namespace environment variable. + envoyNsEnvVar = "ENVOY_GATEWAY_NAMESPACE" + // envoyPodEnvVar is the name of the Envoy pod name environment variable. + envoyPodEnvVar = "ENVOY_POD_NAME" +) + +var ( + // xDS certificate rotation is supported by using SDS path-based resource files. + SdsCAConfigMapData = fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ + `"name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"%s"},`+ + `"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}`, XdsTLSCaFilename) + SdsCertConfigMapData = fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ + `"name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"%s"},`+ + `"private_key":{"filename":"%s"}}}]}`, XdsTLSCertFilename, XdsTLSKeyFilename) +) + +// ExpectedResourceHashedName returns expected resource hashed name including up to the 48 characters of the original name. +func ExpectedResourceHashedName(name string) string { + hashedName := utils.GetHashedName(name, 35) + return hashedName +} + +// EnvoyAppLabel returns the labels used for all Envoy resources. +func EnvoyAppLabel() map[string]string { + return map[string]string{ + "app.kubernetes.io/name": "envoy", + "app.kubernetes.io/component": "proxy", + "app.kubernetes.io/managed-by": "envoy-gateway", + } +} + +// EnvoyAppLabelSelector returns the labels used for all Envoy resources. +func EnvoyAppLabelSelector() []string { + return []string{ + "app.kubernetes.io/name=envoy", + "app.kubernetes.io/component=proxy", + "app.kubernetes.io/managed-by=envoy-gateway", + } +} + +// envoyLabels returns the labels, including extraLabels, used for Envoy resources. +func envoyLabels(extraLabels map[string]string) map[string]string { + labels := EnvoyAppLabel() + for k, v := range extraLabels { + labels[k] = v + } + + return labels +} + +func enablePrometheus(infra *ir.ProxyInfra) bool { + return false +} + +// expectedProxyContainers returns expected proxy containers. +func expectedProxyContainers(infra *ir.ProxyInfra, + deploymentConfig *egv1a1.KubernetesDeploymentSpec, + shutdownConfig *egv1a1.ShutdownConfig) ([]corev1.Container, error) { + // Define slice to hold container ports + var ports []corev1.ContainerPort + + // Iterate over listeners and ports to get container ports + for _, listener := range infra.Listeners { + for _, p := range listener.Ports { + var protocol corev1.Protocol + switch p.Protocol { + case ir.HTTPProtocolType, ir.HTTPSProtocolType, ir.TLSProtocolType, ir.TCPProtocolType: + protocol = corev1.ProtocolTCP + case ir.UDPProtocolType: + protocol = corev1.ProtocolUDP + default: + return nil, fmt.Errorf("invalid protocol %q", p.Protocol) + } + port := corev1.ContainerPort{ + // hashed container port name including up to the 6 characters of the port name and the maximum of 15 characters. + Name: utils.GetHashedName(p.Name, 6), + ContainerPort: p.ContainerPort, + Protocol: protocol, + } + ports = append(ports, port) + } + } + + if enablePrometheus(infra) { + ports = append(ports, corev1.ContainerPort{ + Name: "metrics", + ContainerPort: bootstrap.EnvoyReadinessPort, // TODO: make this configurable + Protocol: corev1.ProtocolTCP, + }) + } + + var bootstrapConfigurations string + + var proxyMetrics *egv1a1.ProxyMetrics + + // Get the default Bootstrap + bootstrapConfigurations, err := bootstrap.GetRenderedBootstrapConfig(proxyMetrics) + if err != nil { + return nil, err + } + + args := []string{ + fmt.Sprintf("--service-cluster %s", infra.Name), + fmt.Sprintf("--service-node $(%s)", envoyPodEnvVar), + fmt.Sprintf("--config-yaml %s", bootstrapConfigurations), + fmt.Sprintf("--log-level %s", "warn"), + "--cpuset-threads", + } + + // if infra.Config != nil && + // infra.Config.Spec.Concurrency != nil { + // args = append(args, fmt.Sprintf("--concurrency %d", *infra.Config.Spec.Concurrency)) + // } + + // if componentsLogLevel := logging.GetEnvoyProxyComponentLevel(); componentsLogLevel != "" { + // args = append(args, fmt.Sprintf("--component-log-level %s", componentsLogLevel)) + // } + + if shutdownConfig != nil && shutdownConfig.DrainTimeout != nil { + args = append(args, fmt.Sprintf("--drain-time-s %.0f", shutdownConfig.DrainTimeout.Seconds())) + } + + // if infra.Config != nil { + // args = append(args, infra.Config.Spec.ExtraArgs...) + // } + + containers := []corev1.Container{ + { + Name: envoyContainerName, + Image: *deploymentConfig.EnvoyProxyContainer.Image, + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{"envoy"}, + Args: args, + Env: expectedContainerEnv(deploymentConfig.EnvoyProxyContainer), + Resources: *deploymentConfig.EnvoyProxyContainer.Resources, + SecurityContext: deploymentConfig.EnvoyProxyContainer.SecurityContext, + Ports: ports, + VolumeMounts: expectedContainerVolumeMounts(deploymentConfig.EnvoyProxyContainer), + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + TerminationMessagePath: "/dev/termination-log", + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: bootstrap.EnvoyReadinessPath, + Port: intstr.IntOrString{Type: intstr.Int, IntVal: bootstrap.EnvoyReadinessPort}, + Scheme: corev1.URISchemeHTTP, + }, + }, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: envoy.ShutdownManagerReadyPath, + Port: intstr.FromInt32(envoy.ShutdownManagerPort), + Scheme: corev1.URISchemeHTTP, + }, + }, + }, + }, + { + Name: "shutdown-manager", + Image: expectedShutdownManagerImage(), + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{"envoy-gateway"}, + Args: expectedShutdownManagerArgs(shutdownConfig), + Env: expectedContainerEnv(nil), + Resources: *egv1a1.DefaultShutdownManagerContainerResourceRequirements(), + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + TerminationMessagePath: "/dev/termination-log", + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: envoy.ShutdownManagerHealthCheckPath, + Port: intstr.IntOrString{Type: intstr.Int, IntVal: envoy.ShutdownManagerPort}, + Scheme: corev1.URISchemeHTTP, + }, + }, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: envoy.ShutdownManagerHealthCheckPath, + Port: intstr.IntOrString{Type: intstr.Int, IntVal: envoy.ShutdownManagerPort}, + Scheme: corev1.URISchemeHTTP, + }, + }, + TimeoutSeconds: 1, + PeriodSeconds: 10, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: expectedShutdownPreStopCommand(shutdownConfig), + }, + }, + }, + }, + { + Name: enforcerContainerName, + Image: *deploymentConfig.EnforcerContainer.Image, + ImagePullPolicy: corev1.PullIfNotPresent, + // Command: []string{"envoy"}, + // Args: args, + Env: expectedEnforcerEnv(deploymentConfig.EnforcerContainer), + Resources: *deploymentConfig.EnforcerContainer.Resources, + SecurityContext: deploymentConfig.EnforcerContainer.SecurityContext, + Ports: expectedEnforcerPorts(), + VolumeMounts: expectedEnforcerVolumeMounts(deploymentConfig.EnforcerContainer), + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + TerminationMessagePath: "/dev/termination-log", + //todo(amali) + // ReadinessProbe: &corev1.Probe{ + // ProbeHandler: corev1.ProbeHandler{ + // Exec: &corev1.ExecAction{ + // Command: []string{"sh", "check_health.sh"}, + // }, + // }, + // TimeoutSeconds: 1, + // PeriodSeconds: 20, + // SuccessThreshold: 1, + // FailureThreshold: 5, + // InitialDelaySeconds: 20, + // }, + // LivenessProbe: &corev1.Probe{ + // ProbeHandler: corev1.ProbeHandler{ + // Exec: &corev1.ExecAction{ + // Command: []string{"sh", "check_health.sh"}, + // }, + // }, + // TimeoutSeconds: 1, + // PeriodSeconds: 20, + // SuccessThreshold: 1, + // FailureThreshold: 5, + // InitialDelaySeconds: 20, + // }, + }, + } + + return containers, nil +} + +func expectedShutdownManagerImage() string { + if v := version.Get().ShutdownManagerVersion; v != "" { + return fmt.Sprintf("%s:%s", strings.Split(egv1a1.DefaultShutdownManagerImage, ":")[0], v) + } + return egv1a1.DefaultShutdownManagerImage +} + +func expectedShutdownManagerArgs(cfg *egv1a1.ShutdownConfig) []string { + args := []string{"envoy", "shutdown-manager"} + if cfg != nil && cfg.DrainTimeout != nil { + args = append(args, fmt.Sprintf("--ready-timeout=%.0fs", cfg.DrainTimeout.Seconds()+10)) + } + return args +} + +func expectedShutdownPreStopCommand(cfg *egv1a1.ShutdownConfig) []string { + command := []string{"envoy-gateway", "envoy", "shutdown"} + + if cfg == nil { + return command + } + + if cfg.DrainTimeout != nil { + command = append(command, fmt.Sprintf("--drain-timeout=%.0fs", cfg.DrainTimeout.Seconds())) + } + + if cfg.MinDrainDuration != nil { + command = append(command, fmt.Sprintf("--min-drain-duration=%.0fs", cfg.MinDrainDuration.Seconds())) + } + + return command +} + +// expectedContainerVolumeMounts returns expected proxy container volume mounts. +func expectedContainerVolumeMounts(containerSpec *egv1a1.KubernetesContainerSpec) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{ + // { + // Name: "certs", + // MountPath: "/certs", + // ReadOnly: true, + // }, + // { + // Name: "sds", + // MountPath: "/sds", + // }, + { + Name: "ratelimiter-truststore-secret-volume", + MountPath: "/home/wso2/security/truststore/ratelimiter-ca.crt", + SubPath: "ca.crt", + }, + { + Name: "ratelimiter-truststore-secret-volume", + MountPath: "/home/wso2/security/truststore/ratelimiter.crt", + SubPath: "tls.crt", + }, + // { + // Name: "log-conf-volume", + // MountPath: "/home/wso2/conf/", + // }, + { + Name: "enforcer-keystore-secret-volume", + MountPath: "/home/wso2/security/truststore/enforcer.crt", + SubPath: "tls.crt", + }, + { + Name: "adapter-truststore-secret-volume", + MountPath: "/home/wso2/security/truststore/adapter.crt", + SubPath: "tls.crt", + }, + { + MountPath: "/home/wso2/security/keystore/router.crt", + Name: "router-keystore-secret-volume", + SubPath: "tls.crt", + }, + { + MountPath: "/home/wso2/security/keystore/router.key", + Name: "router-keystore-secret-volume", + SubPath: "tls.key", + }, + } + + return resource.ExpectedContainerVolumeMounts(containerSpec, volumeMounts) +} + +func expectedEnforcerPorts() []corev1.ContainerPort { + ports := []corev1.ContainerPort{ + { + Name: "ext-auth", + ContainerPort: int32(8081), + Protocol: corev1.ProtocolTCP, + }, + { + // Name: "ExtAuthServer", + ContainerPort: int32(9001), + Protocol: corev1.ProtocolTCP, + }, + { + Name: "debug", + ContainerPort: int32(5006), + Protocol: corev1.ProtocolTCP, + }, + { + Name: "jwks", + ContainerPort: int32(9092), + Protocol: corev1.ProtocolTCP, + }, + { + Name: "cc-xds", + ContainerPort: int32(18002), + Protocol: corev1.ProtocolTCP, + }, + } + + return ports +} + +// expectedContainerVolumeMounts returns expected proxy container volume mounts. +func expectedEnforcerVolumeMounts(containerSpec *egv1a1.KubernetesContainerSpec) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{ + { + Name: "tmp", + MountPath: "/tmp", + ReadOnly: true, + }, + { + Name: "enforcer-keystore-secret-volume", + MountPath: "/home/wso2/security/keystore/enforcer.key", + SubPath: "tls.key", + }, + + {MountPath: "/home/wso2/security/keystore/enforcer.crt", + Name: "enforcer-keystore-secret-volume", + SubPath: "tls.crt", + }, + {MountPath: "/home/wso2/security/truststore/apk.crt", + Name: "enforcer-keystore-secret-volume", + SubPath: "ca.crt", + }, + {MountPath: "/home/wso2/security/truststore/enforcer.crt", + Name: "enforcer-keystore-secret-volume", + SubPath: "tls.crt", + }, + {MountPath: "/home/wso2/security/truststore/adapter.crt", + Name: "adapter-truststore-secret-volume", + SubPath: "tls.crt", + }, + {MountPath: "/home/wso2/security/truststore/router.crt", + Name: "router-keystore-secret-volume", + SubPath: "tls.crt", + }, + // {MountPath: "/home/wso2/conf/", + // Name: "log-conf-volume", + // }, + {MountPath: "/home/wso2/security/keystore/mg.pem", + Name: "enforcer-jwt-secret-volume", + SubPath: "mg.pem", + }, + {MountPath: "/home/wso2/security/truststore/mg.pem", + Name: "enforcer-jwt-secret-volume", + SubPath: "mg.pem", + }, + {MountPath: "/home/wso2/security/keystore/mg.key", + Name: "enforcer-jwt-secret-volume", + SubPath: "mg.key", + }, + {MountPath: "/home/wso2/security/truststore/wso2carbon.pem", + Name: "enforcer-trusted-certs", + SubPath: "wso2carbon.pem", + }, + {MountPath: "/home/wso2/security/truststore/wso2-apim-carbon.pem", + Name: "enforcer-apikey-cert", + SubPath: "wso2-apim-carbon.pem", + }, + {MountPath: "/home/wso2/security/truststore/idp.pem", + Name: "idp-certificate-secret-volume", + SubPath: "wso2carbon.pem", + }, + } + + return resource.ExpectedContainerVolumeMounts(containerSpec, volumeMounts) +} + +// expectedDeploymentVolumes returns expected proxy deployment volumes. +func expectedDeploymentVolumes(deploymentSpec *egv1a1.KubernetesDeploymentSpec) []corev1.Volume { + conf := config.ReadConfigs() + volumes := []corev1.Volume{ + { + Name: "ratelimiter-truststore-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: conf.Deployment.Gateway.Volumes.RatelimiterTruststoreSecretVolume, + DefaultMode: ptr.To[int32](420), + }, + }, + }, + { + Name: "enforcer-keystore-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.EnforcerKeystoreSecretVolume, + }, + }, + }, + // { + // Name: "log-conf-volume", + // VolumeSource: corev1.VolumeSource{ + // ConfigMap: &corev1.ConfigMapVolumeSource{ + // DefaultMode: ptr.To[int32](420), + // LocalObjectReference: corev1.LocalObjectReference{ + // Name: "apk-test-wso2-apk-log-conf", + // }, + // }, + // }, + // }, + { + Name: "router-keystore-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.RouterKeystoreSecretVolume, + }, + }, + }, + { + Name: "adapter-truststore-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.AdapterTruststoreSecretVolume, + }, + }, + }, + { + Name: "enforcer-jwt-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.EnforcerJwtSecretVolume, + }, + }, + }, + { + Name: "enforcer-trusted-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.EnforcerTrustedCerts, + }, + }, + }, + { + Name: "enforcer-apikey-cert", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.EnforcerApikeyCert, + }, + }, + }, + { + Name: "idp-certificate-secret-volume", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + DefaultMode: ptr.To[int32](420), + SecretName: conf.Deployment.Gateway.Volumes.IDPCertificateSecretVolume, + }, + }, + }, + { + Name: "tmp", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + } + + return resource.ExpectedDeploymentVolumes(deploymentSpec.Pod, volumes) +} + +// expectedContainerEnv returns expected proxy container envs. +func expectedContainerEnv(containerSpec *egv1a1.KubernetesContainerSpec) []corev1.EnvVar { + env := []corev1.EnvVar{ + { + Name: envoyNsEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: envoyPodEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.name", + }, + }, + }, + } + + if containerSpec != nil { + return resource.ExpectedContainerEnv(containerSpec, env) + } + return env + +} + +func expectedEnforcerEnv(containerSpec *egv1a1.KubernetesContainerSpec) []corev1.EnvVar { + conf := config.ReadConfigs() + env := []corev1.EnvVar{ + { + Name: "ADAPTER_HOST_NAME", + Value: conf.Deployment.Gateway.AdapterHostName, + }, + { + Name: "ADAPTER_HOST", + Value: conf.Deployment.Gateway.AdapterHost, + }, + { + Name: "COMMON_CONTROLLER_HOST_NAME", + Value: conf.Deployment.Gateway.CommonControllerHostName, + }, + { + Name: "COMMON_CONTROLLER_HOST", + Value: conf.Deployment.Gateway.CommonControllerHost, + }, + { + Name: "ENFORCER_PRIVATE_KEY_PATH", + Value: conf.Deployment.Gateway.EnforcerPrivateKeyPath, + }, + { + Name: "ENFORCER_PUBLIC_CERT_PATH", + Value: conf.Deployment.Gateway.EnforcerPublicCertPath, + }, + { + Name: "ENFORCER_SERVER_NAME", + Value: conf.Deployment.Gateway.EnforcerServerName, + }, + { + Name: "TRUSTED_CA_CERTS_PATH", + Value: conf.Deployment.Gateway.AdapterTrustedCAPath, + }, + { + Name: "ADAPTER_XDS_PORT", + Value: conf.Deployment.Gateway.AdapterXDSPort, + }, + { + Name: "COMMON_CONTROLLER_XDS_PORT", + Value: conf.Deployment.Gateway.CommonControllerXDSPort, + }, + { + Name: "COMMON_CONTROLLER_REST_PORT", + Value: conf.Deployment.Gateway.CommonControllerRestPort, + }, + { + Name: "ENFORCER_LABEL", + Value: conf.Deployment.Gateway.EnforcerLabel, + }, + { + Name: "ENFORCER_REGION", + Value: conf.Deployment.Gateway.EnforcerRegion, + }, + { + Name: "XDS_MAX_MSG_SIZE", + Value: conf.Deployment.Gateway.EnforcerXDSMaxMsgSize, + }, + { + Name: "XDS_MAX_RETRIES", + Value: conf.Deployment.Gateway.EnforcerXDSMaxRetries, + }, + { + Name: "JAVA_OPTS", + Value: conf.Deployment.Gateway.JavaOpts, + }, + } + + if containerSpec != nil { + return resource.ExpectedContainerEnv(containerSpec, env) + } + return env + +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider.go new file mode 100644 index 0000000000..e54df5e0bb --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider.go @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package proxy + +import ( + "fmt" + "strconv" + + "golang.org/x/exp/maps" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/bootstrap" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" +) + +type ResourceRender struct { + infra *ir.ProxyInfra + + // Namespace is the Namespace used for managed infra. + Namespace string +} + +func NewResourceRender(ns string, infra *ir.ProxyInfra) *ResourceRender { + return &ResourceRender{ + Namespace: ns, + infra: infra, + } +} + +func (r *ResourceRender) Name() string { + return ExpectedResourceHashedName(r.infra.Name) +} + +// ServiceAccount returns the expected proxy serviceAccount. +func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) { + // Set the labels based on the owning gateway name. + labels := envoyLabels(r.infra.GetProxyMetadata().Labels) + if OwningGatewayLabelsAbsent(labels) { + return nil, fmt.Errorf("missing owning gateway labels") + } + + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Labels: labels, + Annotations: r.infra.GetProxyMetadata().Annotations, + }, + }, nil +} + +// Service returns the expected Service based on the provided infra. +func (r *ResourceRender) Service() (*corev1.Service, error) { + var ports []corev1.ServicePort + for _, listener := range r.infra.Listeners { + for _, port := range listener.Ports { + target := intstr.IntOrString{IntVal: port.ContainerPort} + protocol := corev1.ProtocolTCP + if port.Protocol == ir.UDPProtocolType { + protocol = corev1.ProtocolUDP + } + + p := corev1.ServicePort{ + Name: ExpectedResourceHashedName(port.Name), + Protocol: protocol, + Port: port.ServicePort, + TargetPort: target, + } + ports = append(ports, p) + + if port.Protocol == ir.HTTPSProtocolType { + if listener.HTTP3 != nil { + p := corev1.ServicePort{ + Name: ExpectedResourceHashedName(port.Name + "-h3"), + Protocol: corev1.ProtocolUDP, + Port: port.ServicePort, + TargetPort: target, + } + ports = append(ports, p) + } + } + } + } + + // Set the labels based on the owning gatewayclass name. + labels := envoyLabels(r.infra.GetProxyMetadata().Labels) + if OwningGatewayLabelsAbsent(labels) { + return nil, fmt.Errorf("missing owning gateway labels") + } + + // Get annotations + annotations := map[string]string{} + maps.Copy(annotations, r.infra.GetProxyMetadata().Annotations) + + provider := r.infra.GetProxyConfig().GetEnvoyProxyProvider() + envoyServiceConfig := provider.GetEnvoyProxyKubeProvider().EnvoyService + if envoyServiceConfig.Annotations != nil { + maps.Copy(annotations, envoyServiceConfig.Annotations) + } + if len(annotations) == 0 { + annotations = nil + } + + // Set the spec of gateway service + serviceSpec := resource.ExpectedServiceSpec(envoyServiceConfig) + serviceSpec.Ports = ports + serviceSpec.Selector = resource.GetSelector(labels).MatchLabels + + if (*envoyServiceConfig.Type) == egv1a1.ServiceTypeClusterIP { + if len(r.infra.Addresses) > 0 { + // Since K8s Service requires specify no more than one IP for each IP family + // So we only use the first address + // if address is not set, the automatically assigned clusterIP is used + serviceSpec.ClusterIP = r.infra.Addresses[0] + serviceSpec.ClusterIPs = r.infra.Addresses[0:1] + } + } else { + serviceSpec.ExternalIPs = r.infra.Addresses + } + + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Labels: labels, + Annotations: annotations, + }, + Spec: serviceSpec, + } + + // apply merge patch to service + var err error + if svc, err = envoyServiceConfig.ApplyMergePatch(svc); err != nil { + return nil, err + } + + return svc, nil +} + +// ConfigMap returns the expected ConfigMap based on the provided infra. +func (r *ResourceRender) ConfigMap() (*corev1.ConfigMap, error) { + // Set the labels based on the owning gateway name. + labels := envoyLabels(r.infra.GetProxyMetadata().Labels) + if OwningGatewayLabelsAbsent(labels) { + return nil, fmt.Errorf("missing owning gateway labels") + } + + return &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Labels: labels, + Annotations: r.infra.GetProxyMetadata().Annotations, + }, + Data: map[string]string{ + SdsCAFilename: SdsCAConfigMapData, + SdsCertFilename: SdsCertConfigMapData, + }, + }, nil +} + +// Deployment returns the expected Deployment based on the provided infra. +func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { + proxyConfig := r.infra.GetProxyConfig() + + // Get the EnvoyProxy config to configure the deployment. + provider := proxyConfig.GetEnvoyProxyProvider() + if provider.Type != egv1a1.ProviderTypeKubernetes { + return nil, fmt.Errorf("invalid provider type %v for Kubernetes infra manager", provider.Type) + } + deploymentConfig := provider.GetEnvoyProxyKubeProvider().EnvoyDeployment + + // Get expected bootstrap configurations rendered ProxyContainers + containers, err := expectedProxyContainers(r.infra, deploymentConfig, proxyConfig.Spec.Shutdown) + if err != nil { + return nil, err + } + + // Set the labels based on the owning gateway name. + dpAnnotations := r.infra.GetProxyMetadata().Annotations + labels := r.infra.GetProxyMetadata().Labels + dpLabels := envoyLabels(labels) + if OwningGatewayLabelsAbsent(dpLabels) { + return nil, fmt.Errorf("missing owning gateway labels") + } + + maps.Copy(labels, deploymentConfig.Pod.Labels) + podLabels := envoyLabels(labels) + selector := resource.GetSelector(podLabels) + + // Get annotations + podAnnotations := map[string]string{} + maps.Copy(podAnnotations, dpAnnotations) + maps.Copy(podAnnotations, deploymentConfig.Pod.Annotations) + if enablePrometheus(r.infra) { + podAnnotations["prometheus.io/path"] = "/stats/prometheus" // TODO: make this configurable + podAnnotations["prometheus.io/scrape"] = "true" + podAnnotations["prometheus.io/port"] = strconv.Itoa(bootstrap.EnvoyReadinessPort) + } + if len(podAnnotations) == 0 { + podAnnotations = nil + } + + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Labels: dpLabels, + Annotations: dpAnnotations, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: deploymentConfig.Replicas, + Strategy: *deploymentConfig.Strategy, + Selector: selector, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: selector.MatchLabels, + Annotations: podAnnotations, + }, + Spec: corev1.PodSpec{ + Containers: containers, + InitContainers: deploymentConfig.InitContainers, + ServiceAccountName: ExpectedResourceHashedName(r.infra.Name), + AutomountServiceAccountToken: ptr.To(false), + TerminationGracePeriodSeconds: expectedTerminationGracePeriodSeconds(proxyConfig.Spec.Shutdown), + DNSPolicy: corev1.DNSClusterFirst, + RestartPolicy: corev1.RestartPolicyAlways, + SchedulerName: "default-scheduler", + SecurityContext: deploymentConfig.Pod.SecurityContext, + Affinity: deploymentConfig.Pod.Affinity, + Tolerations: deploymentConfig.Pod.Tolerations, + Volumes: expectedDeploymentVolumes(deploymentConfig), + ImagePullSecrets: deploymentConfig.Pod.ImagePullSecrets, + NodeSelector: deploymentConfig.Pod.NodeSelector, + TopologySpreadConstraints: deploymentConfig.Pod.TopologySpreadConstraints, + }, + }, + RevisionHistoryLimit: ptr.To[int32](10), + ProgressDeadlineSeconds: ptr.To[int32](600), + }, + } + + // omit the deployment replicas if HPA is being set + if provider.GetEnvoyProxyKubeProvider().EnvoyHpa != nil { + deployment.Spec.Replicas = nil + } + + // apply merge patch to deployment + if deployment, err = deploymentConfig.ApplyMergePatch(deployment); err != nil { + return nil, err + } + + return deployment, nil +} + +func expectedTerminationGracePeriodSeconds(cfg *egv1a1.ShutdownConfig) *int64 { + s := 900 // default + if cfg != nil && cfg.DrainTimeout != nil { + s = int(cfg.DrainTimeout.Seconds() + 300) // 5 minutes longer than drain timeout + } + return ptr.To(int64(s)) +} + +func (r *ResourceRender) HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPodAutoscaler, error) { + provider := r.infra.GetProxyConfig().GetEnvoyProxyProvider() + if provider.Type != egv1a1.ProviderTypeKubernetes { + return nil, fmt.Errorf("invalid provider type %v for Kubernetes infra manager", provider.Type) + } + + hpaConfig := provider.GetEnvoyProxyKubeProvider().EnvoyHpa + if hpaConfig == nil { + return nil, nil + } + + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "autoscaling/v2", + Kind: "HorizontalPodAutoscaler", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.Namespace, + Name: r.Name(), + Annotations: r.infra.GetProxyMetadata().Annotations, + Labels: r.infra.GetProxyMetadata().Labels, + }, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: r.Name(), + }, + MinReplicas: hpaConfig.MinReplicas, + MaxReplicas: ptr.Deref(hpaConfig.MaxReplicas, 1), + Metrics: hpaConfig.Metrics, + Behavior: hpaConfig.Behavior, + }, + } + + return hpa, nil +} + +// OwningGatewayLabelsAbsent Check if labels are missing some OwningGatewayLabels +func OwningGatewayLabelsAbsent(labels map[string]string) bool { + return (len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 || + len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0) && + len(labels[gatewayapi.OwningGatewayClassLabel]) == 0 +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider_test.go new file mode 100644 index 0000000000..7281ec61e6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -0,0 +1,989 @@ +// // Copyright Envoy Gateway Authors +// // SPDX-License-Identifier: Apache-2.0 +// // The full text of the Apache license is available in the LICENSE file at +// // the root of the repo. + +package proxy + +// import ( +// "flag" +// "fmt" +// "os" +// "sort" +// "testing" +// "time" + +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/require" +// appsv1 "k8s.io/api/apps/v1" +// autoscalingv2 "k8s.io/api/autoscaling/v2" +// corev1 "k8s.io/api/core/v1" +// v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +// "k8s.io/apimachinery/pkg/api/resource" +// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// "k8s.io/utils/ptr" +// "sigs.k8s.io/yaml" + +// gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" +// "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" +// egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" +// ) + +// var ( +// overrideTestData = flag.Bool("override-testdata", false, "if override the test output data.") +// defaultNamespace = "apk" +// ) + +// const ( +// // envoyHTTPPort is the container port number of Envoy's HTTP endpoint. +// envoyHTTPPort = int32(8080) +// // envoyHTTPSPort is the container port number of Envoy's HTTPS endpoint. +// envoyHTTPSPort = int32(8443) +// ) + +// func newTestInfra() *ir.Infra { +// return newTestInfraWithAnnotations(nil) +// } + +// func newTestInfraWithAnnotations(annotations map[string]string) *ir.Infra { +// return newTestInfraWithAnnotationsAndLabels(annotations, nil) +// } + +// func newTestInfraWithAddresses(addresses []string) *ir.Infra { +// infra := newTestInfraWithAnnotationsAndLabels(nil, nil) +// infra.Proxy.Addresses = addresses + +// return infra +// } + +// func newTestInfraWithAnnotationsAndLabels(annotations, labels map[string]string) *ir.Infra { +// i := ir.NewInfra() + +// i.Proxy.GetProxyMetadata().Annotations = annotations +// if len(labels) > 0 { +// i.Proxy.GetProxyMetadata().Labels = labels +// } +// i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" +// i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = i.Proxy.Name +// i.Proxy.Listeners = []*ir.ProxyListener{ +// { +// Ports: []ir.ListenerPort{ +// { +// Name: "EnvoyHTTPPort", +// Protocol: ir.TCPProtocolType, +// ContainerPort: envoyHTTPPort, +// }, +// { +// Name: "EnvoyHTTPSPort", +// Protocol: ir.TCPProtocolType, +// ContainerPort: envoyHTTPSPort, +// }, +// }, +// }, +// } + +// return i +// } + +// func TestDeployment(t *testing.T) { + +// cases := []struct { +// caseName string +// infra *ir.Infra +// deploy *egv1a1.KubernetesDeploymentSpec +// shutdown *egv1a1.ShutdownConfig +// proxyLogging map[egv1a1.ProxyLogComponent]egv1a1.LogLevel +// bootstrap string +// telemetry *egv1a1.ProxyTelemetry +// concurrency *int32 +// extraArgs []string +// }{ +// { +// caseName: "default", +// infra: newTestInfra(), +// deploy: nil, +// }, +// { +// caseName: "custom", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Replicas: ptr.To[int32](2), +// Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), +// Pod: &egv1a1.KubernetesPodSpec{ +// Labels: map[string]string{ +// "foo.bar": "custom-label", +// }, +// SecurityContext: &corev1.PodSecurityContext{ +// RunAsUser: ptr.To[int64](1000), +// }, +// }, +// EnvoyProxyContainer: &egv1a1.KubernetesContainerSpec{ +// Image: ptr.To("envoyproxy/envoy:v1.2.3"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// EnforcerContainer: &egv1a1.KubernetesContainerSpec{ +// Image: ptr.To("wso2/apk-enforcer:1.1.0"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// }, +// }, +// { +// caseName: "patch-deployment", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Patch: &egv1a1.KubernetesPatchSpec{ +// Type: ptr.To(egv1a1.StrategicMerge), +// Value: v1.JSON{ +// Raw: []byte("{\"spec\":{\"template\":{\"spec\":{\"hostNetwork\":true,\"dnsPolicy\":\"ClusterFirstWithHostNet\"}}}}"), +// }, +// }, +// }, +// }, +// { +// caseName: "shutdown-manager", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Patch: &egv1a1.KubernetesPatchSpec{ +// Type: ptr.To(egv1a1.StrategicMerge), +// Value: v1.JSON{ +// Raw: []byte(`{ +// "spec":{ +// "template":{ +// "spec":{ +// "containers":[{ +// "name":"shutdown-manager", +// "resources":{ +// "requests":{"cpu":"100m","memory":"64Mi"}, +// "limits":{"cpu":"200m","memory":"96Mi"} +// }, +// "securityContext":{"runAsUser":1234}, +// "env":[ +// {"name":"env_a","value":"env_a_value"}, +// {"name":"env_b","value":"env_b_value"} +// ], +// "image":"envoyproxy/gateway-dev:v1.2.3" +// }] +// } +// } +// } +// }`), +// }, +// }, +// }, +// shutdown: &egv1a1.ShutdownConfig{ +// DrainTimeout: &metav1.Duration{ +// Duration: 30 * time.Second, +// }, +// MinDrainDuration: &metav1.Duration{ +// Duration: 15 * time.Second, +// }, +// }, +// }, +// { +// caseName: "bootstrap", +// infra: newTestInfra(), +// deploy: nil, +// // bootstrap: `test bootstrap config`, +// }, +// { +// caseName: "extension-env", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Replicas: ptr.To[int32](2), +// Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), +// Pod: &egv1a1.KubernetesPodSpec{ +// // Annotations: map[string]string{ +// // "prometheus.io/scrape": "true", +// // }, +// SecurityContext: &corev1.PodSecurityContext{ +// RunAsUser: ptr.To[int64](1000), +// }, +// }, +// EnvoyProxyContainer: &egv1a1.KubernetesContainerSpec{ +// Env: []corev1.EnvVar{ +// { +// Name: "env_a", +// Value: "env_a_value", +// }, +// { +// Name: "env_b", +// Value: "env_b_value", +// }, +// }, +// Image: ptr.To("envoyproxy/envoy:v1.2.3"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// EnforcerContainer: &egv1a1.KubernetesContainerSpec{ +// // Env: []corev1.EnvVar{ +// // { +// // Name: "env_a", +// // Value: "env_a_value", +// // }, +// // { +// // Name: "env_b", +// // Value: "env_b_value", +// // }, +// // }, +// Image: ptr.To("wso2/apk-enforcer:1.1.0-ubuntu"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// }, +// }, +// { +// caseName: "default-env", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Replicas: ptr.To[int32](2), +// Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), +// Pod: &egv1a1.KubernetesPodSpec{ +// // Annotations: map[string]string{ +// // "prometheus.io/scrape": "true", +// // }, +// SecurityContext: &corev1.PodSecurityContext{ +// RunAsUser: ptr.To[int64](1000), +// }, +// }, +// EnvoyProxyContainer: &egv1a1.KubernetesContainerSpec{ +// Env: nil, +// Image: ptr.To("envoyproxy/envoy:v1.2.3"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// EnforcerContainer: &egv1a1.KubernetesContainerSpec{ +// Env: nil, +// Image: ptr.To("wso2/enforcer:1.1.0"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// RunAsUser: ptr.To[int64](1000), +// }, +// }, +// }, +// }, +// { +// caseName: "volumes", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Replicas: ptr.To[int32](2), +// Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), +// Pod: &egv1a1.KubernetesPodSpec{ +// // Annotations: map[string]string{ +// // "prometheus.io/scrape": "true", +// // }, +// SecurityContext: &corev1.PodSecurityContext{ +// RunAsUser: ptr.To[int64](1000), +// }, +// Volumes: []corev1.Volume{ +// { +// Name: "certs", +// VolumeSource: corev1.VolumeSource{ +// Secret: &corev1.SecretVolumeSource{ +// SecretName: "custom-envoy-cert", +// DefaultMode: ptr.To[int32](420), +// }, +// }, +// }, +// }, +// }, +// EnvoyProxyContainer: &egv1a1.KubernetesContainerSpec{ +// Env: []corev1.EnvVar{ +// { +// Name: "env_a", +// Value: "env_a_value", +// }, +// { +// Name: "env_b", +// Value: "env_b_value", +// }, +// }, +// Image: ptr.To("envoyproxy/envoy:v1.2.3"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// EnforcerContainer: &egv1a1.KubernetesContainerSpec{ +// Env: []corev1.EnvVar{ +// { +// Name: "env_a", +// Value: "env_a_value", +// }, +// { +// Name: "env_b", +// Value: "env_b_value", +// }, +// }, +// Image: ptr.To("wso2/enforcer:1.1.0"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// }, +// }, +// }, +// { +// caseName: "component-level", +// infra: newTestInfra(), +// deploy: nil, +// proxyLogging: map[egv1a1.ProxyLogComponent]egv1a1.LogLevel{ +// egv1a1.LogComponentDefault: egv1a1.LogLevelError, +// egv1a1.LogComponentFilter: egv1a1.LogLevelInfo, +// }, +// // bootstrap: `test bootstrap config`, +// }, +// // { +// // caseName: "disable-prometheus", +// // infra: newTestInfra(), +// // telemetry: &egv1a1.ProxyTelemetry{ +// // Metrics: &egv1a1.ProxyMetrics{ +// // Prometheus: &egv1a1.ProxyPrometheusProvider{ +// // Disable: true, +// // }, +// // }, +// // }, +// // }, +// { +// caseName: "with-concurrency", +// infra: newTestInfra(), +// deploy: nil, +// concurrency: ptr.To[int32](4), +// // bootstrap: `test bootstrap config`, +// }, +// { +// caseName: "custom_with_initcontainers", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Replicas: ptr.To[int32](3), +// Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(), +// Pod: &egv1a1.KubernetesPodSpec{ +// // Annotations: map[string]string{ +// // "prometheus.io/scrape": "true", +// // }, +// Labels: map[string]string{ +// "foo.bar": "custom-label", +// }, +// SecurityContext: &corev1.PodSecurityContext{ +// RunAsUser: ptr.To[int64](1000), +// }, +// Volumes: []corev1.Volume{ +// { +// Name: "custom-libs", +// VolumeSource: corev1.VolumeSource{ +// EmptyDir: &corev1.EmptyDirVolumeSource{}, +// }, +// }, +// }, +// }, +// EnvoyProxyContainer: &egv1a1.KubernetesContainerSpec{ +// Image: ptr.To("envoyproxy/envoy:v1.2.3"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// VolumeMounts: []corev1.VolumeMount{ +// { +// Name: "custom-libs", +// MountPath: "/lib/filter_foo.so", +// }, +// }, +// }, +// EnforcerContainer: &egv1a1.KubernetesContainerSpec{ +// Image: ptr.To("wso2/apk-enforcer:1.1.0"), +// Resources: &corev1.ResourceRequirements{ +// Limits: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("400m"), +// corev1.ResourceMemory: resource.MustParse("2Gi"), +// }, +// Requests: corev1.ResourceList{ +// corev1.ResourceCPU: resource.MustParse("200m"), +// corev1.ResourceMemory: resource.MustParse("1Gi"), +// }, +// }, +// SecurityContext: &corev1.SecurityContext{ +// Privileged: ptr.To(true), +// }, +// // VolumeMounts: []corev1.VolumeMount{ +// // { +// // Name: "custom-libs", +// // MountPath: "/lib/filter_foo.so", +// // }, +// // }, +// }, +// InitContainers: []corev1.Container{ +// { +// Name: "install-filter-foo", +// Image: "alpine:3.11.3", +// Command: []string{"/bin/sh", "-c"}, +// Args: []string{"echo \"Installing filter-foo\"; wget -q https://example.com/download/filter_foo_v1.0.0.tgz -O - | tar -xz --directory=/lib filter_foo.so; echo \"Done\";"}, +// VolumeMounts: []corev1.VolumeMount{ +// { +// Name: "custom-libs", +// MountPath: "/lib", +// }, +// }, +// }, +// }, +// }, +// }, +// { +// caseName: "with-annotations", +// infra: newTestInfraWithAnnotations(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }), +// deploy: nil, +// }, +// { +// caseName: "override-labels-and-annotations", +// infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }, map[string]string{ +// "label1": "value1", +// "label2": "value2", +// }), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Pod: &egv1a1.KubernetesPodSpec{ +// Annotations: map[string]string{ +// "anno1": "value1-override", +// }, +// Labels: map[string]string{ +// "label1": "value1-override", +// }, +// }, +// }, +// }, +// { +// caseName: "with-image-pull-secrets", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Pod: &egv1a1.KubernetesPodSpec{ +// ImagePullSecrets: []corev1.LocalObjectReference{ +// { +// Name: "aaa", +// }, +// { +// Name: "bbb", +// }, +// }, +// }, +// }, +// }, +// { +// caseName: "with-node-selector", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Pod: &egv1a1.KubernetesPodSpec{ +// NodeSelector: map[string]string{ +// "key1": "value1", +// "key2": "value2", +// }, +// }, +// }, +// }, +// { +// caseName: "with-topology-spread-constraints", +// infra: newTestInfra(), +// deploy: &egv1a1.KubernetesDeploymentSpec{ +// Pod: &egv1a1.KubernetesPodSpec{ +// TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ +// { +// MaxSkew: 1, +// TopologyKey: "kubernetes.io/hostname", +// WhenUnsatisfiable: corev1.DoNotSchedule, +// LabelSelector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{"app": "foo"}, +// }, +// MatchLabelKeys: []string{"pod-template-hash"}, +// }, +// }, +// }, +// }, +// }, +// // { +// // caseName: "with-extra-args", +// // infra: newTestInfra(), +// // // extraArgs: []string{"--key1 val1", "--key2 val2"}, +// // }, +// } +// for _, tc := range cases { +// t.Run(tc.caseName, func(t *testing.T) { +// kube := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider().GetEnvoyProxyKubeProvider() +// if tc.deploy != nil { +// kube.EnvoyDeployment = tc.deploy +// } + +// replace := egv1a1.BootstrapTypeReplace +// if tc.bootstrap != "" { +// tc.infra.Proxy.Config.Spec.Bootstrap = &egv1a1.ProxyBootstrap{ +// Type: &replace, +// Value: tc.bootstrap, +// } +// } + +// if tc.telemetry != nil { +// tc.infra.Proxy.Config.Spec.Telemetry = tc.telemetry +// } + +// if len(tc.proxyLogging) > 0 { +// tc.infra.Proxy.Config.Spec.Logging = egv1a1.ProxyLogging{ +// Level: tc.proxyLogging, +// } +// } + +// if tc.concurrency != nil { +// tc.infra.Proxy.Config.Spec.Concurrency = tc.concurrency +// } + +// if tc.shutdown != nil { +// tc.infra.Proxy.Config.Spec.Shutdown = tc.shutdown +// } + +// if len(tc.extraArgs) > 0 { +// tc.infra.Proxy.Config.Spec.ExtraArgs = tc.extraArgs +// } + +// r := NewResourceRender(defaultNamespace, tc.infra.GetProxyInfra()) +// dp, err := r.Deployment() +// require.NoError(t, err) + +// expected, err := loadDeployment(tc.caseName) +// require.NoError(t, err) + +// sortEnv := func(env []corev1.EnvVar) { +// sort.Slice(env, func(i, j int) bool { +// return env[i].Name > env[j].Name +// }) +// } + +// if *overrideTestData { +// deploymentYAML, err := yaml.Marshal(dp) +// require.NoError(t, err) +// // nolint: gosec +// err = os.WriteFile(fmt.Sprintf("testdata/deployments/%s.yaml", tc.caseName), deploymentYAML, 0644) +// require.NoError(t, err) +// return +// } + +// sortEnv(dp.Spec.Template.Spec.Containers[0].Env) +// sortEnv(expected.Spec.Template.Spec.Containers[0].Env) +// assert.Equal(t, expected, dp, "failed "+tc.caseName) +// }) +// } +// } + +// func loadDeployment(caseName string) (*appsv1.Deployment, error) { +// deploymentYAML, err := os.ReadFile(fmt.Sprintf("testdata/deployments/%s.yaml", caseName)) +// if err != nil { +// return nil, err +// } +// deployment := &appsv1.Deployment{} +// _ = yaml.Unmarshal(deploymentYAML, deployment) +// return deployment, nil +// } + +// func TestService(t *testing.T) { + +// svcType := egv1a1.ServiceTypeClusterIP +// cases := []struct { +// caseName string +// infra *ir.Infra +// service *egv1a1.KubernetesServiceSpec +// }{ +// { +// caseName: "default", +// infra: newTestInfra(), +// service: nil, +// }, +// { +// caseName: "custom", +// infra: newTestInfra(), +// service: &egv1a1.KubernetesServiceSpec{ +// Annotations: map[string]string{ +// "key1": "value1", +// }, +// Type: &svcType, +// }, +// }, +// { +// caseName: "with-annotations", +// infra: newTestInfraWithAnnotations(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }), +// }, +// { +// caseName: "override-annotations", +// infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }, map[string]string{ +// "label1": "value1", +// "label2": "value2", +// }), +// service: &egv1a1.KubernetesServiceSpec{ +// Annotations: map[string]string{ +// "anno1": "value1-override", +// }, +// }, +// }, +// { +// caseName: "clusterIP-custom-addresses", +// infra: newTestInfraWithAddresses([]string{ +// "10.102.168.100", +// }), +// service: &egv1a1.KubernetesServiceSpec{ +// Type: &svcType, +// }, +// }, +// { +// caseName: "patch-service", +// infra: newTestInfra(), +// service: &egv1a1.KubernetesServiceSpec{ +// Patch: &egv1a1.KubernetesPatchSpec{ +// Type: ptr.To(egv1a1.StrategicMerge), +// Value: v1.JSON{ +// Raw: []byte("{\"metadata\":{\"name\":\"foo\"}}"), +// }, +// }, +// }, +// }, +// } +// for _, tc := range cases { +// t.Run(tc.caseName, func(t *testing.T) { +// provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider().GetEnvoyProxyKubeProvider() +// if tc.service != nil { +// provider.EnvoyService = tc.service +// } + +// r := NewResourceRender(defaultNamespace, tc.infra.GetProxyInfra()) +// svc, err := r.Service() +// require.NoError(t, err) + +// expected, err := loadService(tc.caseName) +// require.NoError(t, err) + +// assert.Equal(t, expected, svc) +// }) +// } +// } + +// func loadService(caseName string) (*corev1.Service, error) { +// serviceYAML, err := os.ReadFile(fmt.Sprintf("testdata/services/%s.yaml", caseName)) +// if err != nil { +// return nil, err +// } +// svc := &corev1.Service{} +// _ = yaml.Unmarshal(serviceYAML, svc) +// return svc, nil +// } + +// func TestConfigMap(t *testing.T) { + +// cases := []struct { +// name string +// infra *ir.Infra +// }{ +// { +// name: "default", +// infra: newTestInfra(), +// }, { +// name: "with-annotations", +// infra: newTestInfraWithAnnotations(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }), +// }, +// } + +// for _, tc := range cases { +// t.Run(tc.name, func(t *testing.T) { +// r := NewResourceRender(defaultNamespace, tc.infra.GetProxyInfra()) +// cm, err := r.ConfigMap() +// require.NoError(t, err) + +// expected, err := loadConfigmap(tc.name) +// require.NoError(t, err) + +// assert.Equal(t, expected, cm) +// }) +// } +// } + +// func loadConfigmap(tc string) (*corev1.ConfigMap, error) { +// cmYAML, err := os.ReadFile(fmt.Sprintf("testdata/configmap/%s.yaml", tc)) +// if err != nil { +// return nil, err +// } +// cm := &corev1.ConfigMap{} +// _ = yaml.Unmarshal(cmYAML, cm) +// return cm, nil +// } + +// func TestServiceAccount(t *testing.T) { +// cases := []struct { +// name string +// infra *ir.Infra +// }{ +// { +// name: "default", +// infra: newTestInfra(), +// }, { +// name: "with-annotations", +// infra: newTestInfraWithAnnotations(map[string]string{ +// "anno1": "value1", +// "anno2": "value2", +// }), +// }, +// } + +// for _, tc := range cases { +// t.Run(tc.name, func(t *testing.T) { +// r := NewResourceRender(defaultNamespace, tc.infra.GetProxyInfra()) +// sa, err := r.ServiceAccount() +// require.NoError(t, err) + +// expected, err := loadServiceAccount(tc.name) +// require.NoError(t, err) + +// assert.Equal(t, expected, sa) +// }) +// } +// } + +// func loadServiceAccount(tc string) (*corev1.ServiceAccount, error) { +// saYAML, err := os.ReadFile(fmt.Sprintf("testdata/serviceaccount/%s.yaml", tc)) +// if err != nil { +// return nil, err +// } +// sa := &corev1.ServiceAccount{} +// _ = yaml.Unmarshal(saYAML, sa) +// return sa, nil +// } + +// func TestHorizontalPodAutoscaler(t *testing.T) { + +// cases := []struct { +// caseName string +// infra *ir.Infra +// hpa *egv1a1.KubernetesHorizontalPodAutoscalerSpec +// }{ +// { +// caseName: "default", +// infra: newTestInfra(), +// hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ +// MaxReplicas: ptr.To[int32](1), +// }, +// }, +// { +// caseName: "custom", +// infra: newTestInfra(), +// hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ +// MinReplicas: ptr.To[int32](5), +// MaxReplicas: ptr.To[int32](10), +// Metrics: []autoscalingv2.MetricSpec{ +// { +// Resource: &autoscalingv2.ResourceMetricSource{ +// Name: corev1.ResourceCPU, +// Target: autoscalingv2.MetricTarget{ +// Type: autoscalingv2.UtilizationMetricType, +// AverageUtilization: ptr.To[int32](60), +// }, +// }, +// Type: autoscalingv2.ResourceMetricSourceType, +// }, +// { +// Resource: &autoscalingv2.ResourceMetricSource{ +// Name: corev1.ResourceMemory, +// Target: autoscalingv2.MetricTarget{ +// Type: autoscalingv2.UtilizationMetricType, +// AverageUtilization: ptr.To[int32](70), +// }, +// }, +// Type: autoscalingv2.ResourceMetricSourceType, +// }, +// }, +// }, +// }, +// } + +// for _, tc := range cases { +// t.Run(tc.caseName, func(t *testing.T) { +// provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider() +// provider.Kubernetes = egv1a1.DefaultEnvoyProxyKubeProvider() + +// if tc.hpa != nil { +// provider.Kubernetes.EnvoyHpa = tc.hpa +// } + +// provider.GetEnvoyProxyKubeProvider() + +// r := NewResourceRender(defaultNamespace, tc.infra.GetProxyInfra()) +// hpa, err := r.HorizontalPodAutoscaler() +// require.NoError(t, err) + +// want, err := loadHPA(tc.caseName) +// require.NoError(t, err) + +// assert.Equal(t, want, hpa) +// }) +// } +// } + +// func loadHPA(caseName string) (*autoscalingv2.HorizontalPodAutoscaler, error) { +// hpaYAML, err := os.ReadFile(fmt.Sprintf("testdata/hpa/%s.yaml", caseName)) +// if err != nil { +// return nil, err +// } + +// hpa := &autoscalingv2.HorizontalPodAutoscaler{} +// _ = yaml.Unmarshal(hpaYAML, hpa) +// return hpa, nil +// } + +// func TestOwningGatewayLabelsAbsent(t *testing.T) { + +// cases := []struct { +// caseName string +// labels map[string]string +// expect bool +// }{ +// { +// caseName: "OwningGatewayClassLabel exist, but lack OwningGatewayNameLabel or OwningGatewayNamespaceLabel", +// labels: map[string]string{ +// "apk.wso2.com/owning-gatewayclass": "eg-class", +// }, +// expect: false, +// }, +// { +// caseName: "OwningGatewayNameLabel and OwningGatewayNamespaceLabel exist, but lack OwningGatewayClassLabel", +// labels: map[string]string{ +// "apk.wso2.com/owning-gateway-name": "eg", +// "apk.wso2.com/owning-gateway-namespace": "default", +// }, +// expect: false, +// }, +// { +// caseName: "OwningGatewayNameLabel exist, but lack OwningGatewayClassLabel and OwningGatewayNamespaceLabel", +// labels: map[string]string{ +// "apk.wso2.com/owning-gateway-name": "eg", +// }, +// expect: true, +// }, +// { +// caseName: "OwningGatewayNamespaceLabel exist, but lack OwningGatewayClassLabel and OwningGatewayNameLabel", +// labels: map[string]string{ +// "apk.wso2.com/owning-gateway-namespace": "default", +// }, +// expect: true, +// }, +// { +// caseName: "lack all labels", +// labels: map[string]string{}, +// expect: true, +// }, +// } + +// for _, tc := range cases { +// t.Run(tc.caseName, func(t *testing.T) { +// actual := OwningGatewayLabelsAbsent(tc.labels) +// require.Equal(t, tc.expect, actual) +// }) +// } + +// } diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_test.go new file mode 100644 index 0000000000..de4881c035 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/resource_test.go @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package proxy + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEnvoyPodSelector(t *testing.T) { + cases := []struct { + name string + in map[string]string + expected map[string]string + }{ + { + name: "default", + in: map[string]string{"foo": "bar"}, + expected: map[string]string{ + "foo": "bar", + "app.kubernetes.io/name": "envoy", + "app.kubernetes.io/component": "proxy", + "app.kubernetes.io/managed-by": "envoy-gateway", + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run("", func(t *testing.T) { + got := envoyLabels(tc.in) + require.Equal(t, tc.expected, got) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml new file mode 100644 index 0000000000..088b5372ce --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/default.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +data: + xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}' + xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}' diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml new file mode 100644 index 0000000000..0d2cc9cbd7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/configmap/with-annotations.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +data: + xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}' + xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}' diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml new file mode 100644 index 0000000000..7de7ca171f --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml @@ -0,0 +1,296 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml new file mode 100644 index 0000000000..7ba4cb464b --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml @@ -0,0 +1,297 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + # - --component-log-level filter:info + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml new file mode 100644 index 0000000000..dddf498084 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml @@ -0,0 +1,311 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + replicas: 2 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + foo.bar: custom-label + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + foo.bar: custom-label + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + securityContext: + privileged: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsUser: 1000 + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml new file mode 100644 index 0000000000..7a3d8264d4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml @@ -0,0 +1,328 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + replicas: 3 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + foo.bar: custom-label + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + foo.bar: custom-label + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + securityContext: + privileged: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - mountPath: /lib/filter_foo.so + name: custom-libs + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 200m + memory: 1Gi + limits: + cpu: 400m + memory: 2Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + dnsPolicy: ClusterFirst + initContainers: + - args: + - echo "Installing filter-foo"; wget -q https://example.com/download/filter_foo_v1.0.0.tgz + -O - | tar -xz --directory=/lib filter_foo.so; echo "Done"; + command: + - /bin/sh + - -c + image: alpine:3.11.3 + name: install-filter-foo + resources: {} + volumeMounts: + - mountPath: /lib + name: custom-libs + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsUser: 1000 + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds + - emptyDir: {} + name: custom-libs +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml new file mode 100644 index 0000000000..b515266661 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml @@ -0,0 +1,310 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + replicas: 2 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + securityContext: + privileged: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + RunAsUser: 1000 + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsUser: 1000 + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml new file mode 100644 index 0000000000..d459b8da9b --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml @@ -0,0 +1,296 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml new file mode 100644 index 0000000000..a7f68d155a --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml @@ -0,0 +1,264 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml new file mode 100644 index 0000000000..a7bb218577 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml @@ -0,0 +1,313 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + replicas: 2 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: env_a + value: env_a_value + - name: env_b + value: env_b_value + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + securityContext: + privileged: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0-ubuntu + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsUser: 1000 + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml new file mode 100644 index 0000000000..b1086f11d9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml @@ -0,0 +1,307 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + anno1: value1 + anno2: value2 + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + label1: value1 + label2: value2 + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + label1: value1-override + label2: value2 + strategy: + type: RollingUpdate + template: + metadata: + annotations: + anno1: value1-override + anno2: value2 + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + label1: value1-override + label2: value2 + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml new file mode 100644 index 0000000000..3ca34b511c --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml @@ -0,0 +1,297 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirstWithHostNet + hostNetwork: true + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml new file mode 100644 index 0000000000..46de0fee69 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml @@ -0,0 +1,309 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + - --drain-time-s 30 + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + - --ready-timeout=40s + command: + - envoy-gateway + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_value + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + - --drain-timeout=30s + - --min-drain-duration=15s + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 200m + memory: 96Mi + requests: + cpu: 100m + memory: 64Mi + securityContext: + runAsUser: 1234 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 330 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml new file mode 100644 index 0000000000..fe1845ef59 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml @@ -0,0 +1,313 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + replicas: 2 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: env_a + value: env_a_value + - name: env_b + value: env_b_value + image: envoyproxy/envoy:v1.2.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + securityContext: + privileged: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + limits: + cpu: 400m + memory: 2Gi + requests: + cpu: 200m + memory: 1Gi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + securityContext: + privileged: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsUser: 1000 + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: custom-envoy-cert + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml new file mode 100644 index 0000000000..e94ff083e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml @@ -0,0 +1,301 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + anno1: value1 + anno2: value2 + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + annotations: + anno1: value1 + anno2: value2 + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml new file mode 100644 index 0000000000..5ee173a50c --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml @@ -0,0 +1,297 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + # - --concurrency 4 + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml new file mode 100644 index 0000000000..5a358d89e2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml @@ -0,0 +1,298 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + # - --key1 val1 + # - --key2 val2 + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml new file mode 100644 index 0000000000..cd3821c500 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml @@ -0,0 +1,299 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: aaa + - name: bbb + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml new file mode 100644 index 0000000000..01ea0267a6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml @@ -0,0 +1,299 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + nodeSelector: + key1: value1 + key2: value2 + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml new file mode 100644 index 0000000000..ab8338bb84 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml @@ -0,0 +1,305 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + strategy: + type: RollingUpdate + template: + metadata: + # annotations: + # prometheus.io/path: /stats/prometheus + # prometheus.io/port: "19001" + # prometheus.io/scrape: "true" + creationTimestamp: null + labels: + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + app.kubernetes.io/name: envoy + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + spec: + automountServiceAccountToken: false + containers: + - args: + - --service-cluster default + - --service-node $(ENVOY_POD_NAME) + - | + --config-yaml admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 0.0.0.0 + port_value: 19000 + layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 + dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 + static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-19001 + address: + socket_address: + address: 0.0.0.0 + port_value: 19001 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 18000 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: "/sds/xds-certificate.json" + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: "/sds/xds-trusted-ca.json" + resource_api_version: V3 + - --log-level warn + - --cpuset-threads + command: + - envoy + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/envoy:distroless-v1.29.3 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + httpGet: + path: /shutdown/ready + port: 19002 + scheme: HTTP + name: envoy + ports: + - containerPort: 8080 + name: EnvoyH-d76a15e2 + protocol: TCP + - containerPort: 8443 + name: EnvoyH-6658f727 + protocol: TCP + # - containerPort: 19001 + # name: metrics + # protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /ready + port: 19001 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /certs + name: certs + readOnly: true + - mountPath: /sds + name: sds + - args: + - envoy + - shutdown-manager + command: + - envoy-gateway + env: + - name: ENVOY_GATEWAY_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: ENVOY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + image: envoyproxy/gateway-dev:latest + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - envoy-gateway + - envoy + - shutdown + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: shutdown-manager + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 19002 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 10m + memory: 32Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + - image: wso2/apk-enforcer:1.1.0 + imagePullPolicy: IfNotPresent + name: enforcer + readinessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + livenessProbe: + exec: + command: ["sh", "check_health.sh"] + periodSeconds: 20 + failureThreshold: 5 + initialDelaySeconds: 20 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 512Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + serviceAccountName: envoy-default-37a8eec1 + terminationGracePeriodSeconds: 900 + topologySpreadConstraints: + - labelSelector: + matchLabels: + app: foo + matchLabelKeys: + - pod-template-hash + maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + volumes: + - name: certs + secret: + defaultMode: 420 + secretName: envoy + - configMap: + defaultMode: 420 + items: + - key: xds-trusted-ca.json + path: xds-trusted-ca.json + - key: xds-certificate.json + path: xds-certificate.json + name: envoy-default-37a8eec1 + optional: false + name: sds +status: {} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml new file mode 100644 index 0000000000..274b2b53ee --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/custom.yaml @@ -0,0 +1,28 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + maxReplicas: 10 + metrics: + - resource: + name: cpu + target: + averageUtilization: 60 + type: Utilization + type: Resource + - resource: + name: memory + target: + averageUtilization: 70 + type: Utilization + type: Resource + minReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml new file mode 100644 index 0000000000..f41b6cc458 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/default.yaml @@ -0,0 +1,21 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + metrics: + - resource: + name: cpu + target: + averageUtilization: 80 + type: Utilization + type: Resource + maxReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml new file mode 100644 index 0000000000..61eb91af09 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/hpa/with-annotations.yaml @@ -0,0 +1,24 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + metrics: + - resource: + name: cpu + target: + averageUtilization: 80 + type: Utilization + type: Resource + maxReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: envoy-default-37a8eec1 diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml new file mode 100644 index 0000000000..e7da9b590c --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/default.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml new file mode 100644 index 0000000000..184fce181a --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/serviceaccount/with-annotations.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/clusterIP-custom-addresses.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/clusterIP-custom-addresses.yaml new file mode 100644 index 0000000000..6585fe33bb --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/clusterIP-custom-addresses.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + clusterIP: 10.102.168.100 + clusterIPs: + - 10.102.168.100 + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + sessionAffinity: None + type: ClusterIP diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/custom.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/custom.yaml new file mode 100644 index 0000000000..64f21eb51f --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/custom.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + key1: value1 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + sessionAffinity: None + type: ClusterIP diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/default.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/default.yaml new file mode 100644 index 0000000000..a859db6e4d --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/default.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml new file mode 100644 index 0000000000..3f361b1f5d --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/override-annotations.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1-override + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + label1: value1 + label2: value2 + name: envoy-default-37a8eec1 + namespace: apk +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + label1: value1 + label2: value2 + sessionAffinity: None + type: LoadBalancer diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/patch-service.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/patch-service.yaml new file mode 100644 index 0000000000..c62c7799c1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/patch-service.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: foo + namespace: apk +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml new file mode 100644 index 0000000000..3569d28972 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy/testdata/services/with-annotations.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: apk +spec: + externalTrafficPolicy: Local + ports: + - name: envoy-EnvoyHTTPPort-d76a15e2 + port: 0 + protocol: TCP + targetPort: 8080 + - name: envoy-EnvoyHTTPSPort-6658f727 + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + apk.wso2.com/owning-gateway-name: default + apk.wso2.com/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_configmap_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_configmap_test.go new file mode 100644 index 0000000000..6a04adc8b2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_configmap_test.go @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var ( + namespace = "apk" +) + +// func TestCreateOrUpdateProxyConfigMap(t *testing.T) { +// infra := ir.NewInfra() +// infra.Proxy.Name = "test" +// infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" +// infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name + +// testCases := []struct { +// name string +// current *corev1.ConfigMap +// expect *corev1.ConfigMap +// }{ +// { +// name: "create configmap", +// expect: &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Namespace: namespace, +// Name: "envoy-test-9f86d081", +// Labels: map[string]string{ +// "app.kubernetes.io/name": "envoy", +// "app.kubernetes.io/component": "proxy", +// "app.kubernetes.io/managed-by": "envoy-gateway", +// gatewayapi.OwningGatewayNamespaceLabel: "default", +// gatewayapi.OwningGatewayNameLabel: "test", +// }, +// }, +// Data: map[string]string{ +// proxy.SdsCAFilename: proxy.SdsCAConfigMapData, +// proxy.SdsCertFilename: proxy.SdsCertConfigMapData, +// }, +// }, +// }, +// { +// name: "update configmap", +// current: &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Namespace: namespace, +// Name: "envoy-test", +// Labels: map[string]string{ +// "app.kubernetes.io/name": "envoy", +// "app.kubernetes.io/component": "proxy", +// "app.kubernetes.io/managed-by": "envoy-gateway", +// gatewayapi.OwningGatewayNamespaceLabel: "default", +// gatewayapi.OwningGatewayNameLabel: "test", +// }, +// }, +// Data: map[string]string{"foo": "bar"}, +// }, +// expect: &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Namespace: namespace, +// Name: "envoy-test-9f86d081", +// Labels: map[string]string{ +// "app.kubernetes.io/name": "envoy", +// "app.kubernetes.io/component": "proxy", +// "app.kubernetes.io/managed-by": "envoy-gateway", +// gatewayapi.OwningGatewayNamespaceLabel: "default", +// gatewayapi.OwningGatewayNameLabel: "test", +// }, +// }, +// Data: map[string]string{ +// proxy.SdsCAFilename: proxy.SdsCAConfigMapData, +// proxy.SdsCertFilename: proxy.SdsCertConfigMapData, +// }, +// }, +// }, +// } + +// for _, tc := range testCases { +// tc := tc +// t.Run(tc.name, func(t *testing.T) { +// var cli client.Client +// if tc.current != nil { +// cli = fakeclient.NewClientBuilder().WithObjects(tc.current).Build() +// } else { +// cli = fakeclient.NewClientBuilder().Build() +// } +// kube := NewInfra(cli) +// r := proxy.NewResourceRender(kube.Namespace, infra.GetProxyInfra()) +// err := kube.createOrUpdateConfigMap(context.Background(), r) +// require.NoError(t, err) +// actual := &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Namespace: tc.expect.Namespace, +// Name: tc.expect.Name, +// }, +// } +// require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)) +// require.Equal(t, tc.expect.Data, actual.Data) +// assert.True(t, apiequality.Semantic.DeepEqual(tc.expect.Labels, actual.Labels)) +// }) +// } +// } + +func TestDeleteConfigProxyMap(t *testing.T) { + + infra := ir.NewInfra() + infra.Proxy.Name = "test" + + testCases := []struct { + name string + current *corev1.ConfigMap + expect bool + }{ + { + name: "delete configmap", + current: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "envoy-test", + }, + }, + expect: true, + }, + { + name: "configmap not found", + current: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "foo", + }, + }, + expect: true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + cli := fakeclient.NewClientBuilder().WithObjects(tc.current).Build() + kube := NewInfra(cli) + + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name + + r := proxy.NewResourceRender(kube.Namespace, infra.GetProxyInfra()) + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: r.Name(), + }, + } + err := kube.Client.Delete(context.Background(), cm) + require.NoError(t, err) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_deployment_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_deployment_test.go new file mode 100644 index 0000000000..15966b478b --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_deployment_test.go @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +const ( + // envoyContainerName is the name of the Envoy container. + envoyContainerName = "envoy" +) + +func deploymentWithImage(deploy *appsv1.Deployment, image string) *appsv1.Deployment { + dCopy := deploy.DeepCopy() + for i, c := range dCopy.Spec.Template.Spec.Containers { + if c.Name == envoyContainerName { + dCopy.Spec.Template.Spec.Containers[i].Image = image + } + } + return dCopy +} + +func TestCreateOrUpdateProxyDeployment(t *testing.T) { + + infra := ir.NewInfra() + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name + + r := proxy.NewResourceRender("", infra.GetProxyInfra()) + deploy, err := r.Deployment() + require.NoError(t, err) + + testCases := []struct { + name string + in *ir.Infra + current *appsv1.Deployment + want *appsv1.Deployment + }{ + { + name: "create deployment", + in: infra, + want: deploy, + }, + { + name: "deployment exists", + in: infra, + current: deploy, + want: deploy, + }, + { + name: "update deployment image", + in: &ir.Infra{ + Proxy: &ir.ProxyInfra{ + Metadata: &ir.InfraMetadata{ + Labels: map[string]string{ + gatewayapi.OwningGatewayNamespaceLabel: "default", + gatewayapi.OwningGatewayNameLabel: infra.Proxy.Name, + }, + }, + Name: ir.DefaultProxyName, + Listeners: ir.NewProxyListeners(), + }, + }, + current: deploy, + want: deploy, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + var cli client.Client + if tc.current != nil { + cli = fakeclient.NewClientBuilder().WithObjects(tc.current).Build() + } else { + cli = fakeclient.NewClientBuilder().Build() + } + + kube := NewInfra(cli) + r := proxy.NewResourceRender(kube.Namespace, tc.in.GetProxyInfra()) + err := kube.createOrUpdateDeployment(context.Background(), r) + require.NoError(t, err) + + actual := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: proxy.ExpectedResourceHashedName(tc.in.Proxy.Name), + }, + } + require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)) + require.Equal(t, tc.want.Spec, actual.Spec) + }) + } +} + +func TestDeleteProxyDeployment(t *testing.T) { + cli := fakeclient.NewClientBuilder().WithObjects().Build() + + testCases := []struct { + name string + expect bool + }{ + { + name: "delete deployment", + expect: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + kube := NewInfra(cli) + + infra := ir.NewInfra() + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name + r := proxy.NewResourceRender(kube.Namespace, infra.GetProxyInfra()) + + err := kube.createOrUpdateDeployment(context.Background(), r) + require.NoError(t, err) + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: r.Name(), + }, + } + err = kube.Client.Delete(context.Background(), deployment) + require.NoError(t, err) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra.go new file mode 100644 index 0000000000..ab898ea025 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra.go @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "errors" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" +) + +// CreateOrUpdateProxyInfra creates the managed kube infra, if it doesn't exist. +func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error { + if infra == nil { + return errors.New("infra ir is nil") + } + + if infra.Proxy == nil { + return errors.New("infra proxy ir is nil") + } + + r := proxy.NewResourceRender(i.Namespace, infra.GetProxyInfra()) + return i.createOrUpdate(ctx, r) +} + +// DeleteProxyInfra removes the managed kube infra, if it doesn't exist. +func (i *Infra) DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error { + if infra == nil { + return errors.New("infra ir is nil") + } + + r := proxy.NewResourceRender(i.Namespace, infra.GetProxyInfra()) + return i.delete(ctx, r) +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra_test.go new file mode 100644 index 0000000000..cd3827f279 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_infra_test.go @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func newTestInfra(t *testing.T) *Infra { + cli := fakeclient.NewClientBuilder().Build() + return newTestInfraWithClient(t, cli) +} + +func TestCmpBytes(t *testing.T) { + m1 := map[string][]byte{} + m1["a"] = []byte("aaa") + m2 := map[string][]byte{} + m2["a"] = []byte("aaa") + + assert.True(t, reflect.DeepEqual(m1, m2)) + assert.False(t, reflect.DeepEqual(nil, m2)) + assert.False(t, reflect.DeepEqual(m1, nil)) +} + +func newTestInfraWithClient(t *testing.T, cli client.Client) *Infra { + return NewInfra(cli) +} + +func TestCreateProxyInfra(t *testing.T) { + // Infra with Gateway owner labels. + infraWithLabels := ir.NewInfra() + infraWithLabels.GetProxyInfra().GetProxyMetadata().Labels = proxy.EnvoyAppLabel() + infraWithLabels.GetProxyInfra().GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" + infraWithLabels.GetProxyInfra().GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = "test-gw" + + testCases := []struct { + name string + in *ir.Infra + expect bool + }{ + { + name: "infra-with-expected-labels", + in: infraWithLabels, + expect: true, + }, + { + name: "default infra without Gateway owner labels", + in: ir.NewInfra(), + expect: false, + }, + { + name: "nil-infra", + in: nil, + expect: false, + }, + { + name: "nil-infra-proxy", + in: &ir.Infra{ + Proxy: nil, + }, + expect: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + kube := newTestInfra(t) + // Create or update the proxy infra. + err := kube.CreateOrUpdateProxyInfra(context.Background(), tc.in) + if !tc.expect { + require.Error(t, err) + } else { + require.NoError(t, err) + + // Verify all resources were created via the fake kube client. + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: proxy.ExpectedResourceHashedName(tc.in.Proxy.Name), + }, + } + require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(sa), sa)) + + // cm := &corev1.ConfigMap{ + // ObjectMeta: metav1.ObjectMeta{ + // Namespace: kube.Namespace, + // Name: proxy.ExpectedResourceHashedName(tc.in.Proxy.Name), + // }, + // } + // require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(cm), cm)) + + deploy := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: proxy.ExpectedResourceHashedName(tc.in.Proxy.Name), + }, + } + require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(deploy), deploy)) + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kube.Namespace, + Name: proxy.ExpectedResourceHashedName(tc.in.Proxy.Name), + }, + } + require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(svc), svc)) + } + }) + } +} + +func TestDeleteProxyInfra(t *testing.T) { + + testCases := []struct { + name string + in *ir.Infra + expect bool + }{ + { + name: "nil infra", + in: nil, + expect: false, + }, + { + name: "default infra", + in: ir.NewInfra(), + expect: true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + kube := newTestInfra(t) + + err := kube.DeleteProxyInfra(context.Background(), tc.in) + if !tc.expect { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_service_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_service_test.go new file mode 100644 index 0000000000..f5fea49d2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy_service_test.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" +) + +func TestDeleteProxyService(t *testing.T) { + testCases := []struct { + name string + }{ + { + name: "delete service", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + kube := newTestInfra(t) + infra := ir.NewInfra() + + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default" + infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name + r := proxy.NewResourceRender(kube.Namespace, infra.GetProxyInfra()) + err := kube.createOrUpdateService(context.Background(), r) + require.NoError(t, err) + + err = kube.deleteService(context.Background(), r) + require.NoError(t, err) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource.go new file mode 100644 index 0000000000..06c6721574 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource.go @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package resource + +import ( + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" +) + +// GetSelector returns a label selector used to select resources +// based on the provided labels. +func GetSelector(labels map[string]string) *metav1.LabelSelector { + return &metav1.LabelSelector{ + MatchLabels: labels, + } +} + +// ExpectedServiceSpec returns service spec. +func ExpectedServiceSpec(service *egv1a1.KubernetesServiceSpec) corev1.ServiceSpec { + serviceSpec := corev1.ServiceSpec{} + serviceSpec.Type = corev1.ServiceType(*service.Type) + serviceSpec.SessionAffinity = corev1.ServiceAffinityNone + if service.ExternalTrafficPolicy == nil { + service.ExternalTrafficPolicy = egv1a1.DefaultKubernetesServiceExternalTrafficPolicy() + } + if *service.Type == egv1a1.ServiceTypeLoadBalancer { + if service.LoadBalancerClass != nil { + serviceSpec.LoadBalancerClass = service.LoadBalancerClass + } + if service.AllocateLoadBalancerNodePorts != nil { + serviceSpec.AllocateLoadBalancerNodePorts = service.AllocateLoadBalancerNodePorts + } + if service.LoadBalancerIP != nil { + serviceSpec.LoadBalancerIP = *service.LoadBalancerIP + } + serviceSpec.ExternalTrafficPolicy = corev1.ServiceExternalTrafficPolicy(*service.ExternalTrafficPolicy) + } + + return serviceSpec +} + +// CompareSvc compares the Service resource and ignores specific fields that may have been modified by other actors. +func CompareSvc(currentSvc, originalSvc *corev1.Service) bool { + return cmp.Equal(currentSvc.Spec, originalSvc.Spec, + cmpopts.IgnoreFields(corev1.ServicePort{}, "NodePort"), + cmpopts.IgnoreFields(corev1.ServiceSpec{}, "ClusterIP", "ClusterIPs"), + cmpopts.IgnoreFields(metav1.ObjectMeta{}, "Finalizers")) +} + +// ExpectedContainerEnv returns expected container envs. +func ExpectedContainerEnv(container *egv1a1.KubernetesContainerSpec, env []corev1.EnvVar) []corev1.EnvVar { + amendFunc := func(envVar corev1.EnvVar) { + for index, e := range env { + if e.Name == envVar.Name { + env[index] = envVar + return + } + } + env = append(env, envVar) + } + + for _, envVar := range container.Env { + amendFunc(envVar) + } + + return env +} + +// ExpectedDeploymentVolumes returns expected deployment volumes. +func ExpectedDeploymentVolumes(pod *egv1a1.KubernetesPodSpec, volumes []corev1.Volume) []corev1.Volume { + amendFunc := func(volume corev1.Volume) { + for index, e := range volumes { + if e.Name == volume.Name { + volumes[index] = volume + return + } + } + + volumes = append(volumes, volume) + } + + for _, envVar := range pod.Volumes { + amendFunc(envVar) + } + + return volumes +} + +// ExpectedContainerVolumeMounts returns expected container volume mounts. +func ExpectedContainerVolumeMounts(container *egv1a1.KubernetesContainerSpec, volumeMounts []corev1.VolumeMount) []corev1.VolumeMount { + amendFunc := func(volumeMount corev1.VolumeMount) { + for index, e := range volumeMounts { + if e.Name == volumeMount.Name { + volumeMounts[index] = volumeMount + return + } + } + + volumeMounts = append(volumeMounts, volumeMount) + } + + for _, volumeMount := range container.VolumeMounts { + amendFunc(volumeMount) + } + + return volumeMounts +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource_test.go b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource_test.go new file mode 100644 index 0000000000..6194590006 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/kubernetes/resource/resource_test.go @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package resource + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" +) + +func TestExpectedServiceSpec(t *testing.T) { + type args struct { + service *egv1a1.KubernetesServiceSpec + } + loadbalancerClass := "foobar" + tests := []struct { + name string + args args + want corev1.ServiceSpec + }{ + { + name: "LoadBalancer", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "LoadBalancerWithExternalTrafficPolicyCluster", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + ExternalTrafficPolicy: egv1a1.GetKubernetesServiceExternalTrafficPolicy(egv1a1.ServiceExternalTrafficPolicyCluster), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeCluster, + }, + }, + { + name: "LoadBalancerWithClass", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerClass: &loadbalancerClass, + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + LoadBalancerClass: &loadbalancerClass, + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "LoadBalancerWithAllocateLoadBalancerNodePorts", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + AllocateLoadBalancerNodePorts: ptr.To(true), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + AllocateLoadBalancerNodePorts: ptr.To(true), + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "LoadBalancerWithLoadBalancerIP", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeLoadBalancer), + LoadBalancerIP: ptr.To("10.11.12.13"), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + LoadBalancerIP: "10.11.12.13", + SessionAffinity: corev1.ServiceAffinityNone, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal, + }, + }, + { + name: "ClusterIP", + args: args{service: &egv1a1.KubernetesServiceSpec{ + Type: egv1a1.GetKubernetesServiceType(egv1a1.ServiceTypeClusterIP), + }}, + want: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + SessionAffinity: corev1.ServiceAffinityNone, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ExpectedServiceSpec(tt.args.service), "expectedServiceSpec(%v)", tt.args.service) + }) + } +} + +func TestGetSelector(t *testing.T) { + cases := []struct { + name string + in map[string]string + expected map[string]string + }{ + { + name: "proxy", + in: map[string]string{ + "app.kubernetes.io/name": "envoy", + "app.kubernetes.io/component": "proxy", + "app.kubernetes.io/managed-by": "envoy-gateway", + }, + expected: map[string]string{ + "app.kubernetes.io/name": "envoy", + "app.kubernetes.io/component": "proxy", + "app.kubernetes.io/managed-by": "envoy-gateway", + }, + }, + { + name: "ratelimit", + in: map[string]string{ + "app.kubernetes.io/name": "envoy-ratelimit", + "app.kubernetes.io/component": "ratelimit", + "app.kubernetes.io/managed-by": "envoy-gateway", + }, + expected: map[string]string{ + "app.kubernetes.io/name": "envoy-ratelimit", + "app.kubernetes.io/component": "ratelimit", + "app.kubernetes.io/managed-by": "envoy-gateway", + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run("", func(t *testing.T) { + got := GetSelector(tc.in) + require.Equal(t, tc.expected, got.MatchLabels) + }) + } +} + +func TestCompareSvc(t *testing.T) { + cases := []struct { + ExpectRet bool + NewSvc *corev1.Service + OriginalSvc *corev1.Service + }{ + { + // Only Spec.Ports[*].NodePort and ClusterType is different + ExpectRet: true, + NewSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + NodePort: 30000, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "NodePort", + }, + }, + OriginalSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + NodePort: 30001, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "NodePort", + }, + }, + }, { + // Only Spec.Ports[*].Port is different + ExpectRet: false, + NewSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + OriginalSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 90, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + }, + { + // only Spec.ClusterIP is different, NewSvc's ClusterIP is nil build by ResourceRender + ExpectRet: true, + NewSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "", + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + OriginalSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "192.168.1.1", + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + }, { + // Finalizers field differs + ExpectRet: true, + NewSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + Finalizers: []string{"service.k8s.aws/resources"}, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + OriginalSvc: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-service", + Namespace: "default", + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: map[string]string{ + "app": "my-app", + }, + Type: "ClusterIP", + }, + }, + }, + } + + for _, tc := range cases { + t.Run("", func(t *testing.T) { + assert.Equal(t, tc.ExpectRet, CompareSvc(tc.NewSvc, tc.OriginalSvc), "expectedCompareSvcReturn(%v)", tc.ExpectRet) + }) + } +} + +func TestExpectedProxyContainerEnv(t *testing.T) { + type args struct { + container *egv1a1.KubernetesContainerSpec + env []corev1.EnvVar + } + tests := []struct { + name string + args args + want []corev1.EnvVar + }{ + { + name: "TestExpectedProxyContainerEnv", + args: args{ + container: &egv1a1.KubernetesContainerSpec{ + Env: []corev1.EnvVar{ + { + Name: "env_a", + Value: "override_env_a_value", + }, + { + Name: "env_b", + Value: "override_env_a_value", + }, + { + Name: "env_c", + Value: "new_env_c_value", + }, + }, + }, + env: []corev1.EnvVar{ + { + Name: "env_a", + Value: "default_env_a_value", + }, + { + Name: "env_b", + Value: "default_env_a_value", + }, + { + Name: "default_env", + Value: "default_env_value", + }, + }, + }, + want: []corev1.EnvVar{ + { + Name: "env_a", + Value: "override_env_a_value", + }, + { + Name: "env_b", + Value: "override_env_a_value", + }, + { + Name: "default_env", + Value: "default_env_value", + }, + { + Name: "env_c", + Value: "new_env_c_value", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ExpectedContainerEnv(tt.args.container, tt.args.env), "ExpectedProxyContainerEnv(%v, %v)", tt.args.container, tt.args.env) + }) + } +} + +func TestExpectedDeploymentVolumes(t *testing.T) { + type args struct { + pod *egv1a1.KubernetesPodSpec + volumes []corev1.Volume + } + tests := []struct { + name string + args args + want []corev1.Volume + }{ + { + name: "TestExpectedDeploymentVolumes", + args: args{ + pod: &egv1a1.KubernetesPodSpec{ + Volumes: []corev1.Volume{ + { + Name: "certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "override-cert", + }, + }, + }, + }, + }, + volumes: []corev1.Volume{ + { + Name: "certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "cert", + }, + }, + }, + { + Name: "default-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "default-cert", + }, + }, + }, + }, + }, + want: []corev1.Volume{ + { + Name: "certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "override-cert", + }, + }, + }, + { + Name: "default-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "default-cert", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ExpectedDeploymentVolumes(tt.args.pod, tt.args.volumes), "ExpectedDeploymentVolumes(%v, %v)", tt.args.pod, tt.args.volumes) + }) + } +} + +func TestExpectedContainerVolumeMounts(t *testing.T) { + type args struct { + container *egv1a1.KubernetesContainerSpec + volumeMounts []corev1.VolumeMount + } + tests := []struct { + name string + args args + want []corev1.VolumeMount + }{ + { + name: "TestExpectedContainerVolumeMounts", + args: args{ + container: &egv1a1.KubernetesContainerSpec{ + VolumeMounts: []corev1.VolumeMount{ + { + Name: "certs", + MountPath: "/override-certs", + ReadOnly: true, + }, + }, + }, + volumeMounts: []corev1.VolumeMount{ + { + Name: "certs", + MountPath: "/certs", + ReadOnly: true, + }, + { + Name: "default-certs", + MountPath: "/default-certs", + ReadOnly: true, + }, + }, + }, + want: []corev1.VolumeMount{ + { + Name: "certs", + MountPath: "/override-certs", + ReadOnly: true, + }, + { + Name: "default-certs", + MountPath: "/default-certs", + ReadOnly: true, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ExpectedContainerVolumeMounts(tt.args.container, tt.args.volumeMounts), "ExpectedContainerVolumeMounts(%v, %v)", tt.args.container, tt.args.volumeMounts) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/infrastructure/manager.go b/adapter/internal/operator/gateway-api/infrastructure/manager.go new file mode 100644 index 0000000000..40e902b101 --- /dev/null +++ b/adapter/internal/operator/gateway-api/infrastructure/manager.go @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package infrastructure + +import ( + "context" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "sigs.k8s.io/controller-runtime/pkg/client" + clicfg "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +var _ Manager = (*kubernetes.Infra)(nil) + +// Manager provides the scaffolding for managing infrastructure. +type Manager interface { + // CreateOrUpdateProxyInfra creates or updates infra. + CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error + // DeleteProxyInfra deletes infra. + DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error + // // CreateOrUpdateRateLimitInfra creates or updates rate limit infra. + // CreateOrUpdateRateLimitInfra(ctx context.Context) error + // DeleteRateLimitInfra deletes rate limit infra. + // DeleteRateLimitInfra(ctx context.Context) error +} + +// NewManager returns a new infrastructure Manager. +func NewManager() (Manager, error) { + var mgr Manager + cli, err := client.New(clicfg.GetConfigOrDie(), client.Options{}) + if err != nil { + return nil, err + } + mgr = kubernetes.NewInfra(cli) + return mgr, nil +} diff --git a/adapter/internal/operator/gateway-api/ir/infra.go b/adapter/internal/operator/gateway-api/ir/infra.go new file mode 100644 index 0000000000..f679e68ec5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/infra.go @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package ir + +import ( + "cmp" + "errors" + "fmt" + "reflect" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + "golang.org/x/exp/slices" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "sigs.k8s.io/yaml" +) + +const ( + DefaultProxyName = "default" +) + +// Infra defines managed infrastructure. +// +k8s:deepcopy-gen=true +type Infra struct { + // Proxy defines managed proxy infrastructure. + Proxy *ProxyInfra `json:"proxy" yaml:"proxy"` +} + +func (i Infra) YAMLString() string { + y, _ := yaml.Marshal(&i) + return string(y) +} + +// ProxyInfra defines managed proxy infrastructure. +// +k8s:deepcopy-gen=true +type ProxyInfra struct { + // Metadata defines metadata for the managed proxy infrastructure. + Metadata *InfraMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` + // Name is the name used for managed proxy infrastructure. + Name string `json:"name" yaml:"name"` + // Config defines user-facing configuration of the managed proxy infrastructure. + Config *v1alpha1.EnvoyProxy `json:"config,omitempty" yaml:"config,omitempty"` + // Listeners define the listeners exposed by the proxy infrastructure. + Listeners []*ProxyListener `json:"listeners,omitempty" yaml:"listeners,omitempty"` + // Addresses contain the external addresses this gateway has been + // requested to be available at. + Addresses []string `json:"addresses,omitempty" yaml:"addresses,omitempty"` +} + +// InfraMetadata defines metadata for the managed proxy infrastructure. +// +k8s:deepcopy-gen=true +type InfraMetadata struct { + // Annotations define a map of string keys and values that can be used to + // organize and categorize proxy infrastructure objects. + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` + // Labels define a map of string keys and values that can be used to organize + // and categorize proxy infrastructure objects. + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` +} + +// ProxyListener defines the listener configuration of the proxy infrastructure. +// +k8s:deepcopy-gen=true +type ProxyListener struct { + // Name of the ProxyListener + Name string `json:"name" yaml:"name"` + // Address is the address that the listener should listen on. + Address *string `json:"address" yaml:"address"` + // Ports define network ports of the listener. + Ports []ListenerPort `json:"ports,omitempty" yaml:"ports,omitempty"` + // HTTP3 provides HTTP/3 configuration on the listener. + // +optional + HTTP3 *HTTP3Settings `json:"http3,omitempty"` +} + +// HTTP3Settings provides HTTP/3 configuration on the listener. +type HTTP3Settings struct { + QUICPort int32 `json:"quicPort" yaml:"quicPort"` +} + +// ListenerPort defines a network port of a listener. +// +k8s:deepcopy-gen=true +type ListenerPort struct { + // Name is the name of the listener port. + Name string `json:"name" yaml:"name"` + // Protocol is the protocol that the listener port will listener for. + Protocol ProtocolType `json:"protocol" yaml:"protocol"` + // ServicePort is the port number the proxy service is listening on. + ServicePort int32 `json:"servicePort" yaml:"servicePort"` + // ContainerPort is the port number the proxy container is listening on. + ContainerPort int32 `json:"containerPort" yaml:"containerPort"` +} + +// ProtocolType defines the application protocol accepted by a ListenerPort. +// +// Valid values include "HTTP" and "HTTPS". +type ProtocolType string + +const ( + // HTTPProtocolType accepts cleartext HTTP/1.1 sessions over TCP or HTTP/2 + // over cleartext. + HTTPProtocolType ProtocolType = "HTTP" + + // HTTPSProtocolType accepts HTTP/1.1 or HTTP/2 sessions over TLS. + HTTPSProtocolType ProtocolType = "HTTPS" + + // TLSProtocolType accepts TLS sessions over TCP. + TLSProtocolType ProtocolType = "TLS" + + // TCPProtocolType accepts TCP connection. + TCPProtocolType ProtocolType = "TCP" + + // UDPProtocolType accepts UDP connection. + UDPProtocolType ProtocolType = "UDP" +) + +// NewInfra returns a new Infra with default parameters. +func NewInfra() *Infra { + return &Infra{ + Proxy: NewProxyInfra(), + } +} + +// NewProxyInfra returns a new ProxyInfra with default parameters. +func NewProxyInfra() *ProxyInfra { + return &ProxyInfra{ + Metadata: NewInfraMetadata(), + Name: DefaultProxyName, + } +} + +// NewProxyListeners returns a new slice of ProxyListener with default parameters. +func NewProxyListeners() []*ProxyListener { + return []*ProxyListener{ + { + Ports: nil, + }, + } +} + +// NewInfraMetadata returns a new InfraMetadata. +func NewInfraMetadata() *InfraMetadata { + return &InfraMetadata{ + Labels: map[string]string{}, + } +} + +// GetProxyInfra returns the ProxyInfra. +func (i *Infra) GetProxyInfra() *ProxyInfra { + if i.Proxy == nil { + i.Proxy = NewProxyInfra() + return i.Proxy + } + if len(i.Proxy.Name) == 0 { + i.Proxy.Name = DefaultProxyName + } + if len(i.Proxy.Listeners) == 0 { + i.Proxy.Listeners = NewProxyListeners() + } + if i.Proxy.Metadata == nil { + i.Proxy.Metadata = NewInfraMetadata() + } + + return i.Proxy +} + +// GetProxyMetadata returns the InfraMetadata. +func (p *ProxyInfra) GetProxyMetadata() *InfraMetadata { + if p.Metadata == nil { + p.Metadata = NewInfraMetadata() + } + + return p.Metadata +} + +// GetProxyConfig returns the ProxyInfra config. +func (p *ProxyInfra) GetProxyConfig() *v1alpha1.EnvoyProxy { + if p.Config == nil { + p.Config = new(v1alpha1.EnvoyProxy) + } + + return p.Config +} + +// Validate validates the provided Infra. +func (i *Infra) Validate() error { + if i == nil { + return errors.New("infra ir is nil") + } + + var errs []error + + if i.Proxy != nil { + if err := i.Proxy.Validate(); err != nil { + errs = append(errs, err) + } + } + + return utilerrors.NewAggregate(errs) +} + +// Validate validates the provided ProxyInfra. +func (p *ProxyInfra) Validate() error { + var errs []error + + if len(p.Name) == 0 { + errs = append(errs, errors.New("name field required")) + } + + if len(p.Listeners) > 0 { + for i := range p.Listeners { + listener := p.Listeners[i] + if len(listener.Ports) == 0 { + errs = append(errs, errors.New("listener ports field required")) + } + for j := range listener.Ports { + if len(listener.Ports[j].Name) == 0 { + errs = append(errs, errors.New("listener name field required")) + } + if listener.Ports[j].ServicePort < 1 || listener.Ports[j].ServicePort > 65353 { + errs = append(errs, errors.New("listener service port must be a valid port number")) + } + if listener.Ports[j].ContainerPort < 1024 || listener.Ports[j].ContainerPort > 65353 { + errs = append(errs, errors.New("listener container port must be a valid ephemeral port number")) + } + } + } + } + + return utilerrors.NewAggregate(errs) +} + +// ObjectName returns the name of the proxy infrastructure object. +func (p *ProxyInfra) ObjectName() string { + if len(p.Name) == 0 { + return fmt.Sprintf("envoy-%s", DefaultProxyName) + } + return "envoy-" + p.Name +} + +// Equal implements the Comparable interface used by watchable.DeepEqual to skip unnecessary updates. +func (p *ProxyInfra) Equal(y *ProxyInfra) bool { + // Deep copy to avoid modifying the original ordering. + p = p.DeepCopy() + p.sort() + y = y.DeepCopy() + y.sort() + return reflect.DeepEqual(p, y) +} + +// sort ensures the listeners are in a consistent order. +func (p *ProxyInfra) sort() { + slices.SortFunc(p.Listeners, func(l1, l2 *ProxyListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) +} diff --git a/adapter/internal/operator/gateway-api/ir/infra_test.go b/adapter/internal/operator/gateway-api/ir/infra_test.go new file mode 100644 index 0000000000..d87e2836d7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/infra_test.go @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package ir + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" +) + +func TestValidateInfra(t *testing.T) { + testCases := []struct { + name string + infra *Infra + expect bool + }{ + { + name: "default", + infra: NewInfra(), + expect: true, + }, + { + name: "no-name", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "", + Listeners: NewProxyListeners(), + }, + }, + expect: false, + }, + { + name: "no-listeners", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "test", + }, + }, + expect: true, + }, + { + name: "no-listener-ports", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "test", + Listeners: []*ProxyListener{ + { + Ports: []ListenerPort{}, + }, + }, + }, + }, + expect: false, + }, + { + name: "no-listener-port-name", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "test", + Listeners: []*ProxyListener{ + { + Ports: []ListenerPort{ + { + ServicePort: int32(80), + ContainerPort: int32(8080), + }, + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "no-listener-service-port-number", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "test", + Listeners: []*ProxyListener{ + { + Ports: []ListenerPort{ + { + Name: "http", + ContainerPort: int32(8080), + }, + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "no-listener-container-port-number", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "test", + Listeners: []*ProxyListener{ + { + Ports: []ListenerPort{ + { + Name: "http", + ServicePort: int32(80), + }, + }, + }, + }, + }, + }, + expect: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.infra.Validate() + if !tc.expect { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestNewInfra(t *testing.T) { + testCases := []struct { + name string + expected *Infra + }{ + { + name: "default infra", + expected: &Infra{ + Proxy: NewProxyInfra(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual := NewInfra() + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestNewProxyInfra(t *testing.T) { + testCases := []struct { + name string + expected *ProxyInfra + }{ + { + name: "default infra", + expected: &ProxyInfra{ + Metadata: NewInfraMetadata(), + Name: DefaultProxyName, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual := NewProxyInfra() + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestObjectName(t *testing.T) { + defaultInfra := NewInfra() + + testCases := []struct { + name string + infra *Infra + expected string + }{ + { + name: "default infra", + infra: defaultInfra, + expected: "envoy-default", + }, + { + name: "defined infra", + infra: &Infra{ + Proxy: &ProxyInfra{ + Name: "foo", + }, + }, + expected: "envoy-foo", + }, + { + name: "unspecified infra name", + infra: &Infra{ + Proxy: &ProxyInfra{}, + }, + expected: "envoy-default", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual := tc.infra.Proxy.ObjectName() + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestEqualInfra(t *testing.T) { + tests := []struct { + desc string + a *ProxyInfra + b *ProxyInfra + equal bool + }{ + { + desc: "out of order proxy listeners are equal", + a: &ProxyInfra{ + Listeners: []*ProxyListener{ + {Name: "listener-1"}, + {Name: "listener-2"}, + }, + }, + b: &ProxyInfra{ + Listeners: []*ProxyListener{ + {Name: "listener-2"}, + {Name: "listener-1"}, + }, + }, + equal: true, + }, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + require.Equal(t, tc.equal, cmp.Equal(tc.a, tc.b)) + }) + } +} diff --git a/adapter/internal/operator/gateway-api/ir/protocol.go b/adapter/internal/operator/gateway-api/ir/protocol.go new file mode 100644 index 0000000000..8847ab54a9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/protocol.go @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package ir + +type AppProtocol string + +const ( + // GRPC declares that the port carries gRPC traffic. + GRPC AppProtocol = "GRPC" + // GRPCWeb declares that the port carries gRPC traffic. + GRPCWeb AppProtocol = "GRPC-Web" + // HTTP declares that the port carries HTTP/1.1 traffic. + // Note that HTTP/1.0 or earlier may not be supported by the proxy. + HTTP AppProtocol = "HTTP" + // HTTP2 declares that the port carries HTTP/2 traffic. + HTTP2 AppProtocol = "HTTP2" + // HTTPS declares that the port carries HTTPS traffic. + HTTPS AppProtocol = "HTTPS" + // TCP declares the port uses TCP. + // This is the default protocol for a service port. + TCP AppProtocol = "TCP" + // UDP declares that the port uses UDP. + UDP AppProtocol = "UDP" +) diff --git a/adapter/internal/operator/gateway-api/ir/xds.go b/adapter/internal/operator/gateway-api/ir/xds.go new file mode 100644 index 0000000000..f9cc355592 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/xds.go @@ -0,0 +1,1749 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package ir + +import ( + "cmp" + "errors" + "net/http" + "net/netip" + "reflect" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/types" + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + "golang.org/x/exp/slices" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + "sigs.k8s.io/yaml" +) + +var ( + ErrListenerNameEmpty = errors.New("field Name must be specified") + ErrListenerAddressInvalid = errors.New("field Address must be a valid IP address") + ErrListenerPortInvalid = errors.New("field Port specified is invalid") + ErrHTTPListenerHostnamesEmpty = errors.New("field Hostnames must be specified with at least a single hostname entry") + ErrTCPListenerSNIsEmpty = errors.New("field SNIs must be specified with at least a single server name entry") + ErrTLSServerCertEmpty = errors.New("field ServerCertificate must be specified") + ErrTLSPrivateKey = errors.New("field PrivateKey must be specified") + ErrHTTPRouteNameEmpty = errors.New("field Name must be specified") + ErrHTTPRouteHostnameEmpty = errors.New("field Hostname must be specified") + ErrDestinationNameEmpty = errors.New("field Name must be specified") + ErrDestEndpointHostInvalid = errors.New("field Address must be a valid IP or FQDN address") + ErrDestEndpointPortInvalid = errors.New("field Port specified is invalid") + ErrStringMatchConditionInvalid = errors.New("only one of the Exact, Prefix, SafeRegex or Distinct fields must be set") + ErrStringMatchNameIsEmpty = errors.New("field Name must be specified") + ErrDirectResponseStatusInvalid = errors.New("only HTTP status codes 100 - 599 are supported for DirectResponse") + ErrRedirectUnsupportedStatus = errors.New("only HTTP status codes 301 and 302 are supported for redirect filters") + ErrRedirectUnsupportedScheme = errors.New("only http and https are supported for the scheme in redirect filters") + ErrHTTPPathModifierDoubleReplace = errors.New("redirect filter cannot have a path modifier that supplies both fullPathReplace and prefixMatchReplace") + ErrHTTPPathModifierNoReplace = errors.New("redirect filter cannot have a path modifier that does not supply either fullPathReplace or prefixMatchReplace") + ErrAddHeaderEmptyName = errors.New("header modifier filter cannot configure a header without a name to be added") + ErrAddHeaderDuplicate = errors.New("header modifier filter attempts to add the same header more than once (case insensitive)") + ErrRemoveHeaderDuplicate = errors.New("header modifier filter attempts to remove the same header more than once (case insensitive)") + ErrLoadBalancerInvalid = errors.New("loadBalancer setting is invalid, only one setting can be set") + ErrHealthCheckTimeoutInvalid = errors.New("field HealthCheck.Timeout must be specified") + ErrHealthCheckIntervalInvalid = errors.New("field HealthCheck.Interval must be specified") + ErrHealthCheckUnhealthyThresholdInvalid = errors.New("field HealthCheck.UnhealthyThreshold should be greater than 0") + ErrHealthCheckHealthyThresholdInvalid = errors.New("field HealthCheck.HealthyThreshold should be greater than 0") + ErrHealthCheckerInvalid = errors.New("health checker setting is invalid, only one health checker can be set") + ErrHCHTTPHostInvalid = errors.New("field HTTPHealthChecker.Host should be specified") + ErrHCHTTPPathInvalid = errors.New("field HTTPHealthChecker.Path should be specified") + ErrHCHTTPMethodInvalid = errors.New("only one of the GET, HEAD, POST, DELETE, OPTIONS, TRACE, PATCH of HTTPHealthChecker.Method could be set") + ErrHCHTTPExpectedStatusesInvalid = errors.New("field HTTPHealthChecker.ExpectedStatuses should be specified") + ErrHealthCheckPayloadInvalid = errors.New("one of Text, Binary fields must be set in payload") + ErrHTTPStatusInvalid = errors.New("HTTPStatus should be in [200,600)") + ErrOutlierDetectionBaseEjectionTimeInvalid = errors.New("field OutlierDetection.BaseEjectionTime must be specified") + ErrOutlierDetectionIntervalInvalid = errors.New("field OutlierDetection.Interval must be specified") + + redacted = []byte("[redacted]") +) + +// Xds holds the intermediate representation of a Gateway and is +// used by the xDS Translator to convert it into xDS resources. +// +k8s:deepcopy-gen=true +type Xds struct { + // AccessLog configuration for the gateway. + AccessLog *AccessLog `json:"accessLog,omitempty" yaml:"accessLog,omitempty"` + // Tracing configuration for the gateway. + Tracing *Tracing `json:"tracing,omitempty" yaml:"tracing,omitempty"` + // Metrics configuration for the gateway. + Metrics *Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty"` + // HTTP listeners exposed by the gateway. + HTTP []*HTTPListener `json:"http,omitempty" yaml:"http,omitempty"` + // TCP Listeners exposed by the gateway. + TCP []*TCPListener `json:"tcp,omitempty" yaml:"tcp,omitempty"` + // UDP Listeners exposed by the gateway. + UDP []*UDPListener `json:"udp,omitempty" yaml:"udp,omitempty"` + // EnvoyPatchPolicies is the intermediate representation of the EnvoyPatchPolicy resource + // EnvoyPatchPolicies []*EnvoyPatchPolicy `json:"envoyPatchPolicies,omitempty" yaml:"envoyPatchPolicies,omitempty"` +} + +// Equal implements the Comparable interface used by watchable.DeepEqual to skip unnecessary updates. +func (x *Xds) Equal(y *Xds) bool { + // Deep copy to avoid modifying the original ordering. + x = x.DeepCopy() + x.sort() + y = y.DeepCopy() + y.sort() + return reflect.DeepEqual(x, y) +} + +// sort ensures the listeners are in a consistent order. +func (x *Xds) sort() { + slices.SortFunc(x.HTTP, func(l1, l2 *HTTPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) + for _, l := range x.HTTP { + slices.SortFunc(l.Routes, func(r1, r2 *HTTPRoute) int { + return cmp.Compare(r1.Name, r2.Name) + }) + } + slices.SortFunc(x.TCP, func(l1, l2 *TCPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) + slices.SortFunc(x.UDP, func(l1, l2 *UDPListener) int { + return cmp.Compare(l1.Name, l2.Name) + }) +} + +// Validate the fields within the Xds structure. +func (x Xds) Validate() error { + var errs error + for _, http := range x.HTTP { + if err := http.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + for _, tcp := range x.TCP { + if err := tcp.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + for _, udp := range x.UDP { + if err := udp.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + return errs +} + +func (x Xds) GetHTTPListener(name string) *HTTPListener { + for _, listener := range x.HTTP { + if listener.Name == name { + return listener + } + } + return nil +} + +func (x Xds) GetTCPListener(name string) *TCPListener { + for _, listener := range x.TCP { + if listener.Name == name { + return listener + } + } + return nil +} + +func (x Xds) GetUDPListener(name string) *UDPListener { + for _, listener := range x.UDP { + if listener.Name == name { + return listener + } + } + return nil +} + +func (x Xds) YAMLString() string { + y, _ := yaml.Marshal(x.Printable()) + return string(y) +} + +// Printable returns a deep copy of the resource that can be safely logged. +func (x Xds) Printable() *Xds { + out := x.DeepCopy() + for _, listener := range out.HTTP { + // Omit field + if listener.TLS != nil { + for i := range listener.TLS.Certificates { + listener.TLS.Certificates[i].PrivateKey = redacted + } + } + + for _, route := range listener.Routes { + // Omit field + if route.OIDC != nil { + route.OIDC.ClientSecret = redacted + route.OIDC.HMACSecret = redacted + } + if route.BasicAuth != nil { + route.BasicAuth.Users = redacted + } + } + } + return out +} + +// HTTPListener holds the listener configuration. +// +k8s:deepcopy-gen=true +type HTTPListener struct { + // Name of the HttpListener + Name string `json:"name" yaml:"name"` + // Address that the listener should listen on. + Address string `json:"address" yaml:"address"` + // Port on which the service can be expected to be accessed by clients. + Port uint32 `json:"port" yaml:"port"` + // Hostnames (Host/Authority header value) with which the service can be expected to be accessed by clients. + // This field is required. Wildcard hosts are supported in the suffix or prefix form. + // Refer to https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost + // for more info. + Hostnames []string `json:"hostnames" yaml:"hostnames"` + // Tls configuration. If omitted, the gateway will expose a plain text HTTP server. + TLS *TLSConfig `json:"tls,omitempty" yaml:"tls,omitempty"` + // Routes associated with HTTP traffic to the service. + Routes []*HTTPRoute `json:"routes,omitempty" yaml:"routes,omitempty"` + // IsHTTP2 is set if the listener is configured to serve HTTP2 traffic, + // grpc-web and grpc-stats are also enabled if this is set. + IsHTTP2 bool `json:"isHTTP2" yaml:"isHTTP2"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` + // Headers configures special header management for the listener + Headers *HeaderSettings `json:"headers,omitempty" yaml:"headers,omitempty"` + // EnableProxyProtocol enables the listener to interpret proxy protocol header + EnableProxyProtocol bool `json:"enableProxyProtocol,omitempty" yaml:"enableProxyProtocol,omitempty"` + // ClientIPDetection controls how the original client IP address is determined for requests. + // ClientIPDetection *ClientIPDetectionSettings `json:"clientIPDetection,omitempty" yaml:"clientIPDetection,omitempty"` + // HTTP3 provides HTTP/3 configuration on the listener. + // +optional + HTTP3 *HTTP3Settings `json:"http3,omitempty"` + // Path contains settings for path URI manipulations + Path PathSettings `json:"path,omitempty"` + // HTTP1 provides HTTP/1 configuration on the listener + // +optional + HTTP1 *HTTP1Settings `json:"http1,omitempty" yaml:"http1,omitempty"` + // ClientTimeout sets the timeout configuration for downstream connections + Timeout *ClientTimeout `json:"timeout,omitempty" yaml:"clientTimeout,omitempty"` +} + +// Validate the fields within the HTTPListener structure +func (h HTTPListener) Validate() error { + var errs error + if h.Name == "" { + errs = errors.Join(errs, ErrListenerNameEmpty) + } + if _, err := netip.ParseAddr(h.Address); err != nil { + errs = errors.Join(errs, ErrListenerAddressInvalid) + } + if h.Port == 0 { + errs = errors.Join(errs, ErrListenerPortInvalid) + } + if len(h.Hostnames) == 0 { + errs = errors.Join(errs, ErrHTTPListenerHostnamesEmpty) + } + if h.TLS != nil { + if err := h.TLS.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + for _, route := range h.Routes { + if err := route.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + return errs +} + +type TLSVersion types.TLSVersion + +const ( + // TLSAuto allows Envoy to choose the optimal TLS Version + TLSAuto = TLSVersion(types.TLSAuto) + // TLSv10 specifies TLS version 1.0 + TLSv10 = TLSVersion(types.TLSv10) + // TLSv11 specifies TLS version 1.1 + TLSv11 = TLSVersion(types.TLSv11) + // TLSv12 specifies TLS version 1.2 + TLSv12 = TLSVersion(types.TLSv12) + // TLSv13 specifies TLS version 1.3 + TLSv13 = TLSVersion(types.TLSv13) +) + +// TLSConfig holds the configuration for downstream TLS context. +// +k8s:deepcopy-gen=true +type TLSConfig struct { + // Certificates contains the set of certificates associated with this listener + Certificates []TLSCertificate `json:"certificates,omitempty" yaml:"certificates,omitempty"` + // CACertificate to verify the client + CACertificate *TLSCACertificate `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty"` + // MinVersion defines the minimal version of the TLS protocol supported by this listener. + MinVersion *TLSVersion `json:"minVersion,omitempty" yaml:"version,omitempty"` + // MaxVersion defines the maximal version of the TLS protocol supported by this listener. + MaxVersion *TLSVersion `json:"maxVersion,omitempty" yaml:"version,omitempty"` + // CipherSuites supported by this listener + Ciphers []string `json:"ciphers,omitempty" yaml:"ciphers,omitempty"` + // EDCHCurves supported by this listener + ECDHCurves []string `json:"ecdhCurves,omitempty" yaml:"ecdhCurves,omitempty"` + // SignatureAlgorithms supported by this listener + SignatureAlgorithms []string `json:"signatureAlgorithms,omitempty" yaml:"signatureAlgorithms,omitempty"` + // ALPNProtocols exposed by this listener + ALPNProtocols []string `json:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty"` +} + +// TLSCertificate holds a single certificate's details +// +k8s:deepcopy-gen=true +type TLSCertificate struct { + // Name of the Secret object. + Name string `json:"name" yaml:"name"` + // ServerCertificate of the server. + ServerCertificate []byte `json:"serverCertificate,omitempty" yaml:"serverCertificate,omitempty"` + // PrivateKey for the server. + PrivateKey []byte `json:"privateKey,omitempty" yaml:"privateKey,omitempty"` +} + +// TLSCACertificate holds CA Certificate to validate clients +// +k8s:deepcopy-gen=true +type TLSCACertificate struct { + // Name of the Secret object. + Name string `json:"name,omitempty" yaml:"name,omitempty"` + // Certificate content. + Certificate []byte `json:"certificate,omitempty" yaml:"certificate,omitempty"` +} + +func (t TLSCertificate) Validate() error { + var errs error + if len(t.ServerCertificate) == 0 { + errs = errors.Join(errs, ErrTLSServerCertEmpty) + } + if len(t.PrivateKey) == 0 { + errs = errors.Join(errs, ErrTLSPrivateKey) + } + return errs +} + +// Validate the fields within the TLSListenerConfig structure +func (t TLSConfig) Validate() error { + var errs error + for _, cert := range t.Certificates { + if err := cert.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + // Correct values for cipher suites, ECDH curves, and signature algorithms are + // dependent on the version of EnvoyProxy being used - different values are valid + // depending if Envoy was compiled against BoringSSL or OpenSSL, or even the exact version + // of each of these libraries. + // Validation for TLS versions was done with CEL + return errs +} + +// PathSettings holds configuration for path URI manipulations +// +k8s:deepcopy-gen=true +type PathSettings struct { + MergeSlashes bool `json:"mergeSlashes" yaml:"mergeSlashes"` + // EscapedSlashesAction PathEscapedSlashAction `json:"escapedSlashesAction" yaml:"escapedSlashesAction"` +} + +// ClientIPDetectionSettings provides configuration for determining the original client IP address for requests. +// +k8s:deepcopy-gen=true +// type ClientIPDetectionSettings egv1a1.ClientIPDetectionSettings + +// BackendWeights stores the weights of valid and invalid backends for the route so that 500 error responses can be returned in the same proportions +type BackendWeights struct { + Valid uint32 `json:"valid" yaml:"valid"` + Invalid uint32 `json:"invalid" yaml:"invalid"` +} + +// HTTP1Settings provides HTTP/1 configuration on the listener. +// +k8s:deepcopy-gen=true +type HTTP1Settings struct { + EnableTrailers bool `json:"enableTrailers,omitempty" yaml:"enableTrailers,omitempty"` + PreserveHeaderCase bool `json:"preserveHeaderCase,omitempty" yaml:"preserveHeaderCase,omitempty"` + HTTP10 *HTTP10Settings `json:"http10,omitempty" yaml:"http10,omitempty"` +} + +// HTTP10Settings provides HTTP/1.0 configuration on the listener. +// +k8s:deepcopy-gen=true +type HTTP10Settings struct { + // defaultHost is set to the default host that should be injected for HTTP10. If the hostname shouldn't + // be set, then defaultHost will be nil + DefaultHost *string `json:"defaultHost,omitempty" yaml:"defaultHost,omitempty"` +} + +// HeaderSettings provides configuration related to header processing on the listener. +// +k8s:deepcopy-gen=true +type HeaderSettings struct { + // EnableEnvoyHeaders controls if "x-envoy-" headers are added by the HTTP Router filter. + // The default is to suppress these headers. + // Refer to https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/router/v3/router.proto#extensions-filters-http-router-v3-router + EnableEnvoyHeaders bool `json:"enableEnvoyHeaders,omitempty" yaml:"enableEnvoyHeaders,omitempty"` +} + +// ClientTimeout sets the timeout configuration for downstream connections +// +k8s:deepcopy-gen=true +type ClientTimeout struct { + // Timeout settings for HTTP. + HTTP *HTTPClientTimeout `json:"http,omitempty" yaml:"http,omitempty"` +} + +// HTTPClientTimeout set the configuration for client HTTP. +// +k8s:deepcopy-gen=true +type HTTPClientTimeout struct { + // The duration envoy waits for the complete request reception. This timer starts upon request + // initiation and stops when either the last byte of the request is sent upstream or when the response begins. + RequestReceivedTimeout *metav1.Duration `json:"requestReceivedTimeout,omitempty" yaml:"requestReceivedTimeout,omitempty"` +} + +// HTTPRoute holds the route information associated with the HTTP Route +// +k8s:deepcopy-gen=true +type HTTPRoute struct { + // Name of the HTTPRoute + Name string `json:"name" yaml:"name"` + // Hostname that the route matches against + Hostname string `json:"hostname" yaml:"hostname,omitempty"` + // IsHTTP2 is set if the route is configured to serve HTTP2 traffic + IsHTTP2 bool `json:"isHTTP2" yaml:"isHTTP2"` + // PathMatch defines the match conditions on the path. + PathMatch *StringMatch `json:"pathMatch,omitempty" yaml:"pathMatch,omitempty"` + // HeaderMatches define the match conditions on the request headers for this route. + HeaderMatches []*StringMatch `json:"headerMatches,omitempty" yaml:"headerMatches,omitempty"` + // QueryParamMatches define the match conditions on the query parameters. + QueryParamMatches []*StringMatch `json:"queryParamMatches,omitempty" yaml:"queryParamMatches,omitempty"` + // DestinationWeights stores the weights of valid and invalid backends for the route so that 500 error responses can be returned in the same proportions + BackendWeights BackendWeights `json:"backendWeights" yaml:"backendWeights"` + // AddRequestHeaders defines header/value sets to be added to the headers of requests. + AddRequestHeaders []AddHeader `json:"addRequestHeaders,omitempty" yaml:"addRequestHeaders,omitempty"` + // RemoveRequestHeaders defines a list of headers to be removed from requests. + RemoveRequestHeaders []string `json:"removeRequestHeaders,omitempty" yaml:"removeRequestHeaders,omitempty"` + // AddResponseHeaders defines header/value sets to be added to the headers of response. + AddResponseHeaders []AddHeader `json:"addResponseHeaders,omitempty" yaml:"addResponseHeaders,omitempty"` + // RemoveResponseHeaders defines a list of headers to be removed from response. + RemoveResponseHeaders []string `json:"removeResponseHeaders,omitempty" yaml:"removeResponseHeaders,omitempty"` + // Direct responses to be returned for this route. Takes precedence over Destinations and Redirect. + DirectResponse *DirectResponse `json:"directResponse,omitempty" yaml:"directResponse,omitempty"` + // Redirections to be returned for this route. Takes precedence over Destinations. + Redirect *Redirect `json:"redirect,omitempty" yaml:"redirect,omitempty"` + // Destination that requests to this HTTPRoute will be mirrored to + Mirrors []*RouteDestination `json:"mirrors,omitempty" yaml:"mirrors,omitempty"` + // Destination associated with this matched route. + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` + // Rewrite to be changed for this route. + URLRewrite *URLRewrite `json:"urlRewrite,omitempty" yaml:"urlRewrite,omitempty"` + // RateLimit defines the more specific match conditions as well as limits for ratelimiting + // the requests on this route. + RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` + // load balancer policy to use when routing to the backend endpoints. + LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` + // CORS policy for the route. + CORS *CORS `json:"cors,omitempty" yaml:"cors,omitempty"` + // JWT defines the schema for authenticating HTTP requests using JSON Web Tokens (JWT). + // JWT *JWT `json:"jwt,omitempty" yaml:"jwt,omitempty"` + // OIDC defines the schema for authenticating HTTP requests using OpenID Connect (OIDC). + OIDC *OIDC `json:"oidc,omitempty" yaml:"oidc,omitempty"` + // Proxy Protocol Settings + ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty"` + // BasicAuth defines the schema for the HTTP Basic Authentication. + BasicAuth *BasicAuth `json:"basicAuth,omitempty" yaml:"basicAuth,omitempty"` + // ExtAuth defines the schema for the external authorization. + ExtAuth *ExtAuth `json:"extAuth,omitempty" yaml:"extAuth,omitempty"` + // HealthCheck defines the configuration for health checking on the upstream. + HealthCheck *HealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"` + // FaultInjection defines the schema for injecting faults into HTTP requests. + FaultInjection *FaultInjection `json:"faultInjection,omitempty" yaml:"faultInjection,omitempty"` + // ExtensionRefs holds unstructured resources that were introduced by an extension and used on the HTTPRoute as extensionRef filters + ExtensionRefs []*UnstructuredRef `json:"extensionRefs,omitempty" yaml:"extensionRefs,omitempty"` + // Circuit Breaker Settings + CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"` + // Request and connection timeout settings + Timeout *Timeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` + // TcpKeepalive settings associated with the upstream client connection. + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` + // Retry settings + Retry *Retry `json:"retry,omitempty" yaml:"retry,omitempty"` +} + +// UnstructuredRef holds unstructured data for an arbitrary k8s resource introduced by an extension +// Envoy Gateway does not need to know about the resource types in order to store and pass the data for these objects +// to an extension. +// +// +k8s:deepcopy-gen=true +type UnstructuredRef struct { + Object *unstructured.Unstructured `json:"object,omitempty" yaml:"object,omitempty"` +} + +// CORS holds the Cross-Origin Resource Sharing (CORS) policy for the route. +// +// +k8s:deepcopy-gen=true +type CORS struct { + // AllowOrigins defines the origins that are allowed to make requests. + AllowOrigins []*StringMatch `json:"allowOrigins,omitempty" yaml:"allowOrigins,omitempty"` + // AllowMethods defines the methods that are allowed to make requests. + AllowMethods []string `json:"allowMethods,omitempty" yaml:"allowMethods,omitempty"` + // AllowHeaders defines the headers that are allowed to be sent with requests. + AllowHeaders []string `json:"allowHeaders,omitempty" yaml:"allowHeaders,omitempty"` + // ExposeHeaders defines the headers that can be exposed in the responses. + ExposeHeaders []string `json:"exposeHeaders,omitempty" yaml:"exposeHeaders,omitempty"` + // MaxAge defines how long the results of a preflight request can be cached. + MaxAge *metav1.Duration `json:"maxAge,omitempty" yaml:"maxAge,omitempty"` + // AllowCredentials indicates whether a request can include user credentials. + AllowCredentials bool `json:"allowCredentials,omitempty" yaml:"allowCredentials,omitempty"` +} + +// // JWT defines the schema for authenticating HTTP requests using +// // JSON Web Tokens (JWT). +// // +// // +k8s:deepcopy-gen=true +// type JWT struct { +// // Providers defines a list of JSON Web Token (JWT) authentication providers. +// Providers []egv1a1.JWTProvider `json:"providers,omitempty" yaml:"providers,omitempty"` +// } + +// OIDC defines the schema for authenticating HTTP requests using +// OpenID Connect (OIDC). +// +// +k8s:deepcopy-gen=true +type OIDC struct { + // The OIDC Provider configuration. + Provider OIDCProvider `json:"provider" yaml:"provider"` + + // The OIDC client ID to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + ClientID string `json:"clientID" yaml:"clientID"` + + // The OIDC client secret to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // This is an Opaque secret. The client secret should be stored in the key "client-secret". + ClientSecret []byte `json:"clientSecret,omitempty" yaml:"clientSecret,omitempty"` + + // HMACSecret is the secret used to sign the HMAC of the OAuth2 filter cookies. + HMACSecret []byte `json:"hmacSecret,omitempty" yaml:"hmacSecret,omitempty"` + + // The OIDC scopes to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty"` + + // The redirect URL to be used in the OIDC + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + RedirectURL string `json:"redirectURL,omitempty"` + + // The path part of the redirect URL + RedirectPath string `json:"redirectPath,omitempty"` + + // The path to log a user out, clearing their credential cookies. + LogoutPath string `json:"logoutPath,omitempty"` + + // CookieSuffix will be added to the name of the cookies set by the oauth filter. + // Adding a suffix avoids multiple oauth filters from overwriting each other's cookies. + // These cookies are set by the oauth filter, including: BearerToken, + // OauthHMAC, OauthExpires, IdToken, and RefreshToken. + CookieSuffix string `json:"cookieSuffix,omitempty"` +} + +type OIDCProvider struct { + // The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). + AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` + + // The OIDC Provider's [token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). + TokenEndpoint string `json:"tokenEndpoint,omitempty"` +} + +// BasicAuth defines the schema for the HTTP Basic Authentication. +// +// +k8s:deepcopy-gen=true +type BasicAuth struct { + // The username-password pairs in htpasswd format. + Users []byte `json:"users,omitempty" yaml:"users,omitempty"` +} + +// ExtAuth defines the schema for the external authorization. +// +// +k8s:deepcopy-gen=true +type ExtAuth struct { + // GRPC defines the gRPC External Authorization service. + // Only one of GRPCService or HTTPService may be specified. + GRPC *GRPCExtAuthService `json:"grpc,omitempty"` + + // HTTP defines the HTTP External Authorization service. + // Only one of GRPCService or HTTPService may be specified. + HTTP *HTTPExtAuthService `json:"http,omitempty"` + + // HeadersToExtAuth defines the client request headers that will be included + // in the request to the external authorization service. + // Note: If not specified, the default behavior for gRPC and HTTP external + // authorization services is different due to backward compatibility reasons. + // All headers will be included in the check request to a gRPC authorization server. + // Only the following headers will be included in the check request to an HTTP + // authorization server: Host, Method, Path, Content-Length, and Authorization. + // And these headers will always be included to the check request to an HTTP + // authorization server by default, no matter whether they are specified + // in HeadersToExtAuth or not. + // +optional + HeadersToExtAuth []string `json:"headersToExtAuth,omitempty"` +} + +// HTTPExtAuthService defines the HTTP External Authorization service +// +k8s:deepcopy-gen=true +type HTTPExtAuthService struct { + // Destination defines the destination for the HTTP External Authorization service. + Destination RouteDestination `json:"destination"` + + // Authority is the hostname:port of the HTTP External Authorization service. + Authority string `json:"authority"` + + // Path is the path of the HTTP External Authorization service. + // If path is not empty, the authorization request will be sent to that path, + // or else the authorization request will be sent to the root path. + Path string `json:"path"` + + // HeadersToBackend are the authorization response headers that will be added + // to the original client request before sending it to the backend server. + // Note that coexisting headers will be overridden. + // If not specified, no authorization response headers will be added to the + // original client request. + // +optional + HeadersToBackend []string `json:"headersToBackend,omitempty"` +} + +// GRPCExtAuthService defines the gRPC External Authorization service +// The authorization request message is defined in +// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto +// +k8s:deepcopy-gen=true +type GRPCExtAuthService struct { + // Destination defines the destination for the gRPC External Authorization service. + Destination RouteDestination `json:"destination"` + + // Authority is the hostname:port of the gRPC External Authorization service. + Authority string `json:"authority"` +} + +// FaultInjection defines the schema for injecting faults into requests. +// +// +k8s:deepcopy-gen=true +type FaultInjection struct { + // Delay defines the fault injection delay. + Delay *FaultInjectionDelay `json:"delay,omitempty" yaml:"delay,omitempty"` + // Abort defines the fault injection abort. + Abort *FaultInjectionAbort `json:"abort,omitempty" yaml:"abort,omitempty"` +} + +// FaultInjectionDelay defines the schema for injecting delay into requests. +// +// +k8s:deepcopy-gen=true +type FaultInjectionDelay struct { + // FixedDelay defines the fixed delay duration. + FixedDelay *metav1.Duration `json:"fixedDelay,omitempty" yaml:"fixedDelay,omitempty"` + // Percentage defines the percentage of requests to be delayed. + Percentage *float32 `json:"percentage,omitempty" yaml:"percentage,omitempty"` +} + +// FaultInjectionAbort defines the schema for injecting abort into requests. +// +// +k8s:deepcopy-gen=true +type FaultInjectionAbort struct { + // HTTPStatus defines the HTTP status code to be returned. + HTTPStatus *int32 `json:"httpStatus,omitempty" yaml:"httpStatus,omitempty"` + // GrpcStatus defines the gRPC status code to be returned. + GrpcStatus *int32 `json:"grpcStatus,omitempty" yaml:"grpcStatus,omitempty"` + // Percentage defines the percentage of requests to be aborted. + Percentage *float32 `json:"percentage,omitempty" yaml:"percentage,omitempty"` +} + +// Validate the fields within the HTTPRoute structure +func (h HTTPRoute) Validate() error { + var errs error + if h.Name == "" { + errs = errors.Join(errs, ErrHTTPRouteNameEmpty) + } + if h.Hostname == "" { + errs = errors.Join(errs, ErrHTTPRouteHostnameEmpty) + } + if h.PathMatch != nil { + if err := h.PathMatch.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + for _, hMatch := range h.HeaderMatches { + if err := hMatch.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + for _, qMatch := range h.QueryParamMatches { + if err := qMatch.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.Redirect != nil { + if err := h.Redirect.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.DirectResponse != nil { + if err := h.DirectResponse.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.URLRewrite != nil { + if err := h.URLRewrite.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.Mirrors != nil { + for _, mirror := range h.Mirrors { + if err := mirror.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + } + if len(h.AddRequestHeaders) > 0 { + occurred := sets.NewString() + for _, header := range h.AddRequestHeaders { + if err := header.Validate(); err != nil { + errs = errors.Join(errs, err) + } + if occurred.Has(header.Name) { + errs = errors.Join(errs, ErrAddHeaderDuplicate) + break + } + occurred.Insert(header.Name) + } + } + if len(h.RemoveRequestHeaders) > 0 { + occurred := sets.NewString() + for _, header := range h.RemoveRequestHeaders { + if occurred.Has(header) { + errs = errors.Join(errs, ErrRemoveHeaderDuplicate) + break + } + occurred.Insert(header) + } + } + if len(h.AddResponseHeaders) > 0 { + occurred := sets.NewString() + for _, header := range h.AddResponseHeaders { + if err := header.Validate(); err != nil { + errs = errors.Join(errs, err) + } + if occurred.Has(header.Name) { + errs = errors.Join(errs, ErrAddHeaderDuplicate) + break + } + occurred.Insert(header.Name) + } + } + if len(h.RemoveResponseHeaders) > 0 { + occurred := sets.NewString() + for _, header := range h.RemoveResponseHeaders { + if occurred.Has(header) { + errs = errors.Join(errs, ErrRemoveHeaderDuplicate) + break + } + occurred.Insert(header) + } + } + if h.LoadBalancer != nil { + if err := h.LoadBalancer.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + // if h.JWT != nil { + // if err := h.JWT.validate(); err != nil { + // errs = errors.Join(errs, err) + // } + // } + if h.HealthCheck != nil { + if err := h.HealthCheck.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// func (j *JWT) validate() error { +// var errs error + +// if err := egv1a1validation.ValidateJWTProvider(j.Providers); err != nil { +// errs = errors.Join(errs, err) +// } + +// return errs +// } + +// RouteDestination holds the destination details associated with the route +// +kubebuilder:object:generate=true +type RouteDestination struct { + // Name of the destination. This field allows the xds layer + // to check if this route destination already exists and can be + // reused + Name string `json:"name" yaml:"name"` + Settings []*DestinationSetting `json:"settings,omitempty" yaml:"settings,omitempty"` +} + +// Validate the fields within the RouteDestination structure +func (r RouteDestination) Validate() error { + var errs error + if len(r.Name) == 0 { + errs = errors.Join(errs, ErrDestinationNameEmpty) + } + for _, s := range r.Settings { + if err := s.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// DestinationSetting holds the settings associated with the destination +// +kubebuilder:object:generate=true +type DestinationSetting struct { + // Weight associated with this destination. + // Note: Weight is not used in TCP/UDP route. + Weight *uint32 `json:"weight,omitempty" yaml:"weight,omitempty"` + // Protocol associated with this destination/port. + Protocol AppProtocol `json:"protocol" yaml:"protocol"` + Endpoints []*DestinationEndpoint `json:"endpoints,omitempty" yaml:"endpoints,omitempty"` + // AddressTypeState specifies the state of DestinationEndpoint address type. + AddressType *DestinationAddressType `json:"addressType,omitempty" yaml:"addressType,omitempty"` + + TLS *TLSUpstreamConfig `json:"tls,omitempty" yaml:"tls,omitempty"` +} + +// Validate the fields within the RouteDestination structure +func (d DestinationSetting) Validate() error { + var errs error + for _, ep := range d.Endpoints { + if err := ep.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// DestinationAddressType describes the address type state for a group of DestinationEndpoint +type DestinationAddressType string + +const ( + IP DestinationAddressType = "IP" + FQDN DestinationAddressType = "FQDN" + MIXED DestinationAddressType = "Mixed" +) + +// DestinationEndpoint holds the endpoint details associated with the destination +// +kubebuilder:object:generate=true +type DestinationEndpoint struct { + // Host refers to the FQDN or IP address of the backend service. + Host string `json:"host" yaml:"host"` + // Port on the service to forward the request to. + Port uint32 `json:"port" yaml:"port"` +} + +// Validate the fields within the DestinationEndpoint structure +func (d DestinationEndpoint) Validate() error { + var errs error + + err := validation.IsDNS1123Subdomain(d.Host) + _, pErr := netip.ParseAddr(d.Host) + + if err != nil && pErr != nil { + errs = errors.Join(errs, ErrDestEndpointHostInvalid) + } + + if d.Port == 0 { + errs = errors.Join(errs, ErrDestEndpointPortInvalid) + } + + return errs +} + +// NewDestEndpoint creates a new DestinationEndpoint. +func NewDestEndpoint(host string, port uint32) *DestinationEndpoint { + return &DestinationEndpoint{ + Host: host, + Port: port, + } +} + +// AddHeader configures a header to be added to a request or response. +// +k8s:deepcopy-gen=true +type AddHeader struct { + Name string `json:"name" yaml:"name"` + Value string `json:"value" yaml:"value"` + Append bool `json:"append" yaml:"append"` +} + +// Validate the fields within the AddHeader structure +func (h AddHeader) Validate() error { + var errs error + if h.Name == "" { + errs = errors.Join(errs, ErrAddHeaderEmptyName) + } + + return errs +} + +// DirectResponse holds the details for returning a body and status code for a route. +// +k8s:deepcopy-gen=true +type DirectResponse struct { + // Body configures the body of the direct response. Currently only a string response + // is supported, but in the future a config.core.v3.DataSource may replace it. + Body *string `json:"body,omitempty" yaml:"body,omitempty"` + // StatusCode will be used for the direct response's status code. + StatusCode uint32 `json:"statusCode" yaml:"statusCode"` +} + +// Validate the fields within the DirectResponse structure +func (r DirectResponse) Validate() error { + var errs error + if status := r.StatusCode; status > 599 || status < 100 { + errs = errors.Join(errs, ErrDirectResponseStatusInvalid) + } + + return errs +} + +// URLRewrite holds the details for how to rewrite a request +// +k8s:deepcopy-gen=true +type URLRewrite struct { + // Path contains config for rewriting the path of the request. + Path *HTTPPathModifier `json:"path,omitempty" yaml:"path,omitempty"` + // Hostname configures the replacement of the request's hostname. + Hostname *string `json:"hostname,omitempty" yaml:"hostname,omitempty"` +} + +// Validate the fields within the URLRewrite structure +func (r URLRewrite) Validate() error { + var errs error + + if r.Path != nil { + if err := r.Path.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// Redirect holds the details for how and where to redirect a request +// +k8s:deepcopy-gen=true +type Redirect struct { + // Scheme configures the replacement of the request's scheme. + Scheme *string `json:"scheme" yaml:"scheme"` + // Hostname configures the replacement of the request's hostname. + Hostname *string `json:"hostname" yaml:"hostname"` + // Path contains config for rewriting the path of the request. + Path *HTTPPathModifier `json:"path" yaml:"path"` + // Port configures the replacement of the request's port. + Port *uint32 `json:"port" yaml:"port"` + // Status code configures the redirection response's status code. + StatusCode *int32 `json:"statusCode" yaml:"statusCode"` +} + +// Validate the fields within the Redirect structure +func (r Redirect) Validate() error { + var errs error + + if r.Scheme != nil { + if *r.Scheme != "http" && *r.Scheme != "https" { + errs = errors.Join(errs, ErrRedirectUnsupportedScheme) + } + } + + if r.Path != nil { + if err := r.Path.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + if r.StatusCode != nil { + if *r.StatusCode != 301 && *r.StatusCode != 302 { + errs = errors.Join(errs, ErrRedirectUnsupportedStatus) + } + } + + return errs +} + +// HTTPPathModifier holds instructions for how to modify the path of a request on a redirect response +// +k8s:deepcopy-gen=true +type HTTPPathModifier struct { + // FullReplace provides a string to replace the full path of the request. + FullReplace *string `json:"fullReplace" yaml:"fullReplace"` + // PrefixMatchReplace provides a string to replace the matched prefix of the request. + PrefixMatchReplace *string `json:"prefixMatchReplace" yaml:"prefixMatchReplace"` +} + +// Validate the fields within the HTTPPathModifier structure +func (r HTTPPathModifier) Validate() error { + var errs error + + if r.FullReplace != nil && r.PrefixMatchReplace != nil { + errs = errors.Join(errs, ErrHTTPPathModifierDoubleReplace) + } + + if r.FullReplace == nil && r.PrefixMatchReplace == nil { + errs = errors.Join(errs, ErrHTTPPathModifierNoReplace) + } + + return errs +} + +// StringMatch holds the various match conditions. +// Only one of Exact, Prefix, SafeRegex or Distinct can be set. +// +k8s:deepcopy-gen=true +type StringMatch struct { + // Name of the field to match on. + Name string `json:"name" yaml:"name"` + // Exact match condition. + Exact *string `json:"exact,omitempty" yaml:"exact,omitempty"` + // Prefix match condition. + Prefix *string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + // Suffix match condition. + Suffix *string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + // SafeRegex match condition. + SafeRegex *string `json:"safeRegex,omitempty" yaml:"safeRegex,omitempty"` + // Distinct match condition. + // Used to match any and all possible unique values encountered within the Name field. + Distinct bool `json:"distinct" yaml:"distinct"` +} + +// Validate the fields within the StringMatch structure +func (s StringMatch) Validate() error { + var errs error + matchCount := 0 + if s.Exact != nil { + matchCount++ + } + if s.Prefix != nil { + matchCount++ + } + if s.Suffix != nil { + matchCount++ + } + if s.SafeRegex != nil { + matchCount++ + } + if s.Distinct { + if s.Name == "" { + errs = errors.Join(errs, ErrStringMatchNameIsEmpty) + } + matchCount++ + } + + if matchCount != 1 { + errs = errors.Join(errs, ErrStringMatchConditionInvalid) + } + + return errs +} + +// TCPListener holds the TCP listener configuration. +// +k8s:deepcopy-gen=true +type TCPListener struct { + // Name of the TCPListener + Name string `json:"name" yaml:"name"` + // Address that the listener should listen on. + Address string `json:"address" yaml:"address"` + // Port on which the service can be expected to be accessed by clients. + Port uint32 `json:"port" yaml:"port"` + // TLS holds information for configuring TLS on a listener + TLS *TLS `json:"tls,omitempty" yaml:"tls,omitempty"` + // Destinations associated with TCP traffic to the service. + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` +} + +// TLS holds information for configuring TLS on a listener +// +k8s:deepcopy-gen=true +type TLS struct { + // TLS information required for TLS Passthrough, If provided, incoming + // connections' server names are inspected and routed to backends accordingly. + Passthrough *TLSInspectorConfig `json:"passthrough,omitempty" yaml:"passthrough,omitempty"` + // TLS information required for TLS Termination + Terminate *TLSConfig `json:"terminate,omitempty" yaml:"terminate,omitempty"` +} + +// Validate the fields within the TCPListener structure +func (h TCPListener) Validate() error { + var errs error + if h.Name == "" { + errs = errors.Join(errs, ErrListenerNameEmpty) + } + if _, err := netip.ParseAddr(h.Address); err != nil { + errs = errors.Join(errs, ErrListenerAddressInvalid) + } + if h.Port == 0 { + errs = errors.Join(errs, ErrListenerPortInvalid) + } + if h.TLS != nil && h.TLS.Passthrough != nil { + if err := h.TLS.Passthrough.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + if h.TLS != nil && h.TLS.Terminate != nil { + if err := h.TLS.Terminate.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + return errs +} + +// TLSInspectorConfig holds the configuration required for inspecting TLS +// passthrough connections. +// +k8s:deepcopy-gen=true +type TLSInspectorConfig struct { + // Server names that are compared against the server names of a new connection. + // Wildcard hosts are supported in the prefix form. Partial wildcards are not + // supported, and values like *w.example.com are invalid. + // SNIs are used only in case of TLS Passthrough. + SNIs []string `json:"snis,omitempty" yaml:"snis,omitempty"` +} + +func (t TLSInspectorConfig) Validate() error { + var errs error + if len(t.SNIs) == 0 { + errs = errors.Join(errs, ErrTCPListenerSNIsEmpty) + } + return errs +} + +// UDPListener holds the UDP listener configuration. +// +k8s:deepcopy-gen=true +type UDPListener struct { + // Name of the UDPListener + Name string `json:"name" yaml:"name"` + // Address that the listener should listen on. + Address string `json:"address" yaml:"address"` + // Port on which the service can be expected to be accessed by clients. + Port uint32 `json:"port" yaml:"port"` + // Destination associated with UDP traffic to the service. + Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` +} + +// Validate the fields within the UDPListener structure +func (h UDPListener) Validate() error { + var errs error + if h.Name == "" { + errs = errors.Join(errs, ErrListenerNameEmpty) + } + if _, err := netip.ParseAddr(h.Address); err != nil { + errs = errors.Join(errs, ErrListenerAddressInvalid) + } + if h.Port == 0 { + errs = errors.Join(errs, ErrListenerPortInvalid) + } + if h.Destination != nil { + if err := h.Destination.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// RateLimit holds the rate limiting configuration. +// +k8s:deepcopy-gen=true +type RateLimit struct { + // Global rate limit settings. + Global *GlobalRateLimit `json:"global,omitempty" yaml:"global,omitempty"` + + // Local rate limit settings. + Local *LocalRateLimit `json:"local,omitempty" yaml:"local,omitempty"` +} + +// GlobalRateLimit holds the global rate limiting configuration. +// +k8s:deepcopy-gen=true +type GlobalRateLimit struct { + // TODO zhaohuabing: add default values for Global rate limiting. + + // Rules for rate limiting. + Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty"` +} + +// LocalRateLimit holds the local rate limiting configuration. +// +k8s:deepcopy-gen=true +type LocalRateLimit struct { + // Default rate limiting values. + // If a request does not match any of the rules, the default values are used. + Default RateLimitValue `json:"default,omitempty" yaml:"default,omitempty"` + + // Rules for rate limiting. + Rules []*RateLimitRule `json:"rules,omitempty" yaml:"rules,omitempty"` +} + +// RateLimitRule holds the match and limit configuration for ratelimiting. +// +k8s:deepcopy-gen=true +type RateLimitRule struct { + // HeaderMatches define the match conditions on the request headers for this route. + HeaderMatches []*StringMatch `json:"headerMatches" yaml:"headerMatches"` + // CIDRMatch define the match conditions on the source IP's CIDR for this route. + CIDRMatch *CIDRMatch `json:"cidrMatch,omitempty" yaml:"cidrMatch,omitempty"` + // Limit holds the rate limit values. + Limit RateLimitValue `json:"limit,omitempty" yaml:"limit,omitempty"` +} + +type CIDRMatch struct { + CIDR string `json:"cidr" yaml:"cidr"` + IPv6 bool `json:"ipv6" yaml:"ipv6"` + MaskLen int `json:"maskLen" yaml:"maskLen"` + // Distinct means that each IP Address within the specified Source IP CIDR is treated as a distinct client selector + // and uses a separate rate limit bucket/counter. + Distinct bool `json:"distinct" yaml:"distinct"` +} + +// TODO zhaohuabing: remove this function +func (r *RateLimitRule) IsMatchSet() bool { + return len(r.HeaderMatches) != 0 || r.CIDRMatch != nil +} + +// type RateLimitUnit egv1a1.RateLimitUnit + +// RateLimitValue holds the +// +k8s:deepcopy-gen=true +type RateLimitValue struct { + // Requests are the number of requests that need to be rate limited. + Requests uint `json:"requests" yaml:"requests"` + // Unit of rate limiting. + // Unit RateLimitUnit `json:"unit" yaml:"unit"` +} + +// AccessLog holds the access logging configuration. +// +k8s:deepcopy-gen=true +type AccessLog struct { + Text []*TextAccessLog `json:"text,omitempty" yaml:"text,omitempty"` + JSON []*JSONAccessLog `json:"json,omitempty" yaml:"json,omitempty"` + OpenTelemetry []*OpenTelemetryAccessLog `json:"openTelemetry,omitempty" yaml:"openTelemetry,omitempty"` +} + +// TextAccessLog holds the configuration for text access logging. +// +k8s:deepcopy-gen=true +type TextAccessLog struct { + Format *string `json:"format,omitempty" yaml:"format,omitempty"` + Path string `json:"path" yaml:"path"` +} + +// JSONAccessLog holds the configuration for JSON access logging. +// +k8s:deepcopy-gen=true +type JSONAccessLog struct { + JSON map[string]string `json:"json,omitempty" yaml:"json,omitempty"` + Path string `json:"path" yaml:"path"` +} + +// OpenTelemetryAccessLog holds the configuration for OpenTelemetry access logging. +// +k8s:deepcopy-gen=true +type OpenTelemetryAccessLog struct { + Text *string `json:"text,omitempty" yaml:"text,omitempty"` + Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` + Host string `json:"host" yaml:"host"` + Port uint32 `json:"port" yaml:"port"` + Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` +} + +// JSONPatchConfig defines the configuration for patching a Envoy xDS Resource +// using JSONPatch semantics +// +k8s:deepcopy-gen=true +type JSONPatchConfig struct { + // Type is the typed URL of the Envoy xDS Resource + Type string `json:"type" yaml:"type"` + // Name is the name of the resource + Name string `json:"name" yaml:"name"` + // Patch defines the JSON Patch Operation + Operation JSONPatchOperation `json:"operation" yaml:"operation"` +} + +// JSONPatchOperation defines the JSON Patch Operation as defined in +// https://datatracker.ietf.org/doc/html/rfc6902 +// +k8s:deepcopy-gen=true +type JSONPatchOperation struct { + // Op is the type of operation to perform + Op string `json:"op" yaml:"op"` + // Path is the location of the target document/field where the operation will be performed + // Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. + Path string `json:"path" yaml:"path"` + // From is the source location of the value to be copied or moved. Only valid + // for move or copy operations + // Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. + // +optional + From *string `json:"from,omitempty" yaml:"from,omitempty"` + // Value is the new value of the path location. + Value *apiextensionsv1.JSON `json:"value,omitempty" yaml:"value,omitempty"` +} + +// Tracing defines the configuration for tracing a Envoy xDS Resource +// +k8s:deepcopy-gen=true +type Tracing struct { + ServiceName string `json:"serviceName"` + + egv1a1.ProxyTracing +} + +// Metrics defines the configuration for metrics generated by Envoy +// +k8s:deepcopy-gen=true +type Metrics struct { + EnableVirtualHostStats bool `json:"enableVirtualHostStats" yaml:"enableVirtualHostStats"` +} + +// TCPKeepalive define the TCP Keepalive configuration. +// +k8s:deepcopy-gen=true +type TCPKeepalive struct { + // The total number of unacknowledged probes to send before deciding + // the connection is dead. + // Defaults to 9. + Probes *uint32 `json:"probes,omitempty" yaml:"probes,omitempty"` + // The duration, in seconds, a connection needs to be idle before keep-alive + // probes start being sent. + // Defaults to `7200s`. + IdleTime *uint32 `json:"idleTime,omitempty" yaml:"idleTime,omitempty"` + // The duration, in seconds, between keep-alive probes. + // Defaults to `75s`. + Interval *uint32 `json:"interval,omitempty" yaml:"interval,omitempty"` +} + +// LoadBalancer defines the load balancer settings. +// +k8s:deepcopy-gen=true +type LoadBalancer struct { + // RoundRobin load balacning policy + RoundRobin *RoundRobin `json:"roundRobin,omitempty" yaml:"roundRobin,omitempty"` + // LeastRequest load balancer policy + LeastRequest *LeastRequest `json:"leastRequest,omitempty" yaml:"leastRequest,omitempty"` + // Random load balancer policy + Random *Random `json:"random,omitempty" yaml:"random,omitempty"` + // ConsistentHash load balancer policy + ConsistentHash *ConsistentHash `json:"consistentHash,omitempty" yaml:"consistentHash,omitempty"` +} + +// Validate the fields within the LoadBalancer structure +func (l *LoadBalancer) Validate() error { + var errs error + matchCount := 0 + if l.RoundRobin != nil { + matchCount++ + } + if l.LeastRequest != nil { + matchCount++ + } + if l.Random != nil { + matchCount++ + } + if l.ConsistentHash != nil { + matchCount++ + } + if matchCount != 1 { + errs = errors.Join(errs, ErrLoadBalancerInvalid) + } + + return errs +} + +// RoundRobin load balancer settings +// +k8s:deepcopy-gen=true +type RoundRobin struct { + // SlowStart defines the slow start configuration. + // If set, slow start mode is enabled for newly added hosts in the cluster. + SlowStart *SlowStart `json:"slowStart,omitempty" yaml:"slowStart,omitempty"` +} + +// LeastRequest load balancer settings +// +k8s:deepcopy-gen=true +type LeastRequest struct { + // SlowStart defines the slow start configuration. + // If set, slow start mode is enabled for newly added hosts in the cluster. + SlowStart *SlowStart `json:"slowStart,omitempty" yaml:"slowStart,omitempty"` +} + +// Random load balancer settings +// +k8s:deepcopy-gen=true +type Random struct{} + +// ConsistentHash load balancer settings +// +k8s:deepcopy-gen=true +type ConsistentHash struct { + // Hash based on the Source IP Address + SourceIP *bool `json:"sourceIP,omitempty" yaml:"sourceIP,omitempty"` +} + +type ProxyProtocolVersion string + +const ( + // ProxyProtocolVersionV1 is the PROXY protocol version 1 (human readable format). + ProxyProtocolVersionV1 ProxyProtocolVersion = "V1" + // ProxyProtocolVersionV2 is the PROXY protocol version 2 (binary format). + ProxyProtocolVersionV2 ProxyProtocolVersion = "V2" +) + +// ProxyProtocol upstream settings +// +k8s:deepcopy-gen=true +type ProxyProtocol struct { + // Version of proxy protocol to use + Version ProxyProtocolVersion `json:"version,omitempty" yaml:"version,omitempty"` +} + +// SlowStart defines the slow start configuration. +// +k8s:deepcopy-gen=true +type SlowStart struct { + // Window defines the duration of the warm up period for newly added host. + Window *metav1.Duration `json:"window" yaml:"window"` +} + +// Backend CircuitBreaker settings for the DEFAULT routing priority +// +k8s:deepcopy-gen=true +type CircuitBreaker struct { + // The maximum number of connections that Envoy will establish. + MaxConnections *uint32 `json:"maxConnections,omitempty" yaml:"maxConnections,omitempty"` + + // The maximum number of pending requests that Envoy will queue. + MaxPendingRequests *uint32 `json:"maxPendingRequests,omitempty" yaml:"maxPendingRequests,omitempty"` + + // The maximum number of parallel requests that Envoy will make. + MaxParallelRequests *uint32 `json:"maxParallelRequests,omitempty" yaml:"maxParallelRequests,omitempty"` + + // The maximum number of parallel requests that Envoy will make. + MaxRequestsPerConnection *uint32 `json:"maxRequestsPerConnection,omitempty" yaml:"maxRequestsPerConnection,omitempty"` + + // The maximum number of parallel retries that Envoy will make. + MaxParallelRetries *uint32 `json:"maxParallelRetries,omitempty" yaml:"maxParallelRetries,omitempty"` +} + +// HealthCheck defines health check settings +// +k8s:deepcopy-gen=true +type HealthCheck struct { + Active *ActiveHealthCheck `json:"active,omitempty" yaml:"active,omitempty"` + + Passive *OutlierDetection `json:"passive,omitempty" yaml:"passive,omitempty"` +} + +// OutlierDetection defines passive health check settings +// +k8s:deepcopy-gen=true +type OutlierDetection struct { + // Interval defines the time between passive health checks. + Interval *metav1.Duration `json:"interval,omitempty"` + // SplitExternalLocalOriginErrors enables splitting of errors between external and local origin. + SplitExternalLocalOriginErrors *bool `json:"splitExternalLocalOriginErrors,omitempty" yaml:"splitExternalLocalOriginErrors,omitempty"` + // ConsecutiveLocalOriginFailures sets the number of consecutive local origin failures triggering ejection. + ConsecutiveLocalOriginFailures *uint32 `json:"consecutiveLocalOriginFailures,omitempty" yaml:"consecutiveLocalOriginFailures,omitempty"` + // ConsecutiveGatewayErrors sets the number of consecutive gateway errors triggering ejection. + ConsecutiveGatewayErrors *uint32 `json:"consecutiveGatewayErrors,omitempty" yaml:"consecutiveGatewayErrors,omitempty"` + // Consecutive5xxErrors sets the number of consecutive 5xx errors triggering ejection. + Consecutive5xxErrors *uint32 `json:"consecutive5XxErrors,omitempty" yaml:"consecutive5XxErrors,omitempty"` + // BaseEjectionTime defines the base duration for which a host will be ejected on consecutive failures. + BaseEjectionTime *metav1.Duration `json:"baseEjectionTime,omitempty" yaml:"baseEjectionTime,omitempty"` + // MaxEjectionPercent sets the maximum percentage of hosts in a cluster that can be ejected. + MaxEjectionPercent *int32 `json:"maxEjectionPercent,omitempty" yaml:"maxEjectionPercent,omitempty"` +} + +// ActiveHealthCheck defines active health check settings +// +k8s:deepcopy-gen=true +type ActiveHealthCheck struct { + // Timeout defines the time to wait for a health check response. + Timeout *metav1.Duration `json:"timeout"` + // Interval defines the time between active health checks. + Interval *metav1.Duration `json:"interval"` + // UnhealthyThreshold defines the number of unhealthy health checks required before a backend host is marked unhealthy. + UnhealthyThreshold *uint32 `json:"unhealthyThreshold"` + // HealthyThreshold defines the number of healthy health checks required before a backend host is marked healthy. + HealthyThreshold *uint32 `json:"healthyThreshold"` + // HTTP defines the configuration of http health checker. + HTTP *HTTPHealthChecker `json:"http,omitempty" yaml:"http,omitempty"` + // TCP defines the configuration of tcp health checker. + TCP *TCPHealthChecker `json:"tcp,omitempty" yaml:"tcp,omitempty"` +} + +func (h *HealthCheck) SetHTTPHostIfAbsent(host string) { + if h != nil && h.Active != nil && h.Active.HTTP != nil && h.Active.HTTP.Host == "" { + h.Active.HTTP.Host = host + } +} + +// Validate the fields within the HealthCheck structure. +func (h *HealthCheck) Validate() error { + var errs error + if h.Active != nil { + if h.Active.Timeout != nil && h.Active.Timeout.Duration == 0 { + errs = errors.Join(errs, ErrHealthCheckTimeoutInvalid) + } + if h.Active.Interval != nil && h.Active.Interval.Duration == 0 { + errs = errors.Join(errs, ErrHealthCheckIntervalInvalid) + } + if h.Active.UnhealthyThreshold != nil && *h.Active.UnhealthyThreshold == 0 { + errs = errors.Join(errs, ErrHealthCheckUnhealthyThresholdInvalid) + } + if h.Active.HealthyThreshold != nil && *h.Active.HealthyThreshold == 0 { + errs = errors.Join(errs, ErrHealthCheckHealthyThresholdInvalid) + } + + matchCount := 0 + if h.Active.HTTP != nil { + matchCount++ + } + if h.Active.TCP != nil { + matchCount++ + } + if matchCount > 1 { + errs = errors.Join(errs, ErrHealthCheckerInvalid) + } + + if h.Active.HTTP != nil { + if err := h.Active.HTTP.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if h.Active.TCP != nil { + if err := h.Active.TCP.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + } + + if h.Passive != nil { + if h.Passive.BaseEjectionTime != nil && h.Passive.BaseEjectionTime.Duration == 0 { + errs = errors.Join(errs, ErrOutlierDetectionBaseEjectionTimeInvalid) + } + + if h.Passive.Interval != nil && h.Passive.Interval.Duration == 0 { + errs = errors.Join(errs, ErrOutlierDetectionIntervalInvalid) + } + } + + return errs +} + +// HTTPHealthChecker defines the settings of http health check. +// +k8s:deepcopy-gen=true +type HTTPHealthChecker struct { + // Host defines the value of the host header in the HTTP health check request. + Host string `json:"host" yaml:"host"` + // Path defines the HTTP path that will be requested during health checking. + Path string `json:"path" yaml:"path"` + // Method defines the HTTP method used for health checking. + Method *string `json:"method,omitempty" yaml:"method,omitempty"` + // ExpectedStatuses defines a list of HTTP response statuses considered healthy. + ExpectedStatuses []HTTPStatus `json:"expectedStatuses,omitempty" yaml:"expectedStatuses,omitempty"` + // ExpectedResponse defines a list of HTTP expected responses to match. + ExpectedResponse *HealthCheckPayload `json:"expectedResponse,omitempty" yaml:"expectedResponses,omitempty"` +} + +// Validate the fields within the HTTPHealthChecker structure. +func (c *HTTPHealthChecker) Validate() error { + var errs error + if c.Host == "" { + errs = errors.Join(errs, ErrHCHTTPHostInvalid) + } + if c.Path == "" { + errs = errors.Join(errs, ErrHCHTTPPathInvalid) + } + if c.Method != nil { + switch *c.Method { + case http.MethodGet: + case http.MethodHead: + case http.MethodPost: + case http.MethodPut: + case http.MethodDelete: + case http.MethodOptions: + case http.MethodTrace: + case http.MethodPatch: + case "": + default: + errs = errors.Join(errs, ErrHCHTTPMethodInvalid) + } + } + if len(c.ExpectedStatuses) == 0 { + errs = errors.Join(errs, ErrHCHTTPExpectedStatusesInvalid) + } + for _, r := range c.ExpectedStatuses { + if err := r.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if c.ExpectedResponse != nil { + if err := c.ExpectedResponse.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + return errs +} + +// HTTPStatus represents http status code. +type HTTPStatus int + +func (h HTTPStatus) Validate() error { + if h < 100 || h >= 600 { + return ErrHTTPStatusInvalid + } + return nil +} + +// TCPHealthChecker defines the settings of tcp health check. +// +k8s:deepcopy-gen=true +type TCPHealthChecker struct { + Send *HealthCheckPayload `json:"send,omitempty" yaml:"send,omitempty"` + Receive *HealthCheckPayload `json:"receive,omitempty" yaml:"receive,omitempty"` +} + +// Validate the fields within the TCPHealthChecker structure. +func (c *TCPHealthChecker) Validate() error { + var errs error + if c.Send != nil { + if err := c.Send.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + if c.Receive != nil { + if err := c.Receive.Validate(); err != nil { + errs = errors.Join(errs, err) + } + } + return errs +} + +// HealthCheckPayload defines the encoding of the payload bytes in the payload. +// +k8s:deepcopy-gen=true +type HealthCheckPayload struct { + // Text payload in plain text. + Text *string `json:"text,omitempty" yaml:"text,omitempty"` + // Binary payload base64 encoded + Binary []byte `json:"binary,omitempty" yaml:"binary,omitempty"` +} + +// Validate the fields in the HealthCheckPayload. +func (p *HealthCheckPayload) Validate() error { + var errs error + matchCount := 0 + if p.Text != nil && *p.Text != "" { + matchCount++ + } + if len(p.Binary) > 0 { + matchCount++ + } + if matchCount != 1 { + errs = errors.Join(errs, ErrHealthCheckPayloadInvalid) + } + return errs +} + +// Backend connection timeout settings +// +k8s:deepcopy-gen=true +type Timeout struct { + // Timeout settings for TCP. + TCP *TCPTimeout `json:"tcp,omitempty" yaml:"tcp,omitempty"` + + // Timeout settings for HTTP. + HTTP *HTTPTimeout `json:"http,omitempty" yaml:"tcp,omitempty"` +} + +// +k8s:deepcopy-gen=true +type TCPTimeout struct { + // The timeout for network connection establishment, including TCP and TLS handshakes. + ConnectTimeout *metav1.Duration `json:"connectTimeout,omitempty" yaml:"connectTimeout,omitempty"` +} + +// +k8s:deepcopy-gen=true +type HTTPTimeout struct { + // RequestTimeout is the time until which entire response is received from the upstream. + RequestTimeout *metav1.Duration `json:"requestTimeout,omitempty" yaml:"requestTimeout,omitempty"` + + // The idle timeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection. + ConnectionIdleTimeout *metav1.Duration `json:"connectionIdleTimeout,omitempty" yaml:"connectionIdleTimeout,omitempty"` + + // The maximum duration of an HTTP connection. + MaxConnectionDuration *metav1.Duration `json:"maxConnectionDuration,omitempty" yaml:"maxConnectionDuration,omitempty"` +} + +// Retry define the retry policy configuration. +// +k8s:deepcopy-gen=true +type Retry struct { + // NumRetries is the number of retries to be attempted. Defaults to 2. + NumRetries *uint32 `json:"numRetries,omitempty"` + + // RetryOn specifies the retry trigger condition. + RetryOn *RetryOn `json:"retryOn,omitempty"` + + // PerRetry is the retry policy to be applied per retry attempt. + PerRetry *PerRetryPolicy `json:"perRetry,omitempty"` +} + +type TriggerEnum egv1a1.TriggerEnum + +const ( + Error5XX = TriggerEnum(egv1a1.Error5XX) + GatewayError = TriggerEnum(egv1a1.GatewayError) + Reset = TriggerEnum(egv1a1.Reset) + ConnectFailure = TriggerEnum(egv1a1.ConnectFailure) + Retriable4XX = TriggerEnum(egv1a1.Retriable4XX) + RefusedStream = TriggerEnum(egv1a1.RefusedStream) + RetriableStatusCodes = TriggerEnum(egv1a1.RetriableStatusCodes) + Cancelled = TriggerEnum(egv1a1.Cancelled) + DeadlineExceeded = TriggerEnum(egv1a1.DeadlineExceeded) + Internal = TriggerEnum(egv1a1.Internal) + ResourceExhausted = TriggerEnum(egv1a1.ResourceExhausted) + Unavailable = TriggerEnum(egv1a1.Unavailable) +) + +// +k8s:deepcopy-gen=true +type RetryOn struct { + // Triggers specifies the retry trigger condition(Http/Grpc). + Triggers []TriggerEnum `json:"triggers,omitempty"` + + // HttpStatusCodes specifies the http status codes to be retried. + HTTPStatusCodes []HTTPStatus `json:"httpStatusCodes,omitempty"` +} + +// +k8s:deepcopy-gen=true +type PerRetryPolicy struct { + // Timeout is the timeout per retry attempt. + Timeout *metav1.Duration `json:"timeout,omitempty"` + // Backoff is the backoff policy to be applied per retry attempt. + BackOff *BackOffPolicy `json:"backOff,omitempty"` +} + +// +k8s:deepcopy-gen=true +type BackOffPolicy struct { + // BaseInterval is the base interval between retries. + BaseInterval *metav1.Duration `json:"baseInterval,omitempty"` + // MaxInterval is the maximum interval between retries. + MaxInterval *metav1.Duration `json:"maxInterval,omitempty"` +} + +// TLSUpstreamConfig contains sni and ca file in []byte format. +// +k8s:deepcopy-gen=true +type TLSUpstreamConfig struct { + SNI string `json:"sni,omitempty" yaml:"sni,omitempty"` + UseSystemTrustStore bool `json:"useSystemTrustStore,omitempty" yaml:"useSystemTrustStore,omitempty"` + CACertificate *TLSCACertificate `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/ir/xds_test.go b/adapter/internal/operator/gateway-api/ir/xds_test.go new file mode 100644 index 0000000000..c2c9b1aac0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/xds_test.go @@ -0,0 +1,1438 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package ir + +import ( + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +var ( + // HTTPListener + happyHTTPListener = HTTPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*HTTPRoute{&happyHTTPRoute}, + } + happyHTTPSListener = HTTPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + TLS: &TLSConfig{ + Certificates: []TLSCertificate{{ + + Name: "happy", + ServerCertificate: []byte{1, 2, 3}, + PrivateKey: []byte{1, 2, 3}, + }}}, + Routes: []*HTTPRoute{&happyHTTPRoute}, + } + redactedHappyHTTPSListener = HTTPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + TLS: &TLSConfig{ + Certificates: []TLSCertificate{{ + + Name: "happy", + ServerCertificate: []byte{1, 2, 3}, + PrivateKey: redacted, + }}}, + Routes: []*HTTPRoute{&happyHTTPRoute}, + } + invalidAddrHTTPListener = HTTPListener{ + Name: "invalid-addr", + Address: "1.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*HTTPRoute{&happyHTTPRoute}, + } + invalidBackendHTTPListener = HTTPListener{ + Name: "invalid-backend-match", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*HTTPRoute{&invalidBackendHTTPRoute}, + } + weightedInvalidBackendsHTTPListener = HTTPListener{ + Name: "weighted-invalid-backends-match", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*HTTPRoute{&weightedInvalidBackendsHTTPRoute}, + } + + // TCPListener + happyTCPListenerTLSPassthrough = TCPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, + } + + happyTCPListenerTLSTerminate = TCPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Terminate: &TLSConfig{ + Certificates: []TLSCertificate{{ + Name: "happy", + ServerCertificate: []byte("server-cert"), + PrivateKey: []byte("priv-key"), + }}}}, + Destination: &happyRouteDestination, + } + + emptySNITCPListenerTLSPassthrough = TCPListener{ + Name: "empty-sni", + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, + } + invalidNameTCPListenerTLSPassthrough = TCPListener{ + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, + } + invalidAddrTCPListenerTLSPassthrough = TCPListener{ + Name: "invalid-addr", + Address: "1.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{"example.com"}}}, + Destination: &happyRouteDestination, + } + invalidSNITCPListenerTLSPassthrough = TCPListener{ + Address: "0.0.0.0", + Port: 80, + TLS: &TLS{Passthrough: &TLSInspectorConfig{SNIs: []string{}}}, + Destination: &happyRouteDestination, + } + + // UDPListener + happyUDPListener = UDPListener{ + Name: "happy", + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, + } + invalidNameUDPListener = UDPListener{ + Address: "0.0.0.0", + Port: 80, + Destination: &happyRouteDestination, + } + invalidAddrUDPListener = UDPListener{ + Name: "invalid-addr", + Address: "1.0.0", + Port: 80, + Destination: &happyRouteDestination, + } + invalidPortUDPListenerT = UDPListener{ + Name: "invalid-port", + Address: "0.0.0.0", + Port: 0, + Destination: &happyRouteDestination, + } + + // HTTPRoute + happyHTTPRoute = HTTPRoute{ + Name: "happy", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("example"), + }, + Destination: &happyRouteDestination, + } + invalidBackendHTTPRoute = HTTPRoute{ + Name: "invalid-backend", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("invalid-backend"), + }, + BackendWeights: BackendWeights{ + Invalid: 1, + }, + } + weightedInvalidBackendsHTTPRoute = HTTPRoute{ + Name: "weighted-invalid-backends", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("invalid-backends"), + }, + Destination: &happyRouteDestination, + BackendWeights: BackendWeights{ + Invalid: 1, + Valid: 1, + }, + } + + redirectHTTPRoute = HTTPRoute{ + Name: "redirect", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("redirect"), + }, + Redirect: &Redirect{ + Scheme: ptr.To("https"), + Hostname: ptr.To("redirect.example.com"), + Path: &HTTPPathModifier{ + FullReplace: ptr.To("/redirect"), + }, + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](301), + }, + } + // A direct response error is used when an invalid filter type is supplied + invalidFilterHTTPRoute = HTTPRoute{ + Name: "filter-error", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("filter-error"), + }, + DirectResponse: &DirectResponse{ + Body: ptr.To("invalid filter type"), + StatusCode: uint32(500), + }, + } + + redirectFilterInvalidStatus = HTTPRoute{ + Name: "redirect-bad-status-scheme-nopat", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("redirect"), + }, + Redirect: &Redirect{ + Scheme: ptr.To("err"), + Hostname: ptr.To("redirect.example.com"), + Path: &HTTPPathModifier{}, + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](305), + }, + } + redirectFilterBadPath = HTTPRoute{ + Name: "redirect", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("redirect"), + }, + Redirect: &Redirect{ + Scheme: ptr.To("https"), + Hostname: ptr.To("redirect.example.com"), + Path: &HTTPPathModifier{ + FullReplace: ptr.To("/redirect"), + PrefixMatchReplace: ptr.To("/redirect"), + }, + Port: ptr.To(uint32(8443)), + StatusCode: ptr.To[int32](301), + }, + } + directResponseBadStatus = HTTPRoute{ + Name: "redirect", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("redirect"), + }, + DirectResponse: &DirectResponse{ + Body: ptr.To("invalid filter type"), + StatusCode: uint32(799), + }, + } + + urlRewriteHTTPRoute = HTTPRoute{ + Name: "rewrite", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("rewrite"), + }, + URLRewrite: &URLRewrite{ + Hostname: ptr.To("rewrite.example.com"), + Path: &HTTPPathModifier{ + FullReplace: ptr.To("/rewrite"), + }, + }, + } + + urlRewriteFilterBadPath = HTTPRoute{ + Name: "rewrite", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("rewrite"), + }, + URLRewrite: &URLRewrite{ + Hostname: ptr.To("rewrite.example.com"), + Path: &HTTPPathModifier{ + FullReplace: ptr.To("/rewrite"), + PrefixMatchReplace: ptr.To("/rewrite"), + }, + }, + } + + addRequestHeaderHTTPRoute = HTTPRoute{ + Name: "addheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("addheader"), + }, + AddRequestHeaders: []AddHeader{ + { + Name: "example-header", + Value: "example-value", + Append: true, + }, + { + Name: "example-header-2", + Value: "example-value-2", + Append: false, + }, + { + Name: "empty-header", + Value: "", + Append: false, + }, + }, + } + + removeRequestHeaderHTTPRoute = HTTPRoute{ + Name: "remheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("remheader"), + }, + RemoveRequestHeaders: []string{ + "x-request-header", + "example-header", + "another-header", + }, + } + + addAndRemoveRequestHeadersDupeHTTPRoute = HTTPRoute{ + Name: "duplicateheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("duplicateheader"), + }, + AddRequestHeaders: []AddHeader{ + { + Name: "example-header", + Value: "example-value", + Append: true, + }, + { + Name: "example-header", + Value: "example-value-2", + Append: false, + }, + }, + RemoveRequestHeaders: []string{ + "x-request-header", + "example-header", + "example-header", + }, + } + + addRequestHeaderEmptyHTTPRoute = HTTPRoute{ + Name: "addemptyheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("addemptyheader"), + }, + AddRequestHeaders: []AddHeader{ + { + Name: "", + Value: "example-value", + Append: true, + }, + }, + } + + addResponseHeaderHTTPRoute = HTTPRoute{ + Name: "addheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("addheader"), + }, + AddResponseHeaders: []AddHeader{ + { + Name: "example-header", + Value: "example-value", + Append: true, + }, + { + Name: "example-header-2", + Value: "example-value-2", + Append: false, + }, + { + Name: "empty-header", + Value: "", + Append: false, + }, + }, + } + + removeResponseHeaderHTTPRoute = HTTPRoute{ + Name: "remheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("remheader"), + }, + RemoveResponseHeaders: []string{ + "x-request-header", + "example-header", + "another-header", + }, + } + + addAndRemoveResponseHeadersDupeHTTPRoute = HTTPRoute{ + Name: "duplicateheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("duplicateheader"), + }, + AddResponseHeaders: []AddHeader{ + { + Name: "example-header", + Value: "example-value", + Append: true, + }, + { + Name: "example-header", + Value: "example-value-2", + Append: false, + }, + }, + RemoveResponseHeaders: []string{ + "x-request-header", + "example-header", + "example-header", + }, + } + + addResponseHeaderEmptyHTTPRoute = HTTPRoute{ + Name: "addemptyheader", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("addemptyheader"), + }, + AddResponseHeaders: []AddHeader{ + { + Name: "", + Value: "example-value", + Append: true, + }, + }, + } + + jwtAuthenHTTPRoute = HTTPRoute{ + Name: "jwtauthen", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("jwtauthen"), + }, + } + requestMirrorFilter = HTTPRoute{ + Name: "mirrorfilter", + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("mirrorfilter"), + }, + Mirrors: []*RouteDestination{&happyRouteDestination}, + } + + // RouteDestination + happyRouteDestination = RouteDestination{ + Name: "happy-dest", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, + } +) + +func TestValidateXds(t *testing.T) { + tests := []struct { + name string + input Xds + want []error + }{ + { + name: "happy", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPListener}, + }, + want: nil, + }, + { + name: "happy tls passthrough", + input: Xds{ + TCP: []*TCPListener{&happyTCPListenerTLSPassthrough}, + }, + want: nil, + }, + { + name: "happy tls terminate", + input: Xds{ + TCP: []*TCPListener{&happyTCPListenerTLSTerminate}, + }, + want: nil, + }, + { + name: "invalid listener", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPListener, &invalidAddrHTTPListener}, + }, + want: []error{ErrListenerAddressInvalid}, + }, + { + name: "invalid backend", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPListener, &invalidBackendHTTPListener}, + }, + want: nil, + }, + { + name: "weighted invalid backend", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPListener, &weightedInvalidBackendsHTTPListener}, + }, + want: nil, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + got := test.input.Validate() + for _, w := range test.want { + require.ErrorContains(t, got, w.Error()) + } + } + }) + } +} + +func TestValidateHTTPListener(t *testing.T) { + tests := []struct { + name string + input HTTPListener + want []error + }{ + { + name: "happy", + input: happyHTTPListener, + want: nil, + }, + { + name: "invalid name", + input: HTTPListener{ + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*HTTPRoute{&happyHTTPRoute}, + }, + want: []error{ErrListenerNameEmpty}, + }, + { + name: "invalid addr", + input: invalidAddrHTTPListener, + want: []error{ErrListenerAddressInvalid}, + }, + { + name: "invalid port and hostnames", + input: HTTPListener{ + Name: "invalid-port-and-hostnames", + Address: "1.0.0", + Routes: []*HTTPRoute{&happyHTTPRoute}, + }, + want: []error{ErrListenerPortInvalid, ErrHTTPListenerHostnamesEmpty}, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + got := test.input.Validate() + for _, w := range test.want { + require.ErrorContains(t, got, w.Error()) + } + } + }) + } +} + +func TestValidateTCPListener(t *testing.T) { + tests := []struct { + name string + input TCPListener + want []error + }{ + { + name: "tls passthrough happy", + input: happyTCPListenerTLSPassthrough, + want: nil, + }, + { + name: "tcp empty SNIs", + input: emptySNITCPListenerTLSPassthrough, + want: nil, + }, + { + name: "tls passthrough invalid name", + input: invalidNameTCPListenerTLSPassthrough, + want: []error{ErrListenerNameEmpty}, + }, + { + name: "tls passthrough invalid addr", + input: invalidAddrTCPListenerTLSPassthrough, + want: []error{ErrListenerAddressInvalid}, + }, + { + name: "tls passthrough empty SNIs", + input: invalidSNITCPListenerTLSPassthrough, + want: []error{ErrTCPListenerSNIsEmpty}, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + got := test.input.Validate() + for _, w := range test.want { + require.ErrorContains(t, got, w.Error()) + } + } + }) + } +} + +func TestValidateTLSListenerConfig(t *testing.T) { + tests := []struct { + name string + input TLSConfig + want error + }{ + { + name: "happy", + input: TLSConfig{ + Certificates: []TLSCertificate{{ + ServerCertificate: []byte("server-cert"), + PrivateKey: []byte("priv-key"), + }}}, + want: nil, + }, + { + name: "invalid server cert", + input: TLSConfig{ + Certificates: []TLSCertificate{{ + PrivateKey: []byte("priv-key"), + }}}, + want: ErrTLSServerCertEmpty, + }, + { + name: "invalid private key", + input: TLSConfig{ + Certificates: []TLSCertificate{{ + ServerCertificate: []byte("server-cert"), + }}}, + want: ErrTLSPrivateKey, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + require.EqualError(t, test.input.Validate(), test.want.Error()) + } + }) + } +} + +func TestEqualXds(t *testing.T) { + tests := []struct { + desc string + a *Xds + b *Xds + equal bool + }{ + { + desc: "out of order http routes are equal", + a: &Xds{ + HTTP: []*HTTPListener{ + { + Name: "listener-1", + Routes: []*HTTPRoute{ + {Name: "route-1"}, + {Name: "route-2"}, + }, + }, + }, + }, + b: &Xds{ + HTTP: []*HTTPListener{ + { + Name: "listener-1", + Routes: []*HTTPRoute{ + {Name: "route-2"}, + {Name: "route-1"}, + }, + }, + }, + }, + equal: true, + }, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + require.Equal(t, tc.equal, cmp.Equal(tc.a, tc.b)) + }) + } +} + +func TestValidateUDPListener(t *testing.T) { + tests := []struct { + name string + input UDPListener + want []error + }{ + { + name: "udp happy", + input: happyUDPListener, + want: nil, + }, + { + name: "udp invalid name", + input: invalidNameUDPListener, + want: []error{ErrListenerNameEmpty}, + }, + { + name: "udp invalid addr", + input: invalidAddrUDPListener, + want: []error{ErrListenerAddressInvalid}, + }, + { + name: "udp invalid port", + input: invalidPortUDPListenerT, + want: []error{ErrListenerPortInvalid}, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + got := test.input.Validate() + for _, w := range test.want { + require.ErrorContains(t, got, w.Error()) + } + } + }) + } +} + +func TestValidateHTTPRoute(t *testing.T) { + tests := []struct { + name string + input HTTPRoute + want []error + }{ + { + name: "happy", + input: happyHTTPRoute, + want: nil, + }, + { + name: "invalid name", + input: HTTPRoute{ + Hostname: "*", + PathMatch: &StringMatch{ + Exact: ptr.To("example"), + }, + Destination: &happyRouteDestination, + }, + want: []error{ErrHTTPRouteNameEmpty}, + }, + { + name: "invalid hostname", + input: HTTPRoute{ + Name: "invalid hostname", + PathMatch: &StringMatch{ + Exact: ptr.To("example"), + }, + Destination: &happyRouteDestination, + }, + want: []error{ErrHTTPRouteHostnameEmpty}, + }, + { + name: "invalid backend", + input: invalidBackendHTTPRoute, + want: nil, + }, + { + name: "weighted invalid backends", + input: weightedInvalidBackendsHTTPRoute, + want: nil, + }, + { + name: "empty name and invalid match", + input: HTTPRoute{ + Hostname: "*", + HeaderMatches: []*StringMatch{ptr.To(StringMatch{})}, + Destination: &happyRouteDestination, + }, + want: []error{ErrHTTPRouteNameEmpty, ErrStringMatchConditionInvalid}, + }, + { + name: "redirect-httproute", + input: redirectHTTPRoute, + want: nil, + }, + { + name: "filter-error-httproute", + input: invalidFilterHTTPRoute, + want: nil, + }, + { + name: "redirect-bad-status-scheme-nopath", + input: redirectFilterInvalidStatus, + want: []error{ErrRedirectUnsupportedStatus, ErrRedirectUnsupportedScheme, ErrHTTPPathModifierNoReplace}, + }, + { + name: "redirect-bad-path", + input: redirectFilterBadPath, + want: []error{ErrHTTPPathModifierDoubleReplace}, + }, + { + name: "direct-response-bad-status", + input: directResponseBadStatus, + want: []error{ErrDirectResponseStatusInvalid}, + }, + { + name: "rewrite-httproute", + input: urlRewriteHTTPRoute, + want: nil, + }, + { + name: "rewrite-bad-path", + input: urlRewriteFilterBadPath, + want: []error{ErrHTTPPathModifierDoubleReplace}, + }, + { + name: "add-request-headers-httproute", + input: addRequestHeaderHTTPRoute, + want: nil, + }, + { + name: "remove-request-headers-httproute", + input: removeRequestHeaderHTTPRoute, + want: nil, + }, + { + name: "add-remove-request-headers-duplicate", + input: addAndRemoveRequestHeadersDupeHTTPRoute, + want: []error{ErrAddHeaderDuplicate, ErrRemoveHeaderDuplicate}, + }, + { + name: "add-request-header-empty", + input: addRequestHeaderEmptyHTTPRoute, + want: []error{ErrAddHeaderEmptyName}, + }, + { + name: "add-response-headers-httproute", + input: addResponseHeaderHTTPRoute, + want: nil, + }, + { + name: "remove-response-headers-httproute", + input: removeResponseHeaderHTTPRoute, + want: nil, + }, + { + name: "add-remove-response-headers-duplicate", + input: addAndRemoveResponseHeadersDupeHTTPRoute, + want: []error{ErrAddHeaderDuplicate, ErrRemoveHeaderDuplicate}, + }, + { + name: "add-response-header-empty", + input: addResponseHeaderEmptyHTTPRoute, + want: []error{ErrAddHeaderEmptyName}, + }, + { + name: "jwt-authen-httproute", + input: jwtAuthenHTTPRoute, + }, + { + name: "mirror-filter", + input: requestMirrorFilter, + want: nil, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + got := test.input.Validate() + for _, w := range test.want { + require.ErrorContains(t, got, w.Error()) + } + } + }) + } +} + +func TestValidateRouteDestination(t *testing.T) { + tests := []struct { + name string + input RouteDestination + want error + }{ + { + name: "happy", + input: happyRouteDestination, + want: nil, + }, + { + name: "valid hostname", + input: RouteDestination{ + Name: "valid hostname", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "example.com", + Port: 8080, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "valid ip", + input: RouteDestination{ + Name: "valid ip", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "1.2.3.4", + Port: 8080, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "invalid address", + input: RouteDestination{ + Name: "invalid address", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "example.com::foo.bar", + Port: 8080, + }, + }, + }, + }, + }, + want: ErrDestEndpointHostInvalid, + }, + { + name: "missing ip", + input: RouteDestination{ + Name: "missing ip", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Port: 8080, + }, + }, + }, + }, + }, + want: ErrDestEndpointHostInvalid, + }, + { + name: "missing port", + input: RouteDestination{ + Name: "missing port", + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + }, + }, + }, + }, + }, + want: ErrDestEndpointPortInvalid, + }, + { + name: "missing name", + input: RouteDestination{ + Settings: []*DestinationSetting{ + { + Endpoints: []*DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, + }, + want: ErrDestinationNameEmpty, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + require.EqualError(t, test.input.Validate(), test.want.Error()) + } + }) + } +} + +func TestValidateStringMatch(t *testing.T) { + tests := []struct { + name string + input StringMatch + want error + }{ + { + name: "happy", + input: StringMatch{ + Exact: ptr.To("example"), + }, + want: nil, + }, + { + name: "no fields set", + input: StringMatch{}, + want: ErrStringMatchConditionInvalid, + }, + { + name: "multiple fields set", + input: StringMatch{ + Exact: ptr.To("example"), + Name: "example", + Prefix: ptr.To("example"), + }, + want: ErrStringMatchConditionInvalid, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + require.EqualError(t, test.input.Validate(), test.want.Error()) + } + }) + } +} + +func TestValidateLoadBalancer(t *testing.T) { + tests := []struct { + name string + input LoadBalancer + want error + }{ + { + name: "random", + input: LoadBalancer{ + Random: &Random{}, + }, + want: nil, + }, + { + name: "consistent hash", + input: LoadBalancer{ + ConsistentHash: &ConsistentHash{ + SourceIP: ptr.To(true), + }, + }, + want: nil, + }, + + { + name: "least request and random set", + input: LoadBalancer{ + Random: &Random{}, + LeastRequest: &LeastRequest{}, + }, + want: ErrLoadBalancerInvalid, + }, + } + for i := range tests { + test := tests[i] + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + require.EqualError(t, test.input.Validate(), test.want.Error()) + } + }) + } +} + +func TestPrintable(t *testing.T) { + tests := []struct { + name string + input Xds + want *Xds + }{ + { + name: "empty", + input: Xds{}, + want: &Xds{}, + }, + { + name: "http", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPListener}, + }, + want: &Xds{ + HTTP: []*HTTPListener{&happyHTTPListener}, + }, + }, + { + name: "https", + input: Xds{ + HTTP: []*HTTPListener{&happyHTTPSListener}, + }, + want: &Xds{ + HTTP: []*HTTPListener{&redactedHappyHTTPSListener}, + }, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, *test.want, *test.input.Printable()) + }) + } +} + +func TestValidateHealthCheck(t *testing.T) { + tests := []struct { + name string + input HealthCheck + want error + }{ + { + name: "invalid timeout", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Duration(0)}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To[uint32](3), + HealthyThreshold: ptr.To[uint32](3), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckTimeoutInvalid, + }, + { + name: "invalid interval", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Duration(0)}, + UnhealthyThreshold: ptr.To[uint32](3), + HealthyThreshold: ptr.To[uint32](3), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodGet), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckIntervalInvalid, + }, + { + name: "invalid unhealthy threshold", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To[uint32](0), + HealthyThreshold: ptr.To[uint32](3), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodPatch), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckUnhealthyThresholdInvalid, + }, + { + name: "invalid healthy threshold", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To[uint32](3), + HealthyThreshold: ptr.To[uint32](0), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodPost), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckHealthyThresholdInvalid, + }, + { + name: "http-health-check: invalid host", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To[uint32](3), + HealthyThreshold: ptr.To[uint32](3), + HTTP: &HTTPHealthChecker{ + Path: "/healthz", + Method: ptr.To(http.MethodPut), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHCHTTPHostInvalid, + }, + { + name: "http-health-check: invalid path", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To[uint32](3), + HealthyThreshold: ptr.To[uint32](3), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "", + Method: ptr.To(http.MethodPut), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHCHTTPPathInvalid, + }, + { + name: "http-health-check: invalid method", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodConnect), + ExpectedStatuses: []HTTPStatus{200, 400}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHCHTTPMethodInvalid, + }, + { + name: "http-health-check: invalid expected-statuses", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodDelete), + ExpectedStatuses: []HTTPStatus{}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHCHTTPExpectedStatusesInvalid, + }, + { + name: "http-health-check: invalid range", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodHead), + ExpectedStatuses: []HTTPStatus{100, 600}, + }, + }, + &OutlierDetection{}, + }, + want: ErrHTTPStatusInvalid, + }, + { + name: "http-health-check: invalid expected-responses", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + HTTP: &HTTPHealthChecker{ + Host: "*", + Path: "/healthz", + Method: ptr.To(http.MethodOptions), + ExpectedStatuses: []HTTPStatus{200, 300}, + ExpectedResponse: &HealthCheckPayload{ + Text: ptr.To("foo"), + Binary: []byte{'f', 'o', 'o'}, + }, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckPayloadInvalid, + }, + { + name: "tcp-health-check: invalid send payload", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + TCP: &TCPHealthChecker{ + Send: &HealthCheckPayload{ + Text: ptr.To("foo"), + Binary: []byte{'f', 'o', 'o'}, + }, + Receive: &HealthCheckPayload{ + Text: ptr.To("foo"), + }, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckPayloadInvalid, + }, + { + name: "tcp-health-check: invalid receive payload", + input: HealthCheck{&ActiveHealthCheck{ + Timeout: &metav1.Duration{Duration: time.Second}, + Interval: &metav1.Duration{Duration: time.Second}, + UnhealthyThreshold: ptr.To(uint32(3)), + HealthyThreshold: ptr.To(uint32(3)), + TCP: &TCPHealthChecker{ + Send: &HealthCheckPayload{ + Text: ptr.To("foo"), + }, + Receive: &HealthCheckPayload{ + Text: ptr.To("foo"), + Binary: []byte{'f', 'o', 'o'}, + }, + }, + }, + &OutlierDetection{}, + }, + want: ErrHealthCheckPayloadInvalid, + }, + { + name: "OutlierDetection invalid interval", + input: HealthCheck{&ActiveHealthCheck{}, + &OutlierDetection{ + Interval: &metav1.Duration{Duration: time.Duration(0)}, + BaseEjectionTime: &metav1.Duration{Duration: time.Second}, + }, + }, + want: ErrOutlierDetectionIntervalInvalid, + }, + { + name: "OutlierDetection invalid BaseEjectionTime", + input: HealthCheck{&ActiveHealthCheck{}, + &OutlierDetection{ + Interval: &metav1.Duration{Duration: time.Second}, + BaseEjectionTime: &metav1.Duration{Duration: time.Duration(0)}, + }, + }, + want: ErrOutlierDetectionBaseEjectionTimeInvalid, + }, + } + for i := range tests { + test := tests[i] + t.Run(test.name, func(t *testing.T) { + if test.want == nil { + require.NoError(t, test.input.Validate()) + } else { + require.EqualError(t, test.input.Validate(), test.want.Error()) + } + }) + } +} diff --git a/adapter/internal/operator/gateway-api/ir/zz_generated.deepcopy.go b/adapter/internal/operator/gateway-api/ir/zz_generated.deepcopy.go new file mode 100644 index 0000000000..05c2e8eee8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/ir/zz_generated.deepcopy.go @@ -0,0 +1,2221 @@ +//go:build !ignore_autogenerated + +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package ir + +import ( + "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AccessLog) DeepCopyInto(out *AccessLog) { + *out = *in + if in.Text != nil { + in, out := &in.Text, &out.Text + *out = make([]*TextAccessLog, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TextAccessLog) + (*in).DeepCopyInto(*out) + } + } + } + if in.JSON != nil { + in, out := &in.JSON, &out.JSON + *out = make([]*JSONAccessLog, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(JSONAccessLog) + (*in).DeepCopyInto(*out) + } + } + } + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = make([]*OpenTelemetryAccessLog, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(OpenTelemetryAccessLog) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessLog. +func (in *AccessLog) DeepCopy() *AccessLog { + if in == nil { + return nil + } + out := new(AccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ActiveHealthCheck) DeepCopyInto(out *ActiveHealthCheck) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1.Duration) + **out = **in + } + if in.UnhealthyThreshold != nil { + in, out := &in.UnhealthyThreshold, &out.UnhealthyThreshold + *out = new(uint32) + **out = **in + } + if in.HealthyThreshold != nil { + in, out := &in.HealthyThreshold, &out.HealthyThreshold + *out = new(uint32) + **out = **in + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPHealthChecker) + (*in).DeepCopyInto(*out) + } + if in.TCP != nil { + in, out := &in.TCP, &out.TCP + *out = new(TCPHealthChecker) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveHealthCheck. +func (in *ActiveHealthCheck) DeepCopy() *ActiveHealthCheck { + if in == nil { + return nil + } + out := new(ActiveHealthCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddHeader) DeepCopyInto(out *AddHeader) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddHeader. +func (in *AddHeader) DeepCopy() *AddHeader { + if in == nil { + return nil + } + out := new(AddHeader) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackOffPolicy) DeepCopyInto(out *BackOffPolicy) { + *out = *in + if in.BaseInterval != nil { + in, out := &in.BaseInterval, &out.BaseInterval + *out = new(v1.Duration) + **out = **in + } + if in.MaxInterval != nil { + in, out := &in.MaxInterval, &out.MaxInterval + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackOffPolicy. +func (in *BackOffPolicy) DeepCopy() *BackOffPolicy { + if in == nil { + return nil + } + out := new(BackOffPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendWeights) DeepCopyInto(out *BackendWeights) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendWeights. +func (in *BackendWeights) DeepCopy() *BackendWeights { + if in == nil { + return nil + } + out := new(BackendWeights) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuth) DeepCopyInto(out *BasicAuth) { + *out = *in + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth. +func (in *BasicAuth) DeepCopy() *BasicAuth { + if in == nil { + return nil + } + out := new(BasicAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CORS) DeepCopyInto(out *CORS) { + *out = *in + if in.AllowOrigins != nil { + in, out := &in.AllowOrigins, &out.AllowOrigins + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } + if in.AllowMethods != nil { + in, out := &in.AllowMethods, &out.AllowMethods + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AllowHeaders != nil { + in, out := &in.AllowHeaders, &out.AllowHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExposeHeaders != nil { + in, out := &in.ExposeHeaders, &out.ExposeHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MaxAge != nil { + in, out := &in.MaxAge, &out.MaxAge + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CORS. +func (in *CORS) DeepCopy() *CORS { + if in == nil { + return nil + } + out := new(CORS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CircuitBreaker) DeepCopyInto(out *CircuitBreaker) { + *out = *in + if in.MaxConnections != nil { + in, out := &in.MaxConnections, &out.MaxConnections + *out = new(uint32) + **out = **in + } + if in.MaxPendingRequests != nil { + in, out := &in.MaxPendingRequests, &out.MaxPendingRequests + *out = new(uint32) + **out = **in + } + if in.MaxParallelRequests != nil { + in, out := &in.MaxParallelRequests, &out.MaxParallelRequests + *out = new(uint32) + **out = **in + } + if in.MaxRequestsPerConnection != nil { + in, out := &in.MaxRequestsPerConnection, &out.MaxRequestsPerConnection + *out = new(uint32) + **out = **in + } + if in.MaxParallelRetries != nil { + in, out := &in.MaxParallelRetries, &out.MaxParallelRetries + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreaker. +func (in *CircuitBreaker) DeepCopy() *CircuitBreaker { + if in == nil { + return nil + } + out := new(CircuitBreaker) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientTimeout) DeepCopyInto(out *ClientTimeout) { + *out = *in + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPClientTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTimeout. +func (in *ClientTimeout) DeepCopy() *ClientTimeout { + if in == nil { + return nil + } + out := new(ClientTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConsistentHash) DeepCopyInto(out *ConsistentHash) { + *out = *in + if in.SourceIP != nil { + in, out := &in.SourceIP, &out.SourceIP + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsistentHash. +func (in *ConsistentHash) DeepCopy() *ConsistentHash { + if in == nil { + return nil + } + out := new(ConsistentHash) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DestinationEndpoint) DeepCopyInto(out *DestinationEndpoint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationEndpoint. +func (in *DestinationEndpoint) DeepCopy() *DestinationEndpoint { + if in == nil { + return nil + } + out := new(DestinationEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DestinationSetting) DeepCopyInto(out *DestinationSetting) { + *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(uint32) + **out = **in + } + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]*DestinationEndpoint, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DestinationEndpoint) + **out = **in + } + } + } + if in.AddressType != nil { + in, out := &in.AddressType, &out.AddressType + *out = new(DestinationAddressType) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSUpstreamConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationSetting. +func (in *DestinationSetting) DeepCopy() *DestinationSetting { + if in == nil { + return nil + } + out := new(DestinationSetting) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DirectResponse) DeepCopyInto(out *DirectResponse) { + *out = *in + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DirectResponse. +func (in *DirectResponse) DeepCopy() *DirectResponse { + if in == nil { + return nil + } + out := new(DirectResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtAuth) DeepCopyInto(out *ExtAuth) { + *out = *in + if in.GRPC != nil { + in, out := &in.GRPC, &out.GRPC + *out = new(GRPCExtAuthService) + (*in).DeepCopyInto(*out) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPExtAuthService) + (*in).DeepCopyInto(*out) + } + if in.HeadersToExtAuth != nil { + in, out := &in.HeadersToExtAuth, &out.HeadersToExtAuth + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtAuth. +func (in *ExtAuth) DeepCopy() *ExtAuth { + if in == nil { + return nil + } + out := new(ExtAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FaultInjection) DeepCopyInto(out *FaultInjection) { + *out = *in + if in.Delay != nil { + in, out := &in.Delay, &out.Delay + *out = new(FaultInjectionDelay) + (*in).DeepCopyInto(*out) + } + if in.Abort != nil { + in, out := &in.Abort, &out.Abort + *out = new(FaultInjectionAbort) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultInjection. +func (in *FaultInjection) DeepCopy() *FaultInjection { + if in == nil { + return nil + } + out := new(FaultInjection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FaultInjectionAbort) DeepCopyInto(out *FaultInjectionAbort) { + *out = *in + if in.HTTPStatus != nil { + in, out := &in.HTTPStatus, &out.HTTPStatus + *out = new(int32) + **out = **in + } + if in.GrpcStatus != nil { + in, out := &in.GrpcStatus, &out.GrpcStatus + *out = new(int32) + **out = **in + } + if in.Percentage != nil { + in, out := &in.Percentage, &out.Percentage + *out = new(float32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultInjectionAbort. +func (in *FaultInjectionAbort) DeepCopy() *FaultInjectionAbort { + if in == nil { + return nil + } + out := new(FaultInjectionAbort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FaultInjectionDelay) DeepCopyInto(out *FaultInjectionDelay) { + *out = *in + if in.FixedDelay != nil { + in, out := &in.FixedDelay, &out.FixedDelay + *out = new(v1.Duration) + **out = **in + } + if in.Percentage != nil { + in, out := &in.Percentage, &out.Percentage + *out = new(float32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultInjectionDelay. +func (in *FaultInjectionDelay) DeepCopy() *FaultInjectionDelay { + if in == nil { + return nil + } + out := new(FaultInjectionDelay) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GRPCExtAuthService) DeepCopyInto(out *GRPCExtAuthService) { + *out = *in + in.Destination.DeepCopyInto(&out.Destination) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCExtAuthService. +func (in *GRPCExtAuthService) DeepCopy() *GRPCExtAuthService { + if in == nil { + return nil + } + out := new(GRPCExtAuthService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]*RateLimitRule, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RateLimitRule) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRateLimit. +func (in *GlobalRateLimit) DeepCopy() *GlobalRateLimit { + if in == nil { + return nil + } + out := new(GlobalRateLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTP10Settings) DeepCopyInto(out *HTTP10Settings) { + *out = *in + if in.DefaultHost != nil { + in, out := &in.DefaultHost, &out.DefaultHost + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTP10Settings. +func (in *HTTP10Settings) DeepCopy() *HTTP10Settings { + if in == nil { + return nil + } + out := new(HTTP10Settings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTP1Settings) DeepCopyInto(out *HTTP1Settings) { + *out = *in + if in.HTTP10 != nil { + in, out := &in.HTTP10, &out.HTTP10 + *out = new(HTTP10Settings) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTP1Settings. +func (in *HTTP1Settings) DeepCopy() *HTTP1Settings { + if in == nil { + return nil + } + out := new(HTTP1Settings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPClientTimeout) DeepCopyInto(out *HTTPClientTimeout) { + *out = *in + if in.RequestReceivedTimeout != nil { + in, out := &in.RequestReceivedTimeout, &out.RequestReceivedTimeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPClientTimeout. +func (in *HTTPClientTimeout) DeepCopy() *HTTPClientTimeout { + if in == nil { + return nil + } + out := new(HTTPClientTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPExtAuthService) DeepCopyInto(out *HTTPExtAuthService) { + *out = *in + in.Destination.DeepCopyInto(&out.Destination) + if in.HeadersToBackend != nil { + in, out := &in.HeadersToBackend, &out.HeadersToBackend + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPExtAuthService. +func (in *HTTPExtAuthService) DeepCopy() *HTTPExtAuthService { + if in == nil { + return nil + } + out := new(HTTPExtAuthService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPHealthChecker) DeepCopyInto(out *HTTPHealthChecker) { + *out = *in + if in.Method != nil { + in, out := &in.Method, &out.Method + *out = new(string) + **out = **in + } + if in.ExpectedStatuses != nil { + in, out := &in.ExpectedStatuses, &out.ExpectedStatuses + *out = make([]HTTPStatus, len(*in)) + copy(*out, *in) + } + if in.ExpectedResponse != nil { + in, out := &in.ExpectedResponse, &out.ExpectedResponse + *out = new(HealthCheckPayload) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHealthChecker. +func (in *HTTPHealthChecker) DeepCopy() *HTTPHealthChecker { + if in == nil { + return nil + } + out := new(HTTPHealthChecker) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPListener) DeepCopyInto(out *HTTPListener) { + *out = *in + if in.Hostnames != nil { + in, out := &in.Hostnames, &out.Hostnames + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } + if in.Routes != nil { + in, out := &in.Routes, &out.Routes + *out = make([]*HTTPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(HTTPRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = new(HeaderSettings) + **out = **in + } + if in.HTTP3 != nil { + in, out := &in.HTTP3, &out.HTTP3 + *out = new(HTTP3Settings) + **out = **in + } + out.Path = in.Path + if in.HTTP1 != nil { + in, out := &in.HTTP1, &out.HTTP1 + *out = new(HTTP1Settings) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(ClientTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPListener. +func (in *HTTPListener) DeepCopy() *HTTPListener { + if in == nil { + return nil + } + out := new(HTTPListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPPathModifier) DeepCopyInto(out *HTTPPathModifier) { + *out = *in + if in.FullReplace != nil { + in, out := &in.FullReplace, &out.FullReplace + *out = new(string) + **out = **in + } + if in.PrefixMatchReplace != nil { + in, out := &in.PrefixMatchReplace, &out.PrefixMatchReplace + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPathModifier. +func (in *HTTPPathModifier) DeepCopy() *HTTPPathModifier { + if in == nil { + return nil + } + out := new(HTTPPathModifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { + *out = *in + if in.PathMatch != nil { + in, out := &in.PathMatch, &out.PathMatch + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + if in.HeaderMatches != nil { + in, out := &in.HeaderMatches, &out.HeaderMatches + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } + if in.QueryParamMatches != nil { + in, out := &in.QueryParamMatches, &out.QueryParamMatches + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } + out.BackendWeights = in.BackendWeights + if in.AddRequestHeaders != nil { + in, out := &in.AddRequestHeaders, &out.AddRequestHeaders + *out = make([]AddHeader, len(*in)) + copy(*out, *in) + } + if in.RemoveRequestHeaders != nil { + in, out := &in.RemoveRequestHeaders, &out.RemoveRequestHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AddResponseHeaders != nil { + in, out := &in.AddResponseHeaders, &out.AddResponseHeaders + *out = make([]AddHeader, len(*in)) + copy(*out, *in) + } + if in.RemoveResponseHeaders != nil { + in, out := &in.RemoveResponseHeaders, &out.RemoveResponseHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DirectResponse != nil { + in, out := &in.DirectResponse, &out.DirectResponse + *out = new(DirectResponse) + (*in).DeepCopyInto(*out) + } + if in.Redirect != nil { + in, out := &in.Redirect, &out.Redirect + *out = new(Redirect) + (*in).DeepCopyInto(*out) + } + if in.Mirrors != nil { + in, out := &in.Mirrors, &out.Mirrors + *out = make([]*RouteDestination, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } + } + } + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } + if in.URLRewrite != nil { + in, out := &in.URLRewrite, &out.URLRewrite + *out = new(URLRewrite) + (*in).DeepCopyInto(*out) + } + if in.RateLimit != nil { + in, out := &in.RateLimit, &out.RateLimit + *out = new(RateLimit) + (*in).DeepCopyInto(*out) + } + if in.LoadBalancer != nil { + in, out := &in.LoadBalancer, &out.LoadBalancer + *out = new(LoadBalancer) + (*in).DeepCopyInto(*out) + } + if in.CORS != nil { + in, out := &in.CORS, &out.CORS + *out = new(CORS) + (*in).DeepCopyInto(*out) + } + if in.OIDC != nil { + in, out := &in.OIDC, &out.OIDC + *out = new(OIDC) + (*in).DeepCopyInto(*out) + } + if in.ProxyProtocol != nil { + in, out := &in.ProxyProtocol, &out.ProxyProtocol + *out = new(ProxyProtocol) + **out = **in + } + if in.BasicAuth != nil { + in, out := &in.BasicAuth, &out.BasicAuth + *out = new(BasicAuth) + (*in).DeepCopyInto(*out) + } + if in.ExtAuth != nil { + in, out := &in.ExtAuth, &out.ExtAuth + *out = new(ExtAuth) + (*in).DeepCopyInto(*out) + } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(HealthCheck) + (*in).DeepCopyInto(*out) + } + if in.FaultInjection != nil { + in, out := &in.FaultInjection, &out.FaultInjection + *out = new(FaultInjection) + (*in).DeepCopyInto(*out) + } + if in.ExtensionRefs != nil { + in, out := &in.ExtensionRefs, &out.ExtensionRefs + *out = make([]*UnstructuredRef, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(UnstructuredRef) + (*in).DeepCopyInto(*out) + } + } + } + if in.CircuitBreaker != nil { + in, out := &in.CircuitBreaker, &out.CircuitBreaker + *out = new(CircuitBreaker) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(Timeout) + (*in).DeepCopyInto(*out) + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } + if in.Retry != nil { + in, out := &in.Retry, &out.Retry + *out = new(Retry) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. +func (in *HTTPRoute) DeepCopy() *HTTPRoute { + if in == nil { + return nil + } + out := new(HTTPRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) { + *out = *in + if in.RequestTimeout != nil { + in, out := &in.RequestTimeout, &out.RequestTimeout + *out = new(v1.Duration) + **out = **in + } + if in.ConnectionIdleTimeout != nil { + in, out := &in.ConnectionIdleTimeout, &out.ConnectionIdleTimeout + *out = new(v1.Duration) + **out = **in + } + if in.MaxConnectionDuration != nil { + in, out := &in.MaxConnectionDuration, &out.MaxConnectionDuration + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTimeout. +func (in *HTTPTimeout) DeepCopy() *HTTPTimeout { + if in == nil { + return nil + } + out := new(HTTPTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HeaderSettings) DeepCopyInto(out *HeaderSettings) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderSettings. +func (in *HeaderSettings) DeepCopy() *HeaderSettings { + if in == nil { + return nil + } + out := new(HeaderSettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthCheck) DeepCopyInto(out *HealthCheck) { + *out = *in + if in.Active != nil { + in, out := &in.Active, &out.Active + *out = new(ActiveHealthCheck) + (*in).DeepCopyInto(*out) + } + if in.Passive != nil { + in, out := &in.Passive, &out.Passive + *out = new(OutlierDetection) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthCheck. +func (in *HealthCheck) DeepCopy() *HealthCheck { + if in == nil { + return nil + } + out := new(HealthCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthCheckPayload) DeepCopyInto(out *HealthCheckPayload) { + *out = *in + if in.Text != nil { + in, out := &in.Text, &out.Text + *out = new(string) + **out = **in + } + if in.Binary != nil { + in, out := &in.Binary, &out.Binary + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthCheckPayload. +func (in *HealthCheckPayload) DeepCopy() *HealthCheckPayload { + if in == nil { + return nil + } + out := new(HealthCheckPayload) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Infra) DeepCopyInto(out *Infra) { + *out = *in + if in.Proxy != nil { + in, out := &in.Proxy, &out.Proxy + *out = new(ProxyInfra) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Infra. +func (in *Infra) DeepCopy() *Infra { + if in == nil { + return nil + } + out := new(Infra) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfraMetadata) DeepCopyInto(out *InfraMetadata) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfraMetadata. +func (in *InfraMetadata) DeepCopy() *InfraMetadata { + if in == nil { + return nil + } + out := new(InfraMetadata) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONAccessLog) DeepCopyInto(out *JSONAccessLog) { + *out = *in + if in.JSON != nil { + in, out := &in.JSON, &out.JSON + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONAccessLog. +func (in *JSONAccessLog) DeepCopy() *JSONAccessLog { + if in == nil { + return nil + } + out := new(JSONAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONPatchConfig) DeepCopyInto(out *JSONPatchConfig) { + *out = *in + in.Operation.DeepCopyInto(&out.Operation) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONPatchConfig. +func (in *JSONPatchConfig) DeepCopy() *JSONPatchConfig { + if in == nil { + return nil + } + out := new(JSONPatchConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONPatchOperation) DeepCopyInto(out *JSONPatchOperation) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = new(string) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(apiextensionsv1.JSON) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONPatchOperation. +func (in *JSONPatchOperation) DeepCopy() *JSONPatchOperation { + if in == nil { + return nil + } + out := new(JSONPatchOperation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LeastRequest) DeepCopyInto(out *LeastRequest) { + *out = *in + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeastRequest. +func (in *LeastRequest) DeepCopy() *LeastRequest { + if in == nil { + return nil + } + out := new(LeastRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ListenerPort) DeepCopyInto(out *ListenerPort) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListenerPort. +func (in *ListenerPort) DeepCopy() *ListenerPort { + if in == nil { + return nil + } + out := new(ListenerPort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { + *out = *in + if in.RoundRobin != nil { + in, out := &in.RoundRobin, &out.RoundRobin + *out = new(RoundRobin) + (*in).DeepCopyInto(*out) + } + if in.LeastRequest != nil { + in, out := &in.LeastRequest, &out.LeastRequest + *out = new(LeastRequest) + (*in).DeepCopyInto(*out) + } + if in.Random != nil { + in, out := &in.Random, &out.Random + *out = new(Random) + **out = **in + } + if in.ConsistentHash != nil { + in, out := &in.ConsistentHash, &out.ConsistentHash + *out = new(ConsistentHash) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. +func (in *LoadBalancer) DeepCopy() *LoadBalancer { + if in == nil { + return nil + } + out := new(LoadBalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalRateLimit) DeepCopyInto(out *LocalRateLimit) { + *out = *in + out.Default = in.Default + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]*RateLimitRule, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RateLimitRule) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalRateLimit. +func (in *LocalRateLimit) DeepCopy() *LocalRateLimit { + if in == nil { + return nil + } + out := new(LocalRateLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Metrics) DeepCopyInto(out *Metrics) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metrics. +func (in *Metrics) DeepCopy() *Metrics { + if in == nil { + return nil + } + out := new(Metrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDC) DeepCopyInto(out *OIDC) { + *out = *in + out.Provider = in.Provider + if in.ClientSecret != nil { + in, out := &in.ClientSecret, &out.ClientSecret + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.HMACSecret != nil { + in, out := &in.HMACSecret, &out.HMACSecret + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.Scopes != nil { + in, out := &in.Scopes, &out.Scopes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDC. +func (in *OIDC) DeepCopy() *OIDC { + if in == nil { + return nil + } + out := new(OIDC) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryAccessLog) DeepCopyInto(out *OpenTelemetryAccessLog) { + *out = *in + if in.Text != nil { + in, out := &in.Text, &out.Text + *out = new(string) + **out = **in + } + if in.Attributes != nil { + in, out := &in.Attributes, &out.Attributes + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryAccessLog. +func (in *OpenTelemetryAccessLog) DeepCopy() *OpenTelemetryAccessLog { + if in == nil { + return nil + } + out := new(OpenTelemetryAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OutlierDetection) DeepCopyInto(out *OutlierDetection) { + *out = *in + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1.Duration) + **out = **in + } + if in.SplitExternalLocalOriginErrors != nil { + in, out := &in.SplitExternalLocalOriginErrors, &out.SplitExternalLocalOriginErrors + *out = new(bool) + **out = **in + } + if in.ConsecutiveLocalOriginFailures != nil { + in, out := &in.ConsecutiveLocalOriginFailures, &out.ConsecutiveLocalOriginFailures + *out = new(uint32) + **out = **in + } + if in.ConsecutiveGatewayErrors != nil { + in, out := &in.ConsecutiveGatewayErrors, &out.ConsecutiveGatewayErrors + *out = new(uint32) + **out = **in + } + if in.Consecutive5xxErrors != nil { + in, out := &in.Consecutive5xxErrors, &out.Consecutive5xxErrors + *out = new(uint32) + **out = **in + } + if in.BaseEjectionTime != nil { + in, out := &in.BaseEjectionTime, &out.BaseEjectionTime + *out = new(v1.Duration) + **out = **in + } + if in.MaxEjectionPercent != nil { + in, out := &in.MaxEjectionPercent, &out.MaxEjectionPercent + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OutlierDetection. +func (in *OutlierDetection) DeepCopy() *OutlierDetection { + if in == nil { + return nil + } + out := new(OutlierDetection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PathSettings) DeepCopyInto(out *PathSettings) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PathSettings. +func (in *PathSettings) DeepCopy() *PathSettings { + if in == nil { + return nil + } + out := new(PathSettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PerRetryPolicy) DeepCopyInto(out *PerRetryPolicy) { + *out = *in + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.BackOff != nil { + in, out := &in.BackOff, &out.BackOff + *out = new(BackOffPolicy) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PerRetryPolicy. +func (in *PerRetryPolicy) DeepCopy() *PerRetryPolicy { + if in == nil { + return nil + } + out := new(PerRetryPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyInfra) DeepCopyInto(out *ProxyInfra) { + *out = *in + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(InfraMetadata) + (*in).DeepCopyInto(*out) + } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(v1alpha1.EnvoyProxy) + (*in).DeepCopyInto(*out) + } + if in.Listeners != nil { + in, out := &in.Listeners, &out.Listeners + *out = make([]*ProxyListener, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ProxyListener) + (*in).DeepCopyInto(*out) + } + } + } + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyInfra. +func (in *ProxyInfra) DeepCopy() *ProxyInfra { + if in == nil { + return nil + } + out := new(ProxyInfra) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyListener) DeepCopyInto(out *ProxyListener) { + *out = *in + if in.Address != nil { + in, out := &in.Address, &out.Address + *out = new(string) + **out = **in + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]ListenerPort, len(*in)) + copy(*out, *in) + } + if in.HTTP3 != nil { + in, out := &in.HTTP3, &out.HTTP3 + *out = new(HTTP3Settings) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyListener. +func (in *ProxyListener) DeepCopy() *ProxyListener { + if in == nil { + return nil + } + out := new(ProxyListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyProtocol) DeepCopyInto(out *ProxyProtocol) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyProtocol. +func (in *ProxyProtocol) DeepCopy() *ProxyProtocol { + if in == nil { + return nil + } + out := new(ProxyProtocol) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Random) DeepCopyInto(out *Random) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Random. +func (in *Random) DeepCopy() *Random { + if in == nil { + return nil + } + out := new(Random) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimit) DeepCopyInto(out *RateLimit) { + *out = *in + if in.Global != nil { + in, out := &in.Global, &out.Global + *out = new(GlobalRateLimit) + (*in).DeepCopyInto(*out) + } + if in.Local != nil { + in, out := &in.Local, &out.Local + *out = new(LocalRateLimit) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimit. +func (in *RateLimit) DeepCopy() *RateLimit { + if in == nil { + return nil + } + out := new(RateLimit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitRule) DeepCopyInto(out *RateLimitRule) { + *out = *in + if in.HeaderMatches != nil { + in, out := &in.HeaderMatches, &out.HeaderMatches + *out = make([]*StringMatch, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StringMatch) + (*in).DeepCopyInto(*out) + } + } + } + if in.CIDRMatch != nil { + in, out := &in.CIDRMatch, &out.CIDRMatch + *out = new(CIDRMatch) + **out = **in + } + out.Limit = in.Limit +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitRule. +func (in *RateLimitRule) DeepCopy() *RateLimitRule { + if in == nil { + return nil + } + out := new(RateLimitRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitValue) DeepCopyInto(out *RateLimitValue) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitValue. +func (in *RateLimitValue) DeepCopy() *RateLimitValue { + if in == nil { + return nil + } + out := new(RateLimitValue) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Redirect) DeepCopyInto(out *Redirect) { + *out = *in + if in.Scheme != nil { + in, out := &in.Scheme, &out.Scheme + *out = new(string) + **out = **in + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(string) + **out = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(HTTPPathModifier) + (*in).DeepCopyInto(*out) + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(uint32) + **out = **in + } + if in.StatusCode != nil { + in, out := &in.StatusCode, &out.StatusCode + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Redirect. +func (in *Redirect) DeepCopy() *Redirect { + if in == nil { + return nil + } + out := new(Redirect) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Retry) DeepCopyInto(out *Retry) { + *out = *in + if in.NumRetries != nil { + in, out := &in.NumRetries, &out.NumRetries + *out = new(uint32) + **out = **in + } + if in.RetryOn != nil { + in, out := &in.RetryOn, &out.RetryOn + *out = new(RetryOn) + (*in).DeepCopyInto(*out) + } + if in.PerRetry != nil { + in, out := &in.PerRetry, &out.PerRetry + *out = new(PerRetryPolicy) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Retry. +func (in *Retry) DeepCopy() *Retry { + if in == nil { + return nil + } + out := new(Retry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RetryOn) DeepCopyInto(out *RetryOn) { + *out = *in + if in.Triggers != nil { + in, out := &in.Triggers, &out.Triggers + *out = make([]TriggerEnum, len(*in)) + copy(*out, *in) + } + if in.HTTPStatusCodes != nil { + in, out := &in.HTTPStatusCodes, &out.HTTPStatusCodes + *out = make([]HTTPStatus, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryOn. +func (in *RetryOn) DeepCopy() *RetryOn { + if in == nil { + return nil + } + out := new(RetryOn) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RoundRobin) DeepCopyInto(out *RoundRobin) { + *out = *in + if in.SlowStart != nil { + in, out := &in.SlowStart, &out.SlowStart + *out = new(SlowStart) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoundRobin. +func (in *RoundRobin) DeepCopy() *RoundRobin { + if in == nil { + return nil + } + out := new(RoundRobin) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteDestination) DeepCopyInto(out *RouteDestination) { + *out = *in + if in.Settings != nil { + in, out := &in.Settings, &out.Settings + *out = make([]*DestinationSetting, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DestinationSetting) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteDestination. +func (in *RouteDestination) DeepCopy() *RouteDestination { + if in == nil { + return nil + } + out := new(RouteDestination) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlowStart) DeepCopyInto(out *SlowStart) { + *out = *in + if in.Window != nil { + in, out := &in.Window, &out.Window + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowStart. +func (in *SlowStart) DeepCopy() *SlowStart { + if in == nil { + return nil + } + out := new(SlowStart) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StringMatch) DeepCopyInto(out *StringMatch) { + *out = *in + if in.Exact != nil { + in, out := &in.Exact, &out.Exact + *out = new(string) + **out = **in + } + if in.Prefix != nil { + in, out := &in.Prefix, &out.Prefix + *out = new(string) + **out = **in + } + if in.Suffix != nil { + in, out := &in.Suffix, &out.Suffix + *out = new(string) + **out = **in + } + if in.SafeRegex != nil { + in, out := &in.SafeRegex, &out.SafeRegex + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. +func (in *StringMatch) DeepCopy() *StringMatch { + if in == nil { + return nil + } + out := new(StringMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPHealthChecker) DeepCopyInto(out *TCPHealthChecker) { + *out = *in + if in.Send != nil { + in, out := &in.Send, &out.Send + *out = new(HealthCheckPayload) + (*in).DeepCopyInto(*out) + } + if in.Receive != nil { + in, out := &in.Receive, &out.Receive + *out = new(HealthCheckPayload) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPHealthChecker. +func (in *TCPHealthChecker) DeepCopy() *TCPHealthChecker { + if in == nil { + return nil + } + out := new(TCPHealthChecker) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPKeepalive) DeepCopyInto(out *TCPKeepalive) { + *out = *in + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(uint32) + **out = **in + } + if in.IdleTime != nil { + in, out := &in.IdleTime, &out.IdleTime + *out = new(uint32) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPKeepalive. +func (in *TCPKeepalive) DeepCopy() *TCPKeepalive { + if in == nil { + return nil + } + out := new(TCPKeepalive) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPListener) DeepCopyInto(out *TCPListener) { + *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLS) + (*in).DeepCopyInto(*out) + } + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPListener. +func (in *TCPListener) DeepCopy() *TCPListener { + if in == nil { + return nil + } + out := new(TCPListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPTimeout) DeepCopyInto(out *TCPTimeout) { + *out = *in + if in.ConnectTimeout != nil { + in, out := &in.ConnectTimeout, &out.ConnectTimeout + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPTimeout. +func (in *TCPTimeout) DeepCopy() *TCPTimeout { + if in == nil { + return nil + } + out := new(TCPTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLS) DeepCopyInto(out *TLS) { + *out = *in + if in.Passthrough != nil { + in, out := &in.Passthrough, &out.Passthrough + *out = new(TLSInspectorConfig) + (*in).DeepCopyInto(*out) + } + if in.Terminate != nil { + in, out := &in.Terminate, &out.Terminate + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS. +func (in *TLS) DeepCopy() *TLS { + if in == nil { + return nil + } + out := new(TLS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSCACertificate) DeepCopyInto(out *TLSCACertificate) { + *out = *in + if in.Certificate != nil { + in, out := &in.Certificate, &out.Certificate + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCACertificate. +func (in *TLSCACertificate) DeepCopy() *TLSCACertificate { + if in == nil { + return nil + } + out := new(TLSCACertificate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSCertificate) DeepCopyInto(out *TLSCertificate) { + *out = *in + if in.ServerCertificate != nil { + in, out := &in.ServerCertificate, &out.ServerCertificate + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.PrivateKey != nil { + in, out := &in.PrivateKey, &out.PrivateKey + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCertificate. +func (in *TLSCertificate) DeepCopy() *TLSCertificate { + if in == nil { + return nil + } + out := new(TLSCertificate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { + *out = *in + if in.Certificates != nil { + in, out := &in.Certificates, &out.Certificates + *out = make([]TLSCertificate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.CACertificate != nil { + in, out := &in.CACertificate, &out.CACertificate + *out = new(TLSCACertificate) + (*in).DeepCopyInto(*out) + } + if in.MinVersion != nil { + in, out := &in.MinVersion, &out.MinVersion + *out = new(TLSVersion) + **out = **in + } + if in.MaxVersion != nil { + in, out := &in.MaxVersion, &out.MaxVersion + *out = new(TLSVersion) + **out = **in + } + if in.Ciphers != nil { + in, out := &in.Ciphers, &out.Ciphers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ECDHCurves != nil { + in, out := &in.ECDHCurves, &out.ECDHCurves + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SignatureAlgorithms != nil { + in, out := &in.SignatureAlgorithms, &out.SignatureAlgorithms + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ALPNProtocols != nil { + in, out := &in.ALPNProtocols, &out.ALPNProtocols + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig. +func (in *TLSConfig) DeepCopy() *TLSConfig { + if in == nil { + return nil + } + out := new(TLSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSInspectorConfig) DeepCopyInto(out *TLSInspectorConfig) { + *out = *in + if in.SNIs != nil { + in, out := &in.SNIs, &out.SNIs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSInspectorConfig. +func (in *TLSInspectorConfig) DeepCopy() *TLSInspectorConfig { + if in == nil { + return nil + } + out := new(TLSInspectorConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSUpstreamConfig) DeepCopyInto(out *TLSUpstreamConfig) { + *out = *in + if in.CACertificate != nil { + in, out := &in.CACertificate, &out.CACertificate + *out = new(TLSCACertificate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSUpstreamConfig. +func (in *TLSUpstreamConfig) DeepCopy() *TLSUpstreamConfig { + if in == nil { + return nil + } + out := new(TLSUpstreamConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TextAccessLog) DeepCopyInto(out *TextAccessLog) { + *out = *in + if in.Format != nil { + in, out := &in.Format, &out.Format + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TextAccessLog. +func (in *TextAccessLog) DeepCopy() *TextAccessLog { + if in == nil { + return nil + } + out := new(TextAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timeout) DeepCopyInto(out *Timeout) { + *out = *in + if in.TCP != nil { + in, out := &in.TCP, &out.TCP + *out = new(TCPTimeout) + (*in).DeepCopyInto(*out) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timeout. +func (in *Timeout) DeepCopy() *Timeout { + if in == nil { + return nil + } + out := new(Timeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Tracing) DeepCopyInto(out *Tracing) { + *out = *in + in.ProxyTracing.DeepCopyInto(&out.ProxyTracing) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tracing. +func (in *Tracing) DeepCopy() *Tracing { + if in == nil { + return nil + } + out := new(Tracing) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UDPListener) DeepCopyInto(out *UDPListener) { + *out = *in + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UDPListener. +func (in *UDPListener) DeepCopy() *UDPListener { + if in == nil { + return nil + } + out := new(UDPListener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *URLRewrite) DeepCopyInto(out *URLRewrite) { + *out = *in + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(HTTPPathModifier) + (*in).DeepCopyInto(*out) + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new URLRewrite. +func (in *URLRewrite) DeepCopy() *URLRewrite { + if in == nil { + return nil + } + out := new(URLRewrite) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UnstructuredRef) DeepCopyInto(out *UnstructuredRef) { + *out = *in + if in.Object != nil { + in, out := &in.Object, &out.Object + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UnstructuredRef. +func (in *UnstructuredRef) DeepCopy() *UnstructuredRef { + if in == nil { + return nil + } + out := new(UnstructuredRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Xds) DeepCopyInto(out *Xds) { + *out = *in + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(AccessLog) + (*in).DeepCopyInto(*out) + } + if in.Tracing != nil { + in, out := &in.Tracing, &out.Tracing + *out = new(Tracing) + (*in).DeepCopyInto(*out) + } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(Metrics) + **out = **in + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = make([]*HTTPListener, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(HTTPListener) + (*in).DeepCopyInto(*out) + } + } + } + if in.TCP != nil { + in, out := &in.TCP, &out.TCP + *out = make([]*TCPListener, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TCPListener) + (*in).DeepCopyInto(*out) + } + } + } + if in.UDP != nil { + in, out := &in.UDP, &out.UDP + *out = make([]*UDPListener, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(UDPListener) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Xds. +func (in *Xds) DeepCopy() *Xds { + if in == nil { + return nil + } + out := new(Xds) + in.DeepCopyInto(out) + return out +} diff --git a/adapter/internal/operator/gateway-api/listener.go b/adapter/internal/operator/gateway-api/listener.go new file mode 100644 index 0000000000..81ef56d406 --- /dev/null +++ b/adapter/internal/operator/gateway-api/listener.go @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "fmt" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +var _ ListenersTranslator = (*Translator)(nil) + +type ListenersTranslator interface { + ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) +} + +func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) { + // Infra IR proxy ports must be unique. + foundPorts := make(map[string][]*protocolPort) + t.validateConflictedLayer7Listeners(gateways) + t.validateConflictedLayer4Listeners(gateways, gwapiv1.TCPProtocolType, gwapiv1.TLSProtocolType) + t.validateConflictedLayer4Listeners(gateways, gwapiv1.UDPProtocolType) + if t.MergeGateways { + t.validateConflictedMergedListeners(gateways) + } + + // Iterate through all listeners to validate spec + // and compute status for each, and add valid ones + // to the Xds IR. + for _, gateway := range gateways { + irKey := t.getIRKey(gateway.Gateway) + + xdsIR[irKey].AccessLog = processAccessLog() + xdsIR[irKey].Tracing = processTracing() + xdsIR[irKey].Metrics = processMetrics() + + for _, listener := range gateway.listeners { + // Process protocol & supported kinds + switch listener.Protocol { + case gwapiv1.TLSProtocolType: + if listener.TLS != nil { + switch *listener.TLS.Mode { + case gwapiv1.TLSModePassthrough: + t.validateAllowedRoutes(listener, KindTLSRoute) + case gwapiv1.TLSModeTerminate: + t.validateAllowedRoutes(listener, KindTCPRoute) + default: + t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) + } + } else { + t.validateAllowedRoutes(listener, KindTCPRoute, KindTLSRoute) + } + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType: + t.validateAllowedRoutes(listener, KindHTTPRoute, KindGRPCRoute) + case gwapiv1.TCPProtocolType: + t.validateAllowedRoutes(listener, KindTCPRoute) + case gwapiv1.UDPProtocolType: + t.validateAllowedRoutes(listener, KindUDPRoute) + default: + listener.SetCondition( + gwapiv1.ListenerConditionAccepted, + metav1.ConditionFalse, + gwapiv1.ListenerReasonUnsupportedProtocol, + fmt.Sprintf("Protocol %s is unsupported, must be %s, %s, %s or %s.", listener.Protocol, + gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType), + ) + } + + // Validate allowed namespaces + t.validateAllowedNamespaces(listener) + + // Process TLS configuration + t.validateTLSConfiguration(listener, resources) + + // Process Hostname configuration + t.validateHostName(listener) + + // Process conditions and check if the listener is ready + isReady := t.validateListenerConditions(listener) + if !isReady { + continue + } + // Add the listener to the Xds IR + servicePort := &protocolPort{protocol: listener.Protocol, port: int32(listener.Port)} + containerPort := servicePortToContainerPort(servicePort.port) + switch listener.Protocol { + case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType: + irListener := &ir.HTTPListener{ + Name: irHTTPListenerName(listener), + Address: "0.0.0.0", + Port: uint32(containerPort), + TLS: irTLSConfigs(listener.tlsSecrets), + Path: ir.PathSettings{ + MergeSlashes: true, + }, + } + if listener.Hostname != nil { + irListener.Hostnames = append(irListener.Hostnames, string(*listener.Hostname)) + } else { + // Hostname specifies the virtual hostname to match for protocol types that define this concept. + // When unspecified, all hostnames are matched. This field is ignored for protocols that don’t require hostname based matching. + // see more https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/gwapiv1.Listener. + irListener.Hostnames = append(irListener.Hostnames, "*") + } + xdsIR[irKey].HTTP = append(xdsIR[irKey].HTTP, irListener) + } + + // Add the listener to the Infra IR. Infra IR ports must have a unique port number per layer-4 protocol + // (TCP or UDP). + if !containsPort(foundPorts[irKey], servicePort) { + t.processInfraIRListener(listener, infraIR, irKey, servicePort) + foundPorts[irKey] = append(foundPorts[irKey], servicePort) + } + } + } +} + +func (t *Translator) processInfraIRListener(listener *ListenerContext, infraIR InfraIRMap, irKey string, servicePort *protocolPort) { + var proto ir.ProtocolType + switch listener.Protocol { + case gwapiv1.HTTPProtocolType: + proto = ir.HTTPProtocolType + case gwapiv1.HTTPSProtocolType: + proto = ir.HTTPSProtocolType + case gwapiv1.TLSProtocolType: + proto = ir.TLSProtocolType + case gwapiv1.TCPProtocolType: + proto = ir.TCPProtocolType + case gwapiv1.UDPProtocolType: + proto = ir.UDPProtocolType + } + + infraPortName := string(listener.Name) + if t.MergeGateways { + infraPortName = irHTTPListenerName(listener) + } + infraPort := ir.ListenerPort{ + Name: infraPortName, + Protocol: proto, + ServicePort: servicePort.port, + ContainerPort: servicePortToContainerPort(servicePort.port), + } + + proxyListener := &ir.ProxyListener{ + Name: irHTTPListenerName(listener), + Ports: []ir.ListenerPort{infraPort}, + } + + infraIR[irKey].Proxy.Listeners = append(infraIR[irKey].Proxy.Listeners, proxyListener) +} + +func processAccessLog() *ir.AccessLog { + // use the default access log + return &ir.AccessLog{ + Text: []*ir.TextAccessLog{ + { + Path: "/dev/stdout", + }, + }, + } +} + +func processTracing() *ir.Tracing { + return nil +} + +func processMetrics() *ir.Metrics { + return nil +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/controller.go b/adapter/internal/operator/gateway-api/provider/kubernetes/controller.go new file mode 100644 index 0000000000..db7e35f57d --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/controller.go @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +// todo(amali) verify gateway api latest imports +// todo(amali) fix formatting in logs +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/config" + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/pkg/logging" + "github.com/wso2/apk/adapter/pkg/utils/stringutils" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + + "github.com/wso2/apk/adapter/internal/operator/constants" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/operator/status" + "github.com/wso2/apk/adapter/internal/operator/utils" + dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" + discoveryv1 "k8s.io/api/discovery/v1" + k8errors "k8s.io/apimachinery/pkg/api/errors" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +type gatewayReconcilerNew struct { + client client.Client + statusUpdater *status.UpdateHandler + mgr manager.Manager + resources *message.ProviderResources + extGVKs []schema.GroupVersionKind + namespace string + store *kubernetesProviderStore +} + +type resourceMappings struct { + // Map for storing namespaces for Route, Service and Gateway objects. + allAssociatedNamespaces map[string]struct{} + // Map for storing backendRefs' NamespaceNames referred by various Route objects. + allAssociatedBackendRefs map[gwapiv1.BackendObjectReference]struct{} + // extensionRefFilters is a map of filters managed by an extension. + // The key is the namespaced name of the filter and the value is the + // unstructured form of the resource. + extensionRefFilters map[types.NamespacedName]unstructured.Unstructured +} + +const ( + gatewayClassControllerName = "wso2.com/apk-gateway-default" + gatewayClassFinalizer = gwapiv1.GatewayClassFinalizerGatewaysExist +) + +// InitGatewayController creates a new Gateway controller instance. Gateway Controllers watches for gwapiv1.Gateway. +func InitGatewayController(mgr manager.Manager, pResourses *message.ProviderResources, statusUpdater *status.UpdateHandler) error { + conf := config.ReadConfigs() + r := &gatewayReconcilerNew{ + client: mgr.GetClient(), + statusUpdater: statusUpdater, + mgr: mgr, + resources: pResourses, + namespace: conf.Deployment.Gateway.Namespace, + store: newProviderStore(), + } + c, err := controller.New(constants.GatewayControllerNew, mgr, controller.Options{Reconciler: r}) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error3119, logging.BLOCKER, "Error creating API controller, error: %v", err)) + return err + } + + ctx := context.Background() + // Watch resources + if err := r.watchResources(ctx, mgr, c); err != nil { + return err + } + // Subscribe to status updates + r.subscribeAndUpdateStatus(ctx) + loggers.LoggerAPKOperator.Info("New Controller successfully started. Watching Gateway Objects....") + return nil +} + +// Reconcile handles reconciling all resources in a single call. Any resource event should enqueue the +// same reconcile.Request containing the gateway controller name. This allows multiple resource updates to +// be handled by a single call to Reconcile. The reconcile.Request DOES NOT map to a specific resource. +func (r *gatewayReconcilerNew) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var ( + managedGCs []*gwapiv1.GatewayClass + err error + ) + + loggers.LoggerAPKOperator.Infof("Reconciling all gateways due to change in %s:%s", req.Namespace, req.Name) + + // Get the GatewayClasses managed by the Envoy Gateway Controller. + managedGCs, err = r.managedGatewayClasses(ctx) + if err != nil { + return reconcile.Result{}, err + } + + // The gatewayclass was already deleted/finalized and there are stale queue entries. + if managedGCs == nil { + r.resources.GatewayAPIResources.Delete(string(gatewayClassControllerName)) + loggers.LoggerAPKOperator.Info("no accepted gatewayclass") + return reconcile.Result{}, nil + } + + // we only manage one gatewayclass + managedGC := managedGCs[0] + + // Collect all the Gateway API resources, Envoy Gateway customized resources, + // and their referenced resources for the managed GatewayClasses, and store + // them per GatewayClass. + // For example: + // - Gateway API resources: Gateways, xRoutes ... + // - Referenced resources: Services, ServiceImports, EndpointSlices, Secrets, ConfigMaps ... + gwcResource := gatewayapi.NewResources() + gwcResource.GatewayClass = managedGC + gwcResources := gatewayapi.ControllerResources{gwcResource} + resourceMappings := newResourceMapping() + + // Add all Gateways, their associated Routes, and referenced resources to the resourceTree + if err = r.processGateways(ctx, managedGC, resourceMappings, gwcResource); err != nil { + return reconcile.Result{}, err + } + + // Add the referenced services, ServiceImports, and EndpointSlices in + // the collected BackendRefs to the resourceTree. + // BackendRefs are referred by various Route objects and the ExtAuth in SecurityPolicies. + r.processBackendRefs(ctx, gwcResource, resourceMappings) + + // For this particular Gateway, and all associated objects, check whether the + // namespace exists. Add to the resourceTree. + for ns := range resourceMappings.allAssociatedNamespaces { + namespace, err := r.getNamespace(ctx, ns) + if err != nil { + loggers.LoggerAPKOperator.Error("Unable to find the namespace ", err) + if k8errors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, err + } + + gwcResource.Namespaces = append(gwcResource.Namespaces, namespace) + } + + if err := r.updateStatusForGatewayClass(ctx, managedGC, true, string(gwapiv1.GatewayClassReasonAccepted), status.MsgValidGatewayClass); err != nil { + loggers.LoggerAPKOperator.Errorf("Unable to update GatewayClass %s status %v", managedGC.Name, err) + return reconcile.Result{}, err + } + if len(gwcResource.Gateways) == 0 { + loggers.LoggerAPKOperator.Info("No gateways found for accepted gatewayclass ", managedGC.Name) + + // If needed, remove the finalizer from the accepted GatewayClass. + if err := r.removeFinalizer(ctx, managedGC); err != nil { + loggers.LoggerAPKOperator.Errorf("Failed to remove finalizer from gatewayclass %s, %v", + managedGC.Name, err) + return reconcile.Result{}, err + } + } else { + // finalize the accepted GatewayClass. + if err := r.addFinalizer(ctx, managedGC); err != nil { + loggers.LoggerAPKOperator.Errorf("Failed adding finalizer to gatewayclass %s, %v", + managedGC.Name, err) + return reconcile.Result{}, err + } + } + + // Store the Gateway Resources for the GatewayClass. + // The Store is triggered even when there are no Gateways associated to the + // GatewayClass. This would happen in case the last Gateway is removed and the + // Store will be required to trigger a cleanup of envoy infra resources. + r.resources.GatewayAPIResources.Store(gatewayClassControllerName, &gwcResources) + + return ctrl.Result{}, nil +} + +// managedGatewayClasses returns a list of GatewayClass objects that are managed by the Envoy Gateway Controller. +func (r *gatewayReconcilerNew) managedGatewayClasses(ctx context.Context) ([]*gwapiv1.GatewayClass, error) { + var gatewayClasses gwapiv1.GatewayClassList + if err := r.client.List(ctx, &gatewayClasses); err != nil { + return nil, fmt.Errorf("error listing gatewayclasses: %w", err) + } + + var cc controlledClasses + + for _, gwClass := range gatewayClasses.Items { + gwClass := gwClass + if gwClass.Spec.ControllerName == gatewayClassControllerName { + // The gatewayclass was marked for deletion and the finalizer removed, + // so clean-up dependents. + if !gwClass.DeletionTimestamp.IsZero() && + !stringutils.StringInSlice(gatewayClassFinalizer, gwClass.Finalizers) { + loggers.LoggerAPKOperator.Info("Gatewayclass marked for deletion") + cc.removeMatch(&gwClass) + continue + } + + cc.addMatch(&gwClass) + } + } + + return cc.matchedClasses, nil +} + +func newResourceMapping() *resourceMappings { + return &resourceMappings{ + allAssociatedNamespaces: map[string]struct{}{}, + allAssociatedBackendRefs: map[gwapiv1.BackendObjectReference]struct{}{}, + extensionRefFilters: map[types.NamespacedName]unstructured.Unstructured{}, + } +} + +func (r *gatewayReconcilerNew) processGateways(ctx context.Context, managedGC *gwapiv1.GatewayClass, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { + // Find gateways for the managedGC + // Find the Gateways that reference this Class. + gatewayList := &gwapiv1.GatewayList{} + if err := r.client.List(ctx, gatewayList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(classGatewayIndex, managedGC.Name), + }); err != nil || len(gatewayList.Items) < 1 { + loggers.LoggerAPKOperator.Infof("No associated Gateways found for GatewayClass name %s, %v", managedGC.Name, err) + return err + } + + for _, gtw := range gatewayList.Items { + loggers.LoggerAPKOperator.Infof("Processing Gateway namespace %s: Name %s", gtw.Namespace, gtw.Name) + resourceMap.allAssociatedNamespaces[gtw.Namespace] = struct{}{} + + for _, listener := range gtw.Spec.Listeners { + // Get Secret for gateway if it exists. + if terminatesTLS(&listener) { + for _, certRef := range listener.TLS.CertificateRefs { + certRef := certRef + if refsSecret(&certRef) { + if err := r.processSecretRef(ctx, resourceMap, resourceTree, gatewayapi.KindGateway, gtw.Namespace, + gtw.Name, certRef); err != nil { + loggers.LoggerAPKOperator.Errorf("Failed to process TLS SecretRef for gateway %+v, secretRef %+v, Error: %v", gtw, certRef, err) + } + } + } + } + } + + // Get HTTPRoute objects and check if it exists. + if err := r.processHTTPRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + return err + } + + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + gtw.Status = gwapiv1.GatewayStatus{} + resourceTree.Gateways = append(resourceTree.Gateways, >w) + } + + return nil +} + +// processSecretRef adds the referenced Secret to the resourceTree if it's valid. +// - If it exists in the same namespace as the owner. +// - If it exists in a different namespace, and there is a ReferenceGrant. +func (r *gatewayReconcilerNew) processSecretRef( + ctx context.Context, + resourceMap *resourceMappings, + resourceTree *gatewayapi.Resources, + ownerKind string, + ownerNS string, + ownerName string, + secretRef gwapiv1.SecretObjectReference, +) error { + secret := new(corev1.Secret) + secretNS := gatewayapi.NamespaceDerefOr(secretRef.Namespace, ownerNS) + err := r.client.Get(ctx, + types.NamespacedName{Namespace: secretNS, Name: string(secretRef.Name)}, + secret, + ) + if err != nil { + return fmt.Errorf("unable to find the Secret: %s/%s, Error: %v", secretNS, string(secretRef.Name), err) + } + if secretNS != ownerNS { + from := ObjectKindNamespacedName{ + kind: ownerKind, + namespace: ownerNS, + name: ownerName, + } + to := ObjectKindNamespacedName{ + kind: gatewayapi.KindSecret, + namespace: secretNS, + name: secret.Name, + } + refGrant, err := r.findReferenceGrant(ctx, from, to) + switch { + case err != nil: + return fmt.Errorf("failed to find ReferenceGrant: %w", err) + case refGrant == nil: + return fmt.Errorf( + "no matching ReferenceGrants found: from %s/%s to %s/%s", + from.kind, from.namespace, to.kind, to.namespace) + default: + // RefGrant found + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + loggers.LoggerAPKOperator.Infof("Added ReferenceGrant to resource map namespace %s, name %s", + refGrant.Namespace, refGrant.Name) + } + } + resourceMap.allAssociatedNamespaces[secretNS] = struct{}{} // TODO Zhaohuabing do we need this line? + resourceTree.Secrets = append(resourceTree.Secrets, secret) + loggers.LoggerAPKOperator.Infof("Processing Secret namespace %s name %s", secretNS, string(secretRef.Name)) + return nil +} + +func (r *gatewayReconcilerNew) findReferenceGrant(ctx context.Context, from, to ObjectKindNamespacedName) (*gwapiv1b1.ReferenceGrant, error) { + refGrantList := new(gwapiv1b1.ReferenceGrantList) + opts := &client.ListOptions{FieldSelector: fields.OneTermEqualSelector(targetRefGrantRouteIndex, to.kind)} + if err := r.client.List(ctx, refGrantList, opts); err != nil { + return nil, fmt.Errorf("failed to list ReferenceGrants: %w", err) + } + + refGrants := refGrantList.Items + + for _, refGrant := range refGrants { + if refGrant.Namespace == to.namespace { + for _, src := range refGrant.Spec.From { + if src.Kind == gwapiv1.Kind(from.kind) && string(src.Namespace) == from.namespace { + return &refGrant, nil + } + } + } + } + + // No ReferenceGrant found. + return nil, nil +} + +func (r *gatewayReconcilerNew) enqueueClass(_ context.Context, _ client.Object) []reconcile.Request { + return []reconcile.Request{{NamespacedName: types.NamespacedName{ + Name: string(gatewayClassControllerName), + }}} +} + +// watchResources watches gateway api resources. +func (r *gatewayReconcilerNew) watchResources(ctx context.Context, mgr manager.Manager, c controller.Controller) error { + if err := c.Watch( + source.Kind(mgr.GetCache(), &gwapiv1.GatewayClass{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.hasMatchingController), + ); err != nil { + return err + } + + // Watch Gateway CRUDs and reconcile affected GatewayClass. + gPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.validateGatewayForReconcile), + } + + if err := c.Watch( + source.Kind(mgr.GetCache(), &gwapiv1.Gateway{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + gPredicates..., + ); err != nil { + return err + } + if err := addGatewayIndexers(ctx, mgr); err != nil { + return err + } + + // Watch HTTPRoute CRUDs and process affected Gateways. + httprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if err := c.Watch( + source.Kind(mgr.GetCache(), &gwapiv1.HTTPRoute{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + httprPredicates..., + ); err != nil { + return err + } + if err := addHTTPRouteIndexers(ctx, mgr); err != nil { + return err + } + + // // Watch GRPCRoute CRUDs and process affected Gateways. + // grpcrPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &gwapiv1a2.GRPCRoute{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // grpcrPredicates..., + // ); err != nil { + // return err + // } + // if err := addGRPCRouteIndexers(ctx, mgr); err != nil { + // return err + // } + + // // Watch TLSRoute CRUDs and process affected Gateways. + // tlsrPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if r.namespaceLabel != nil { + // tlsrPredicates = append(tlsrPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &gwapiv1a2.TLSRoute{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // tlsrPredicates..., + // ); err != nil { + // return err + // } + // if err := addTLSRouteIndexers(ctx, mgr); err != nil { + // return err + // } + + // // Watch UDPRoute CRUDs and process affected Gateways. + // udprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if r.namespaceLabel != nil { + // udprPredicates = append(udprPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &gwapiv1a2.UDPRoute{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // udprPredicates..., + // ); err != nil { + // return err + // } + // if err := addUDPRouteIndexers(ctx, mgr); err != nil { + // return err + // } + + // // Watch TCPRoute CRUDs and process affected Gateways. + // tcprPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if r.namespaceLabel != nil { + // tcprPredicates = append(tcprPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &gwapiv1a2.TCPRoute{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // tcprPredicates..., + // ); err != nil { + // return err + // } + // if err := addTCPRouteIndexers(ctx, mgr); err != nil { + // return err + // } + + // Watch Service CRUDs and process affected *Route objects and services belongs to gateways + servicePredicates := []predicate.Predicate{predicate.NewPredicateFuncs(r.validateServiceForReconcile)} + if err := c.Watch( + source.Kind(mgr.GetCache(), &corev1.Service{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + servicePredicates..., + ); err != nil { + return err + } + + // serviceImportCRDExists := r.serviceImportCRDExists(mgr) + // if !serviceImportCRDExists { + // loggers.LoggerAPKOperator.Info("ServiceImport CRD not found, skipping ServiceImport watch") + // } + + // // Watch ServiceImport CRUDs and process affected *Route objects. + // if serviceImportCRDExists { + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &mcsapi.ServiceImport{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // predicate.GenerationChangedPredicate{}, + // predicate.NewPredicateFuncs(r.validateServiceImportForReconcile)); err != nil { + // // ServiceImport is not available in the cluster, skip the watch and not throw error. + // loggers.LoggerAPKOperator.Info("unable to watch ServiceImport: %s", err.Error()) + // } + // } + + // // Watch EndpointSlice CRUDs and process affected *Route objects. + // esPredicates := []predicate.Predicate{ + // predicate.GenerationChangedPredicate{}, + // predicate.NewPredicateFuncs(r.validateEndpointSliceForReconcile), + // } + // if r.namespaceLabel != nil { + // esPredicates = append(esPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &discoveryv1.EndpointSlice{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // esPredicates..., + // ); err != nil { + // return err + // } + + // Watch Node CRUDs to update Gateway Address exposed by Service of type NodePort. + // Node creation/deletion and ExternalIP updates would require update in the Gateway + nPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.handleNode), + } + // resource address. + if err := c.Watch( + source.Kind(mgr.GetCache(), &corev1.Node{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + nPredicates..., + ); err != nil { + return err + } + + // Watch Secret CRUDs and process affected EG CRs (Gateway, SecurityPolicy, more in the future). + secretPredicates := []predicate.Predicate{ + predicate.GenerationChangedPredicate{}, + predicate.NewPredicateFuncs(r.validateSecretForReconcile), + } + if err := c.Watch( + source.Kind(mgr.GetCache(), &corev1.Secret{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + secretPredicates..., + ); err != nil { + return err + } + + // // Watch ConfigMap CRUDs and process affected ClienTraffiPolicies and BackendTLSPolicies. + // configMapPredicates := []predicate.Predicate{ + // predicate.GenerationChangedPredicate{}, + // predicate.NewPredicateFuncs(r.validateConfigMapForReconcile), + // } + // if r.namespaceLabel != nil { + // configMapPredicates = append(configMapPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &corev1.ConfigMap{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // configMapPredicates..., + // ); err != nil { + // return err + // } + + // Watch ReferenceGrant CRUDs and process affected Gateways. + rgPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + if err := c.Watch( + source.Kind(mgr.GetCache(), &gwapiv1b1.ReferenceGrant{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + rgPredicates..., + ); err != nil { + return err + } + if err := addReferenceGrantIndexers(ctx, mgr); err != nil { + return err + } + + // Watch Deployment CRUDs and process affected Gateways. + dPredicates := []predicate.Predicate{predicate.NewPredicateFuncs(r.validateDeploymentForReconcile)} + if err := c.Watch( + source.Kind(mgr.GetCache(), &appsv1.Deployment{}), + handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + dPredicates..., + ); err != nil { + return err + } + + // // Watch BackendTLSPolicy + // btlsPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if r.namespaceLabel != nil { + // btlsPredicates = append(btlsPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + + // if err := c.Watch( + // source.Kind(mgr.GetCache(), &gwapiv1a2.BackendTLSPolicy{}), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // btlsPredicates..., + // ); err != nil { + // return err + // } + + // if err := addBtlsIndexers(ctx, mgr); err != nil { + // return err + // } + + // loggers.LoggerAPKOperator.Info("Watching gatewayAPI related objects") + + // // Watch any additional GVKs from the registered extension. + // uPredicates := []predicate.Predicate{predicate.GenerationChangedPredicate{}} + // if r.namespaceLabel != nil { + // uPredicates = append(uPredicates, predicate.NewPredicateFuncs(r.hasMatchingNamespaceLabels)) + // } + // for _, gvk := range r.extGVKs { + // u := &unstructured.Unstructured{} + // u.SetGroupVersionKind(gvk) + // if err := c.Watch(source.Kind(mgr.GetCache(), u), + // handler.EnqueueRequestsFromMapFunc(r.enqueueClass), + // uPredicates..., + // ); err != nil { + // return err + // } + // loggers.LoggerAPKOperator.Info("Watching additional resource", "resource", gvk.String()) + // } + return nil +} + +// processBackendRefs adds the referenced resources in BackendRefs to the resourceTree, including: +// - Services +// - ServiceImports +// - EndpointSlices +func (r *gatewayReconcilerNew) processBackendRefs(ctx context.Context, gwcResource *gatewayapi.Resources, resourceMappings *resourceMappings) { + for backendRef := range resourceMappings.allAssociatedBackendRefs { + backendRefKind := gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService) + loggers.LoggerAPKOperator.Infof("Processing Backend kind %s, namespace %s, name %s", backendRefKind, + string(*backendRef.Namespace), string(backendRef.Name)) + + var endpointSliceLabelKey string + switch backendRefKind { + case gatewayapi.KindService: + service := new(corev1.Service) + err := r.client.Get(ctx, types.NamespacedName{Namespace: string(*backendRef.Namespace), Name: string(backendRef.Name)}, service) + if err != nil { + loggers.LoggerAPKOperator.Errorf("Failed to get Service namespace %s, name: %s, Error: %v", + string(*backendRef.Namespace), string(backendRef.Name), err) + } else { + resourceMappings.allAssociatedNamespaces[service.Namespace] = struct{}{} + gwcResource.Services = append(gwcResource.Services, service) + loggers.LoggerAPKOperator.Infof("Added Service to resource tree namespace %s, name: %s", + string(*backendRef.Namespace), string(backendRef.Name)) + } + endpointSliceLabelKey = discoveryv1.LabelServiceName + + case gatewayapi.KindBackend: + backend := new(dpv1alpha1.Backend) + err := r.client.Get(ctx, types.NamespacedName{Namespace: string(*backendRef.Namespace), Name: string(backendRef.Name)}, backend) + if err != nil { + loggers.LoggerAPKOperator.Errorf("Failed to get Backend namespace %s, name %s, Error: %v", string(*backendRef.Namespace), + string(backendRef.Name), err) + } else { + resourceMappings.allAssociatedNamespaces[backend.Namespace] = struct{}{} + gwcResource.Backends = append(gwcResource.Backends, backend) + loggers.LoggerAPKOperator.Infof("Added Backend to resource tree namespace %s, name %s", + string(*backendRef.Namespace), string(backendRef.Name)) + } + } + + if endpointSliceLabelKey != "" { + // Retrieve the EndpointSlices associated with the service + endpointSliceList := new(discoveryv1.EndpointSliceList) + opts := []client.ListOption{ + client.MatchingLabels(map[string]string{ + endpointSliceLabelKey: string(backendRef.Name), + }), + client.InNamespace(string(*backendRef.Namespace)), + } + if err := r.client.List(ctx, endpointSliceList, opts...); err != nil { + loggers.LoggerAPKOperator.Errorf("Failed to get EndpointSlices namespace %s, kind %s, name %s, Error %v", + string(*backendRef.Namespace), backendRefKind, string(backendRef.Name), err) + } else { + for _, endpointSlice := range endpointSliceList.Items { + endpointSlice := endpointSlice + loggers.LoggerAPKOperator.Infof("Added EndpointSlice to resource tree namespace %s, name %s", + endpointSlice.Namespace, endpointSlice.Name) + gwcResource.EndpointSlices = append(gwcResource.EndpointSlices, &endpointSlice) + } + } + } + } +} + +func (r *gatewayReconcilerNew) getNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + nsKey := types.NamespacedName{Name: name} + ns := new(corev1.Namespace) + if err := r.client.Get(ctx, nsKey, ns); err != nil { + loggers.LoggerAPKOperator.Error("Unable to get Namespace ", err) + return nil, err + } + return ns, nil +} + +func (r *gatewayReconcilerNew) updateStatusForGatewayClass( + ctx context.Context, + gc *gwapiv1.GatewayClass, + accepted bool, + reason, + msg string) error { + if r.statusUpdater != nil { + r.statusUpdater.Send(status.Update{ + NamespacedName: types.NamespacedName{Name: gc.Name}, + Resource: &gwapiv1.GatewayClass{}, + UpdateStatus: func(obj client.Object) client.Object { + gc, ok := obj.(*gwapiv1.GatewayClass) + if !ok { + panic(fmt.Sprintf("unsupported object type %T", obj)) + } + + return status.SetGatewayClassAccepted(gc.DeepCopy(), accepted, reason, msg) + }, + }) + } else { + // this branch makes testing easier by not going through the status.Updater. + duplicate := status.SetGatewayClassAccepted(gc.DeepCopy(), accepted, reason, msg) + + if err := r.client.Status().Update(ctx, duplicate); err != nil && !k8errors.IsNotFound(err) { + return fmt.Errorf("error updating status of gatewayclass %s: %w", duplicate.Name, err) + } + } + return nil +} + +// removeFinalizer removes the gatewayclass finalizer from the provided gc, if it exists. +func (r *gatewayReconcilerNew) removeFinalizer(ctx context.Context, gc *gwapiv1.GatewayClass) error { + if stringutils.StringInSlice(gatewayClassFinalizer, gc.Finalizers) { + base := client.MergeFrom(gc.DeepCopy()) + gc.Finalizers = stringutils.RemoveString(gc.Finalizers, gatewayClassFinalizer) + if err := r.client.Patch(ctx, gc, base); err != nil { + return fmt.Errorf("failed to remove finalizer from gatewayclass %s: %w", gc.Name, err) + } + } + return nil +} + +// addFinalizer adds the gatewayclass finalizer to the provided gc, if it doesn't exist. +func (r *gatewayReconcilerNew) addFinalizer(ctx context.Context, gc *gwapiv1.GatewayClass) error { + if !stringutils.StringInSlice(gatewayClassFinalizer, gc.Finalizers) { + base := client.MergeFrom(gc.DeepCopy()) + gc.Finalizers = append(gc.Finalizers, gatewayClassFinalizer) + if err := r.client.Patch(ctx, gc, base); err != nil { + return fmt.Errorf("failed to add finalizer to gatewayclass %s: %w", gc.Name, err) + } + } + return nil +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/helpers.go b/adapter/internal/operator/gateway-api/provider/kubernetes/helpers.go new file mode 100644 index 0000000000..c31d2dc4e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/helpers.go @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/internal/operator/constants" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure/kubernetes/proxy" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +type controlledClasses struct { + // matchedClasses holds all GatewayClass objects with matching controllerName. + matchedClasses []*gwapiv1.GatewayClass +} + +func (cc *controlledClasses) addMatch(gc *gwapiv1.GatewayClass) { + cc.matchedClasses = append(cc.matchedClasses, gc) +} + +func (cc *controlledClasses) removeMatch(gc *gwapiv1.GatewayClass) { + // First remove gc from matchedClasses. + for i, matchedGC := range cc.matchedClasses { + if matchedGC.Name == gc.Name { + cc.matchedClasses[i] = cc.matchedClasses[len(cc.matchedClasses)-1] + cc.matchedClasses = cc.matchedClasses[:len(cc.matchedClasses)-1] + break + } + } +} + +// terminatesTLS returns true if the provided gateway contains a listener configured +// for TLS termination. +func terminatesTLS(listener *gwapiv1.Listener) bool { + if listener.TLS != nil && + (listener.Protocol == gwapiv1.HTTPSProtocolType || + listener.Protocol == gwapiv1.TLSProtocolType) && + listener.TLS.Mode != nil && + *listener.TLS.Mode == gwapiv1.TLSModeTerminate { + return true + } + return false +} + +// refsSecret returns true if ref refers to a Secret. +func refsSecret(ref *gwapiv1.SecretObjectReference) bool { + return (ref.Group == nil || *ref.Group == corev1.GroupName) && + (ref.Kind == nil || *ref.Kind == gatewayapi.KindSecret) +} + +type ObjectKindNamespacedName struct { + kind string + namespace string + name string +} + +// validateParentRefs validates the provided routeParentReferences, returning the +// referenced Gateways managed by Envoy Gateway. The only supported parentRef +// is a Gateway. +func validateParentRefs(ctx context.Context, client client.Client, namespace string, + gatewayClassController gwapiv1.GatewayController, + routeParentReferences []gwapiv1.ParentReference) ([]gwapiv1.Gateway, error) { + + var gateways []gwapiv1.Gateway + for i := range routeParentReferences { + ref := routeParentReferences[i] + if ref.Kind != nil && *ref.Kind != "Gateway" { + return nil, fmt.Errorf("invalid Kind %q", *ref.Kind) + } + if ref.Group != nil && *ref.Group != gwapiv1.GroupName { + return nil, fmt.Errorf("invalid Group %q", *ref.Group) + } + + // Ensure the referenced Gateway exists, using the route's namespace unless + // specified by the parentRef. + ns := namespace + if ref.Namespace != nil { + ns = string(*ref.Namespace) + } + gwKey := types.NamespacedName{ + Namespace: ns, + Name: string(ref.Name), + } + + gw := new(gwapiv1.Gateway) + if err := client.Get(ctx, gwKey, gw); err != nil { + return nil, fmt.Errorf("failed to get gateway %s/%s: %w", gwKey.Namespace, gwKey.Name, err) + } + + gcKey := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)} + gc := new(gwapiv1.GatewayClass) + if err := client.Get(ctx, gcKey, gc); err != nil { + return nil, fmt.Errorf("failed to get gatewayclass %s: %w", gcKey.Name, err) + } + if gc.Spec.ControllerName == gatewayClassController { + gateways = append(gateways, *gw) + } + } + + return gateways, nil +} + +// validateBackendRef validates that ref is a reference to a local Service. +// TODO: Add support for: +// - Validating weights. +// - Validating ports. +// - Referencing HTTPRoutes. +func validateBackendRef(ref *gwapiv1.BackendRef) error { + switch { + case ref == nil: + return nil + case gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName) != corev1.GroupName && gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName) != constants.GroupName: + return fmt.Errorf("invalid group; must be nil, empty string or %q, given %q", constants.GroupName, gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName)) + case gatewayapi.KindDerefOr(ref.Kind, gatewayapi.KindService) != gatewayapi.KindService && gatewayapi.KindDerefOr(ref.Kind, gatewayapi.KindService) != constants.KindBackend: + return fmt.Errorf("invalid kind %q; must be %q or %q, given %q", + *ref.BackendObjectReference.Kind, gatewayapi.KindService, constants.KindBackend, gatewayapi.GroupDerefOr(ref.Group, corev1.GroupName)) + } + + return nil +} + +// infraName returns expected name for the EnvoyProxy infra resources. +// By default it returns hashed string from {GatewayNamespace}/{GatewayName}, +func infraName(gateway *gwapiv1.Gateway) string { + namespace := gateway.Namespace + if len(namespace) > 5 { + namespace = namespace[:5] + } + infraName := fmt.Sprintf("%s/%s", namespace, gateway.Name) + return proxy.ExpectedResourceHashedName(infraName) +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/indexers.go b/adapter/internal/operator/gateway-api/provider/kubernetes/indexers.go new file mode 100644 index 0000000000..620c750893 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/indexers.go @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + + "github.com/wso2/apk/adapter/internal/loggers" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + classGatewayIndex = "classGatewayIndex" + gatewayTLSRouteIndex = "gatewayTLSRouteIndex" + gatewayHTTPRouteIndex = "gatewayHTTPRouteIndex" + gatewayGRPCRouteIndex = "gatewayGRPCRouteIndex" + gatewayTCPRouteIndex = "gatewayTCPRouteIndex" + gatewayUDPRouteIndex = "gatewayUDPRouteIndex" + secretGatewayIndex = "secretGatewayIndex" + targetRefGrantRouteIndex = "targetRefGrantRouteIndex" + backendHTTPRouteIndex = "backendHTTPRouteIndex" + serviceHTTPRouteIndex = "serviceHTTPRouteIndex" + backendGRPCRouteIndex = "backendGRPCRouteIndex" + backendTLSRouteIndex = "backendTLSRouteIndex" + backendTCPRouteIndex = "backendTCPRouteIndex" + backendUDPRouteIndex = "backendUDPRouteIndex" + secretSecurityPolicyIndex = "secretSecurityPolicyIndex" + backendSecurityPolicyIndex = "backendSecurityPolicyIndex" + configMapCtpIndex = "configMapCtpIndex" + secretCtpIndex = "secretCtpIndex" + configMapBtlsIndex = "configMapBtlsIndex" +) + +func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, func(rawObj client.Object) []string { + refGrant := rawObj.(*gwapiv1b1.ReferenceGrant) + var referredServices []string + for _, target := range refGrant.Spec.To { + referredServices = append(referredServices, string(target.Kind)) + } + return referredServices + }) +} + +// addHTTPRouteIndexers adds indexing on HTTPRoute. +// - For Service, ServiceImports objects that are referenced in HTTPRoute objects via `.spec.rules.backendRefs`. +// This helps in querying for HTTPRoutes that are affected by a particular Service CRUD. +func addHTTPRouteIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, gatewayHTTPRouteIndex, gatewayHTTPRouteIndexFunc); err != nil { + return err + } + // Backend to HTTPRoute indexer + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, backendHTTPRouteIndex, backendHTTPRouteIndexFunc); err != nil { + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, serviceHTTPRouteIndex, serviceHTTPRouteIndexFunc) +} + +func gatewayHTTPRouteIndexFunc(rawObj client.Object) []string { + httproute := rawObj.(*gwapiv1.HTTPRoute) + var gateways []string + for _, parent := range httproute.Spec.ParentRefs { + if parent.Kind == nil || string(*parent.Kind) == gatewayapi.KindGateway { + // If an explicit Gateway namespace is not provided, use the HTTPRoute namespace to + // lookup the provided Gateway Name. + gateways = append(gateways, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(parent.Namespace, httproute.Namespace), + Name: string(parent.Name), + }.String(), + ) + } + } + return gateways +} + +func serviceHTTPRouteIndexFunc(rawObj client.Object) []string { + httproute := rawObj.(*gwapiv1.HTTPRoute) + var backendRefs []string + for _, rule := range httproute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { + // If an explicit Backend namespace is not provided, use the HTTPRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httproute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +func backendHTTPRouteIndexFunc(rawObj client.Object) []string { + httproute := rawObj.(*gwapiv1.HTTPRoute) + var backendRefs []string + for _, rule := range httproute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindBackend { + // If an explicit Backend namespace is not provided, use the HTTPRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httproute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +// addGRPCRouteIndexers adds indexing on GRPCRoute, for Service objects that are +// referenced in GRPCRoute objects via `.spec.rules.backendRefs`. This helps in +// querying for GRPCRoutes that are affected by a particular Service CRUD. +func addGRPCRouteIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, gatewayGRPCRouteIndex, gatewayGRPCRouteIndexFunc); err != nil { + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, backendGRPCRouteIndex, backendGRPCRouteIndexFunc) +} + +func gatewayGRPCRouteIndexFunc(rawObj client.Object) []string { + grpcroute := rawObj.(*gwapiv1a2.GRPCRoute) + var gateways []string + for _, parent := range grpcroute.Spec.ParentRefs { + if parent.Kind == nil || string(*parent.Kind) == gatewayapi.KindGateway { + // If an explicit Gateway namespace is not provided, use the GRPCRoute namespace to + // lookup the provided Gateway Name. + gateways = append(gateways, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(parent.Namespace, grpcroute.Namespace), + Name: string(parent.Name), + }.String(), + ) + } + } + return gateways +} + +func backendGRPCRouteIndexFunc(rawObj client.Object) []string { + grpcroute := rawObj.(*gwapiv1a2.GRPCRoute) + var backendRefs []string + for _, rule := range grpcroute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { + // If an explicit Backend namespace is not provided, use the GRPCRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, grpcroute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +// addTLSRouteIndexers adds indexing on TLSRoute, for Service objects that are +// referenced in TLSRoute objects via `.spec.rules.backendRefs`. This helps in +// querying for TLSRoutes that are affected by a particular Service CRUD. +func addTLSRouteIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, gatewayTLSRouteIndex, func(rawObj client.Object) []string { + tlsRoute := rawObj.(*gwapiv1a2.TLSRoute) + var gateways []string + for _, parent := range tlsRoute.Spec.ParentRefs { + if string(*parent.Kind) == gatewayapi.KindGateway { + // If an explicit Gateway namespace is not provided, use the TLSRoute namespace to + // lookup the provided Gateway Name. + gateways = append(gateways, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(parent.Namespace, tlsRoute.Namespace), + Name: string(parent.Name), + }.String(), + ) + } + } + return gateways + }); err != nil { + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TLSRoute{}, backendTLSRouteIndex, backendTLSRouteIndexFunc) +} + +func backendTLSRouteIndexFunc(rawObj client.Object) []string { + tlsroute := rawObj.(*gwapiv1a2.TLSRoute) + var backendRefs []string + for _, rule := range tlsroute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { + // If an explicit Backend namespace is not provided, use the TLSRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tlsroute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +// addTCPRouteIndexers adds indexing on TCPRoute, for Service objects that are +// referenced in TCPRoute objects via `.spec.rules.backendRefs`. This helps in +// querying for TCPRoutes that are affected by a particular Service CRUD. +func addTCPRouteIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TCPRoute{}, gatewayTCPRouteIndex, func(rawObj client.Object) []string { + tcpRoute := rawObj.(*gwapiv1a2.TCPRoute) + var gateways []string + for _, parent := range tcpRoute.Spec.ParentRefs { + if string(*parent.Kind) == gatewayapi.KindGateway { + // If an explicit Gateway namespace is not provided, use the TCPRoute namespace to + // lookup the provided Gateway Name. + gateways = append(gateways, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(parent.Namespace, tcpRoute.Namespace), + Name: string(parent.Name), + }.String(), + ) + } + } + return gateways + }); err != nil { + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.TCPRoute{}, backendTCPRouteIndex, backendTCPRouteIndexFunc) +} + +func backendTCPRouteIndexFunc(rawObj client.Object) []string { + tcpRoute := rawObj.(*gwapiv1a2.TCPRoute) + var backendRefs []string + for _, rule := range tcpRoute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { + // If an explicit Backend namespace is not provided, use the TCPRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, tcpRoute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +// addUDPRouteIndexers adds indexing on UDPRoute. +// - For Gateway objects that are referenced in UDPRoute objects via `.spec.parentRefs`. This helps in +// querying for UDPRoutes that are affected by a particular Gateway CRUD. +// - For Service objects that are referenced in UDPRoute objects via `.spec.rules.backendRefs`. This helps in +// querying for UDPRoutes that are affected by a particular Service CRUD. +func addUDPRouteIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.UDPRoute{}, gatewayUDPRouteIndex, func(rawObj client.Object) []string { + udpRoute := rawObj.(*gwapiv1a2.UDPRoute) + var gateways []string + for _, parent := range udpRoute.Spec.ParentRefs { + if string(*parent.Kind) == gatewayapi.KindGateway { + // If an explicit Gateway namespace is not provided, use the UDPRoute namespace to + // lookup the provided Gateway Name. + gateways = append(gateways, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(parent.Namespace, udpRoute.Namespace), + Name: string(parent.Name), + }.String(), + ) + } + } + return gateways + }); err != nil { + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.UDPRoute{}, backendUDPRouteIndex, backendUDPRouteIndexFunc) +} + +func backendUDPRouteIndexFunc(rawObj client.Object) []string { + udproute := rawObj.(*gwapiv1a2.UDPRoute) + var backendRefs []string + for _, rule := range udproute.Spec.Rules { + for _, backend := range rule.BackendRefs { + if backend.Kind == nil || string(*backend.Kind) == gatewayapi.KindService { + // If an explicit Backend namespace is not provided, use the UDPRoute namespace to + // lookup the provided Gateway Name. + backendRefs = append(backendRefs, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOrAlpha(backend.Namespace, udproute.Namespace), + Name: string(backend.Name), + }.String(), + ) + } + } + } + return backendRefs +} + +// addGatewayIndexers adds indexing on Gateway, for Secret objects and gatewayclass that are +// referenced in Gateway objects. This helps in querying for Gateways that are +// affected by a particular Secret CRUD. +func addGatewayIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.Gateway{}, secretGatewayIndex, secretGatewayIndexFunc); err != nil { + loggers.LoggerAPKOperator.Errorf("Error while adding index %s, %v ", secretGatewayIndex, err) + return err + } + + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.Gateway{}, classGatewayIndex, func(rawObj client.Object) []string { + gateway := rawObj.(*gwapiv1.Gateway) + loggers.LoggerAPKOperator.Infof("Index %s added for %s:%s to %s", classGatewayIndex, gateway.Namespace, gateway.Name, + gateway.Spec.GatewayClassName) + return []string{string(gateway.Spec.GatewayClassName)} + }) +} + +func secretGatewayIndexFunc(rawObj client.Object) []string { + gateway := rawObj.(*gwapiv1.Gateway) + var secretReferences []string + for _, listener := range gateway.Spec.Listeners { + if listener.TLS == nil || *listener.TLS.Mode != gwapiv1.TLSModeTerminate { + continue + } + for _, cert := range listener.TLS.CertificateRefs { + if *cert.Kind == gatewayapi.KindSecret { + // If an explicit Secret namespace is not provided, use the Gateway namespace to + // lookup the provided Secret Name. + secretReferences = append(secretReferences, + types.NamespacedName{ + Namespace: gatewayapi.NamespaceDerefOr(cert.Namespace, gateway.Namespace), + Name: string(cert.Name), + }.String(), + ) + } + } + } + return secretReferences +} + +// addBtlsIndexers adds indexing on BackendTLSPolicy, for ConfigMap objects that are +// referenced in BackendTLSPolicy objects. This helps in querying for BackendTLSPolicies that are +// affected by a particular ConfigMap CRUD. +func addBtlsIndexers(ctx context.Context, mgr manager.Manager) error { + return mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.BackendTLSPolicy{}, configMapBtlsIndex, configMapBtlsIndexFunc) +} + +func configMapBtlsIndexFunc(rawObj client.Object) []string { + btls := rawObj.(*gwapiv1a2.BackendTLSPolicy) + var configMapReferences []string + if btls.Spec.TLS.CACertRefs != nil { + for _, caCertRef := range btls.Spec.TLS.CACertRefs { + if string(caCertRef.Kind) == gatewayapi.KindConfigMap { + configMapReferences = append(configMapReferences, + types.NamespacedName{ + Namespace: btls.Namespace, + Name: string(caCertRef.Name), + }.String(), + ) + } + } + } + return configMapReferences +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/kubernetes.go b/adapter/internal/operator/gateway-api/provider/kubernetes/kubernetes.go new file mode 100644 index 0000000000..d72604230c --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/kubernetes.go @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "context" + "flag" + "fmt" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/provider" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/operator/status" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +// Provider is the scaffolding for the Kubernetes provider. It sets up dependencies +// and defines the topology of the provider and its managed components, wiring +// them together. +type Provider struct { + client client.Client + manager manager.Manager +} + +// New creates a new Provider from the provided EnvoyGateway. +func New(cfg *rest.Config, resources *message.ProviderResources) (*Provider, error) { + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + log.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // TODO: Decide which mgr opts should be exposed through envoygateway.provider.kubernetes API. + mgrOpts := manager.Options{ + LeaderElection: false, + Scheme: provider.GetScheme(), + HealthProbeBindAddress: ":8081", + LeaderElectionID: "operator-lease.apk.wso2.com", + } + + mgr, err := ctrl.NewManager(cfg, mgrOpts) + if err != nil { + return nil, fmt.Errorf("failed to create manager: %w", err) + } + + updateHandler := status.NewUpdateHandler(mgr.GetClient()) + if err := mgr.Add(updateHandler); err != nil { + return nil, fmt.Errorf("failed to add status update handler %w", err) + } + + // Create and register the controllers with the manager. + if err := InitGatewayController(mgr, resources, updateHandler); err != nil { + return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err) + } + + // Add health check health probes. + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + return nil, fmt.Errorf("unable to set up health check: %w", err) + } + + // Add ready check health probes. + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + return nil, fmt.Errorf("unable to set up ready check: %w", err) + } + + return &Provider{ + manager: mgr, + client: mgr.GetClient(), + }, nil +} + +// Start starts the Provider synchronously until a message is received from ctx. +func (p *Provider) Start(ctx context.Context) error { + errChan := make(chan error) + go func() { + errChan <- p.manager.Start(ctx) + }() + + // Wait for the manager to exit or an explicit stop. + select { + case <-ctx.Done(): + return nil + case err := <-errChan: + return err + } +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/predicates.go b/adapter/internal/operator/gateway-api/provider/kubernetes/predicates.go new file mode 100644 index 0000000000..91a5235640 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/predicates.go @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +import ( + "context" + + appsv1 "k8s.io/api/apps/v1" + + "github.com/wso2/apk/adapter/internal/loggers" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/utils" + corev1 "k8s.io/api/core/v1" + k8errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// hasMatchingController returns true if the provided object is a GatewayClass +// with a Spec.Controller string matching this Envoy Gateway's controller string, +// or false otherwise. +func (r *gatewayReconcilerNew) hasMatchingController(obj client.Object) bool { + gc, ok := obj.(*gwapiv1.GatewayClass) + if !ok { + loggers.LoggerAPKOperator.Info("bypassing reconciliation due to unexpected object type ", obj) + return false + } + + if gc.Spec.ControllerName == gatewayClassControllerName { + loggers.LoggerAPKOperator.Infof("gatewayclass has matching controller name %s, hence processing", + gc.Name) + return true + } + + loggers.LoggerAPKOperator.Info("bypassing reconciliation due to controller name", + gc.Spec.ControllerName) + return false +} + +// validateGatewayForReconcile returns true if the provided object is a Gateway +// using a GatewayClass matching the configured gatewayclass controller name. +func (r *gatewayReconcilerNew) validateGatewayForReconcile(obj client.Object) bool { + gw, ok := obj.(*gwapiv1.Gateway) + if !ok { + loggers.LoggerAPKOperator.Info("unexpected object type, bypassing reconciliation object", obj) + return false + } + + gatewayClass := &gwapiv1.GatewayClass{} + key := types.NamespacedName{Name: string(gw.Spec.GatewayClassName)} + if err := r.client.Get(context.Background(), key, gatewayClass); err != nil { + loggers.LoggerAPKOperator.Errorf("failed to get gatewayclass name %s, %+v", gw.Spec.GatewayClassName, err) + return false + } + + if gatewayClass.Spec.ControllerName != gatewayClassControllerName { + loggers.LoggerAPKOperator.Infof("gatewayclass name %s for gateway doesn't match configured name %s in %s/%s ", + string(gatewayClass.Spec.ControllerName), gatewayClassControllerName, gw.Namespace, gw.Name) + return false + } + loggers.LoggerAPKOperator.Info("Gateway CR change is detected for ", gw.Name) + return true +} + +// validateServiceForReconcile tries finding the owning Gateway of the Service +// if it exists, finds the Gateway's Deployment, and further updates the Gateway +// status Ready condition. All Services are pushed for reconciliation. +func (r *gatewayReconcilerNew) validateServiceForReconcile(obj client.Object) bool { + ctx := context.Background() + svc, ok := obj.(*corev1.Service) + if !ok { + loggers.LoggerAPKOperator.Info("unexpected object type, bypassing reconciliation for object", obj) + return false + } + labels := svc.GetLabels() + + // Check if the Service belongs to a Gateway, if so, update the Gateway status. + gtw := r.findOwningGateway(ctx, labels) + if gtw != nil { + r.updateStatusForGateway(ctx, gtw) + return false + } + + nsName := utils.NamespacedName(svc) + return r.isRouteReferencingBackend(&nsName) + +} + +// findOwningGateway attempts finds a Gateway using "labels". +func (r *gatewayReconcilerNew) findOwningGateway(ctx context.Context, labels map[string]string) *gwapiv1.Gateway { + gwName, ok := labels[gatewayapi.OwningGatewayNameLabel] + if !ok { + return nil + } + + gwNamespace, ok := labels[gatewayapi.OwningGatewayNamespaceLabel] + if !ok { + return nil + } + + gatewayKey := types.NamespacedName{Namespace: gwNamespace, Name: gwName} + gtw := new(gwapiv1.Gateway) + if err := r.client.Get(ctx, gatewayKey, gtw); err != nil { + loggers.LoggerAPKOperator.Infof("Gateway %+v not found : %v", gatewayKey, err) + return nil + } + return gtw +} + +// isRouteReferencingBackend returns true if the backend(service and serviceImport) is referenced by any of the xRoutes +// in the system, else returns false. +func (r *gatewayReconcilerNew) isRouteReferencingBackend(nsName *types.NamespacedName) bool { + ctx := context.Background() + httpRouteList := &gwapiv1.HTTPRouteList{} + if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(serviceHTTPRouteIndex, nsName.String()), + }); err != nil { + loggers.LoggerAPKOperator.Error("unable to find associated HTTPRoutes for the service ", err) + return false + } + + // Check how many Route objects refer this Backend + allAssociatedRoutes := len(httpRouteList.Items) + + return allAssociatedRoutes != 0 +} + +// envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist. +func (r *gatewayReconcilerNew) envoyServiceForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (*corev1.Service, error) { + key := types.NamespacedName{ + Namespace: r.namespace, + Name: infraName(gateway), + } + svc := new(corev1.Service) + if err := r.client.Get(ctx, key, svc); err != nil { + if k8errors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return svc, nil +} + +// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist. +func (r *gatewayReconcilerNew) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (*appsv1.Deployment, error) { + key := types.NamespacedName{ + Namespace: r.namespace, + Name: infraName(gateway), + } + deployment := new(appsv1.Deployment) + if err := r.client.Get(ctx, key, deployment); err != nil { + return nil, err + } + return deployment, nil +} + +func (r *gatewayReconcilerNew) handleNode(obj client.Object) bool { + ctx := context.Background() + node, ok := obj.(*corev1.Node) + if !ok { + loggers.LoggerAPKOperator.Info("unexpected object type, bypassing reconciliation", obj) + return false + } + + key := types.NamespacedName{Name: node.Name} + if err := r.client.Get(ctx, key, node); err != nil { + if k8errors.IsNotFound(err) { + r.store.removeNode(node) + return true + } + loggers.LoggerAPKOperator.Error(err, "unable to find node ", node.Name) + return false + } + + r.store.addNode(node) + return true +} + +// validateSecretForReconcile checks whether the Secret belongs to a valid Gateway. +func (r *gatewayReconcilerNew) validateSecretForReconcile(obj client.Object) bool { + secret, ok := obj.(*corev1.Secret) + if !ok { + loggers.LoggerAPKOperator.Info("unexpected object type, bypassing reconciliation", obj) + return false + } + + nsName := utils.NamespacedName(secret) + + if r.isGatewayReferencingSecret(&nsName) { + return true + } + + return false +} + +func (r *gatewayReconcilerNew) isGatewayReferencingSecret(nsName *types.NamespacedName) bool { + gwList := &gwapiv1.GatewayList{} + if err := r.client.List(context.Background(), gwList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(secretGatewayIndex, nsName.String()), + }); err != nil { + loggers.LoggerAPKOperator.Error(err, "unable to find associated Gateways") + return false + } + + if len(gwList.Items) == 0 { + return false + } + + for _, gw := range gwList.Items { + gw := gw + if !r.validateGatewayForReconcile(&gw) { + return false + } + } + return true +} + +// validateDeploymentForReconcile tries finding the owning Gateway of the Deployment +// if it exists, finds the Gateway's Service, and further updates the Gateway +// status Ready condition. No Deployments are pushed for reconciliation. +func (r *gatewayReconcilerNew) validateDeploymentForReconcile(obj client.Object) bool { + ctx := context.Background() + deployment, ok := obj.(*appsv1.Deployment) + if !ok { + loggers.LoggerAPKOperator.Info("unexpected object type, bypassing reconciliation", obj) + return false + } + labels := deployment.GetLabels() + + // Only deployments in the configured namespace should be reconciled. + if deployment.Namespace == r.namespace { + // Check if the deployment belongs to a Gateway, if so, update the Gateway status. + gtw := r.findOwningGateway(ctx, labels) + if gtw != nil { + r.updateStatusForGateway(ctx, gtw) + return false + } + } + + // There is no need to reconcile the Deployment any further. + return false +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/predicates_test.go b/adapter/internal/operator/gateway-api/provider/kubernetes/predicates_test.go new file mode 100644 index 0000000000..3b405680ec --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/predicates_test.go @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +type GroupKindNamespacedName struct { + Group gwapiv1.Group + Kind gwapiv1.Kind + Namespace gwapiv1.Namespace + Name gwapiv1.ObjectName +} + +// TestGatewayClassHasMatchingController tests the hasMatchingController +// predicate function. +func TestGatewayClassHasMatchingController(t *testing.T) { + testCases := []struct { + name string + obj client.Object + client client.Client + expect bool + }{ + { + name: "matching controller name", + obj: GetGatewayClass("test-gc", gatewayClassControllerName, nil), + expect: true, + }, + { + name: "non-matching controller name", + obj: GetGatewayClass("test-gc", "not.configured/controller", nil), + expect: false, + }, + } + + r := gatewayReconcilerNew{} + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + res := r.hasMatchingController(tc.obj) + require.Equal(t, tc.expect, res) + }) + } +} + +// GetGatewayClass returns a sample GatewayClass. +func GetGatewayClass(name string, controller gwapiv1.GatewayController, envoyProxy *GroupKindNamespacedName) *gwapiv1.GatewayClass { + gwc := &gwapiv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: gwapiv1.GatewayClassSpec{ + ControllerName: controller, + }, + } + + if envoyProxy != nil { + gwc.Spec.ParametersRef = &gwapiv1.ParametersReference{ + Group: envoyProxy.Group, + Kind: envoyProxy.Kind, + Name: string(envoyProxy.Name), + Namespace: &envoyProxy.Namespace, + } + } + + return gwc +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/routes.go b/adapter/internal/operator/gateway-api/provider/kubernetes/routes.go new file mode 100644 index 0000000000..c63353bf6f --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/routes.go @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +import ( + "context" + "errors" + + "github.com/wso2/apk/adapter/internal/loggers" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// processHTTPRoutes finds HTTPRoutes corresponding to a gatewayNamespaceName, further checks for +// the backend references and pushes the HTTPRoutes to the resourceTree. +func (r *gatewayReconcilerNew) processHTTPRoutes(ctx context.Context, gatewayNamespaceName string, + resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { + httpRouteList := &gwapiv1.HTTPRouteList{} + + // extensionRefFilters, err := r.getExtensionRefFilters(ctx) + // if err != nil { + // return err + // } + // for i := range extensionRefFilters { + // filter := extensionRefFilters[i] + // resourceMap.extensionRefFilters[utils.NamespacedName(&filter)] = filter + // } + + if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(gatewayHTTPRouteIndex, gatewayNamespaceName), + }); err != nil { + loggers.LoggerAPKOperator.Error("Failed to list HTTPRoutes", err) + return err + } + + for _, httpRoute := range httpRouteList.Items { + loggers.LoggerAPKOperator.Infof("Processing HTTPRoute %s:%s", httpRoute.Namespace, httpRoute.Name) + + for _, rule := range httpRoute.Spec.Rules { + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + if err := validateBackendRef(&backendRef.BackendRef); err != nil { + loggers.LoggerAPKOperator.Error("Invalid backendRef ", err) + continue + } + + backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace) + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, + }] = struct{}{} + + if backendNamespace != httpRoute.Namespace { + from := ObjectKindNamespacedName{ + kind: gatewayapi.KindHTTPRoute, + namespace: httpRoute.Namespace, + name: httpRoute.Name, + } + to := ObjectKindNamespacedName{ + kind: gatewayapi.KindDerefOr(backendRef.Kind, gatewayapi.KindService), + namespace: backendNamespace, + name: string(backendRef.Name), + } + refGrant, err := r.findReferenceGrant(ctx, from, to) + switch { + case err != nil: + loggers.LoggerAPKOperator.Error("Failed to find ReferenceGrant", err) + case refGrant == nil: + loggers.LoggerAPKOperator.Infof("No matching ReferenceGrants found for kind %s:%s to %s:%s", + from.kind, from.namespace, to.kind, to.namespace) + default: + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + loggers.LoggerAPKOperator.Infof("Added ReferenceGrant to resource map %s:%s", + refGrant.Namespace, refGrant.Name) + } + } + } + + for i := range rule.Filters { + filter := rule.Filters[i] + + if err := gatewayapi.ValidateHTTPRouteFilter(&filter); err != nil { + loggers.LoggerAPKOperator.Errorf("Bypassing filter rule for index %v, %v", i, err) + continue + } + + // Load in the backendRefs from any requestMirrorFilters on the HTTPRoute + if filter.Type == gwapiv1.HTTPRouteFilterRequestMirror { + // Make sure the config actually exists + mirrorFilter := filter.RequestMirror + if mirrorFilter == nil { + loggers.LoggerAPKOperator.Error(errors.New("invalid requestMirror filter"), "bypassing filter rule", "index", i) + continue + } + + mirrorBackendObj := mirrorFilter.BackendRef + // Wrap the filter's BackendObjectReference into a BackendRef so we can use existing tooling to check it + weight := int32(1) + mirrorBackendRef := gwapiv1.BackendRef{ + BackendObjectReference: mirrorBackendObj, + Weight: &weight, + } + + if err := validateBackendRef(&mirrorBackendRef); err != nil { + loggers.LoggerAPKOperator.Error("Invalid backendRef ", err) + continue + } + + backendNamespace := gatewayapi.NamespaceDerefOr(mirrorBackendRef.Namespace, + httpRoute.Namespace) + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: mirrorBackendRef.BackendObjectReference.Group, + Kind: mirrorBackendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: mirrorBackendRef.Name, + }] = struct{}{} + + if backendNamespace != httpRoute.Namespace { + from := ObjectKindNamespacedName{ + kind: gatewayapi.KindHTTPRoute, + namespace: httpRoute.Namespace, + name: httpRoute.Name, + } + to := ObjectKindNamespacedName{ + kind: gatewayapi.KindDerefOr(mirrorBackendRef.Kind, gatewayapi.KindService), + namespace: backendNamespace, + name: string(mirrorBackendRef.Name), + } + refGrant, err := r.findReferenceGrant(ctx, from, to) + switch { + case err != nil: + loggers.LoggerAPKOperator.Error("Failed to find ReferenceGrant", err) + case refGrant == nil: + loggers.LoggerAPKOperator.Infof("No matching ReferenceGrants found from %s:%s target %s:%s", + from.kind, from.namespace, to.kind, to.namespace) + default: + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + loggers.LoggerAPKOperator.Infof("Added ReferenceGrant to resource map %s:%s", + refGrant.Namespace, refGrant.Name) + } + } + } else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef { + // NOTE: filters must be in the same namespace as the HTTPRoute + // Check if it's a Kind managed by an extension and add to resourceTree + key := types.NamespacedName{ + Namespace: httpRoute.Namespace, + Name: string(filter.ExtensionRef.Name), + } + extRefFilter, ok := resourceMap.extensionRefFilters[key] + if !ok { + loggers.LoggerAPKOperator.Errorf("Filter not found; bypassing rule name %v for index %v", + filter.ExtensionRef.Name, i) + continue + } + + resourceTree.ExtensionRefFilters = append(resourceTree.ExtensionRefFilters, extRefFilter) + } + } + } + + resourceMap.allAssociatedNamespaces[httpRoute.Namespace] = struct{}{} + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + httpRoute.Status = gwapiv1.HTTPRouteStatus{} + resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, &httpRoute) + } + + return nil +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/status.go b/adapter/internal/operator/gateway-api/provider/kubernetes/status.go new file mode 100644 index 0000000000..f20ceb9b5f --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/status.go @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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 kubernetes + +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/operator/status" + "github.com/wso2/apk/adapter/internal/operator/utils" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// subscribeAndUpdateStatus subscribes to gateway API object status updates and +// writes it into the Kubernetes API Server. +func (r *gatewayReconcilerNew) subscribeAndUpdateStatus(ctx context.Context) { + // Gateway object status updater + go func() { + message.HandleSubscription( + message.Metadata{Runner: "provider", Message: "gateway-status"}, + r.resources.GatewayStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1.GatewayStatus], errChan chan error) { + // skip delete updates. + if update.Delete { + return + } + // Get gateway object + gtw := new(gwapiv1.Gateway) + if err := r.client.Get(ctx, update.Key, gtw); err != nil { + loggers.LoggerAPKOperator.Error(err, "gateway not found", "namespace", gtw.Namespace, "name", gtw.Name) + errChan <- err + return + } + // Set the updated Status and call the status update + gtw.Status = *update.Value + r.updateStatusForGateway(ctx, gtw) + }, + ) + loggers.LoggerAPKOperator.Info("gateway status subscriber shutting down") + }() + + // HTTPRoute object status updater + go func() { + message.HandleSubscription( + message.Metadata{Runner: "provider", Message: "httproute-status"}, + r.resources.HTTPRouteStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *gwapiv1.HTTPRouteStatus], errChan chan error) { + // skip delete updates. + if update.Delete { + return + } + key := update.Key + val := update.Value + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(gwapiv1.HTTPRoute), + UpdateStatus: func(obj client.Object) client.Object { + h, ok := obj.(*gwapiv1.HTTPRoute) + if !ok { + err := fmt.Errorf("unsupported object type %T", obj) + errChan <- err + panic(err) + } + hCopy := h.DeepCopy() + hCopy.Status.Parents = val.Parents + return hCopy + }, + }) + }, + ) + loggers.LoggerAPKOperator.Info("HttpRoute status subscriber shutting down") + }() + +} + +func (r *gatewayReconcilerNew) updateStatusForGateway(ctx context.Context, gtw *gwapiv1.Gateway) { + // nil check for unit tests. + if r.statusUpdater == nil { + return + } + + // Get deployment + deploy, err := r.envoyDeploymentForGateway(ctx, gtw) + if err != nil || deploy == nil { + loggers.LoggerAPKOperator.Infof("Failed to get Deployment for gateway %s/%s, %+v", + gtw.Namespace, gtw.Name, err) + } + + // Get service + svc, err := r.envoyServiceForGateway(ctx, gtw) + if err != nil || svc == nil { + loggers.LoggerAPKOperator.Infof("Failed to get Service for gateway %s:%s, %+v", + gtw.Namespace, gtw.Name, err) + } + // update accepted condition + status.UpdateGatewayStatusAcceptedCondition(gtw, true) + // update address field and programmed condition + status.UpdateGatewayStatusProgrammedCondition(gtw, svc, deploy, r.store.listNodeAddresses()...) + + key := utils.NamespacedName(gtw) + + // publish status + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(gwapiv1.Gateway), + UpdateStatus: func(obj client.Object) client.Object { + g, ok := obj.(*gwapiv1.Gateway) + if !ok { + panic(fmt.Sprintf("unsupported object type %T", obj)) + } + gCopy := g.DeepCopy() + gCopy.Status.Conditions = gtw.Status.Conditions + gCopy.Status.Addresses = gtw.Status.Addresses + gCopy.Status.Listeners = gtw.Status.Listeners + return gCopy + }, + }) +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/store.go b/adapter/internal/operator/gateway-api/provider/kubernetes/store.go new file mode 100644 index 0000000000..ffc5718526 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/store.go @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "sync" + + corev1 "k8s.io/api/core/v1" +) + +type nodeDetails struct { + name string + address string +} + +// kubernetesProviderStore holds cached information for the kubernetes provider. +type kubernetesProviderStore struct { + // nodes holds information required for updating Gateway status with the Node + // addresses, in case the Gateway is exposed on every Node of the cluster, using + // Service of type NodePort. + nodes map[string]nodeDetails + mu sync.Mutex +} + +func newProviderStore() *kubernetesProviderStore { + return &kubernetesProviderStore{ + nodes: make(map[string]nodeDetails), + } +} + +func (p *kubernetesProviderStore) addNode(n *corev1.Node) { + details := nodeDetails{name: n.Name} + + var internalIP, externalIP string + for _, addr := range n.Status.Addresses { + if addr.Type == corev1.NodeExternalIP { + externalIP = addr.Address + } + if addr.Type == corev1.NodeInternalIP { + internalIP = addr.Address + } + } + + // In certain scenarios (like in local KinD clusters), the Node + // externalIP is not provided, in that case we default back + // to the internalIP of the Node. + if externalIP != "" { + details.address = externalIP + } else if internalIP != "" { + details.address = internalIP + } + p.mu.Lock() + defer p.mu.Unlock() + p.nodes[n.Name] = details +} + +func (p *kubernetesProviderStore) removeNode(n *corev1.Node) { + p.mu.Lock() + defer p.mu.Unlock() + delete(p.nodes, n.Name) +} + +func (p *kubernetesProviderStore) listNodeAddresses() []string { + addrs := []string{} + p.mu.Lock() + defer p.mu.Unlock() + for _, n := range p.nodes { + if n.address != "" { + addrs = append(addrs, n.address) + } + } + return addrs +} diff --git a/adapter/internal/operator/gateway-api/provider/kubernetes/store_test.go b/adapter/internal/operator/gateway-api/provider/kubernetes/store_test.go new file mode 100644 index 0000000000..3dea80e267 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/kubernetes/store_test.go @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package kubernetes + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestNodeDetailsAddressStore(t *testing.T) { + store := newProviderStore() + testCases := []struct { + name string + nodeObject *corev1.Node + expectedAddresses []string + }{ + { + name: "No node addresses", + nodeObject: &corev1.Node{ + ObjectMeta: v1.ObjectMeta{Name: "node1"}, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{}}}, + }, + expectedAddresses: []string{}, + }, + { + name: "only external address", + nodeObject: &corev1.Node{ + ObjectMeta: v1.ObjectMeta{Name: "node1"}, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{ + Address: "1.1.1.1", + Type: corev1.NodeExternalIP, + }}}, + }, + expectedAddresses: []string{"1.1.1.1"}, + }, + { + name: "only internal address", + nodeObject: &corev1.Node{ + ObjectMeta: v1.ObjectMeta{Name: "node1"}, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{ + Address: "1.1.1.1", + Type: corev1.NodeInternalIP, + }}}, + }, + expectedAddresses: []string{"1.1.1.1"}, + }, + { + name: "prefer external address", + nodeObject: &corev1.Node{ + ObjectMeta: v1.ObjectMeta{Name: "node1"}, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{ + { + Address: "1.1.1.1", + Type: corev1.NodeExternalIP, + }, + { + Address: "2.2.2.2", + Type: corev1.NodeInternalIP, + }, + }}, + }, + expectedAddresses: []string{"1.1.1.1"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + store.addNode(tc.nodeObject) + assert.Equal(t, tc.expectedAddresses, store.listNodeAddresses()) + store.removeNode(tc.nodeObject) + }) + } +} + +func TestRace(t *testing.T) { + s := newProviderStore() + + go func() { + for { + s.addNode(&corev1.Node{ + ObjectMeta: v1.ObjectMeta{Name: "node1"}, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{}}}, + }) + } + }() + + _ = s.listNodeAddresses() +} diff --git a/adapter/internal/operator/gateway-api/provider/scheme.go b/adapter/internal/operator/gateway-api/provider/scheme.go new file mode 100644 index 0000000000..ebbeb26314 --- /dev/null +++ b/adapter/internal/operator/gateway-api/provider/scheme.go @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package provider + +import ( + dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" + dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +var ( + // scheme contains all the API types necessary for the provider's dynamic + // clients to work. Any new non-core types must be added here. + // + // NOTE: The discovery mechanism used by the client doesn't automatically + // refresh, so only add types here that are guaranteed to exist before the + // provider starts. + scheme = runtime.NewScheme() +) + +func init() { + if err := clientgoscheme.AddToScheme(scheme); err != nil { + panic(err) + } + // Add Gateway API types. + if err := gwapiv1.AddToScheme(scheme); err != nil { + panic(err) + } + if err := gwapiv1b1.AddToScheme(scheme); err != nil { + panic(err) + } + if err := gwapiv1a2.AddToScheme(scheme); err != nil { + panic(err) + } + if err := dpv1alpha1.AddToScheme(scheme); err != nil { + panic(err) + } + if err := dpv1alpha2.AddToScheme(scheme); err != nil { + panic(err) + } +} + +// GetScheme returns a scheme with types supported by the Kubernetes provider. +func GetScheme() *runtime.Scheme { + return scheme +} diff --git a/adapter/internal/operator/gateway-api/resource.go b/adapter/internal/operator/gateway-api/resource.go new file mode 100644 index 0000000000..7e407b0d35 --- /dev/null +++ b/adapter/internal/operator/gateway-api/resource.go @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "cmp" + "reflect" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" + "golang.org/x/exp/slices" + v1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +type XdsIRMap map[string]*ir.Xds +type InfraIRMap map[string]*ir.Infra + +// Resources holds the Gateway API and related +// resources that the translators needs as inputs. +// +k8s:deepcopy-gen=true +type Resources struct { + // This field is only used for marshalling/unmarshalling purposes and is not used by + // the translator + GatewayClass *gwapiv1.GatewayClass `json:"gatewayClass,omitempty" yaml:"gatewayClass,omitempty"` + Gateways []*gwapiv1.Gateway `json:"gateways,omitempty" yaml:"gateways,omitempty"` + HTTPRoutes []*gwapiv1.HTTPRoute `json:"httpRoutes,omitempty" yaml:"httpRoutes,omitempty"` + GRPCRoutes []*gwapiv1a2.GRPCRoute `json:"grpcRoutes,omitempty" yaml:"grpcRoutes,omitempty"` + TLSRoutes []*gwapiv1a2.TLSRoute `json:"tlsRoutes,omitempty" yaml:"tlsRoutes,omitempty"` + TCPRoutes []*gwapiv1a2.TCPRoute `json:"tcpRoutes,omitempty" yaml:"tcpRoutes,omitempty"` + UDPRoutes []*gwapiv1a2.UDPRoute `json:"udpRoutes,omitempty" yaml:"udpRoutes,omitempty"` + ReferenceGrants []*gwapiv1b1.ReferenceGrant `json:"referenceGrants,omitempty" yaml:"referenceGrants,omitempty"` + Namespaces []*v1.Namespace `json:"namespaces,omitempty" yaml:"namespaces,omitempty"` + Services []*v1.Service `json:"services,omitempty" yaml:"services,omitempty"` + Backends []*dpv1alpha1.Backend `json:"backends,omitempty" yaml:"backends,omitempty"` + EndpointSlices []*discoveryv1.EndpointSlice `json:"endpointSlices,omitempty" yaml:"endpointSlices,omitempty"` + Secrets []*v1.Secret `json:"secrets,omitempty" yaml:"secrets,omitempty"` + ConfigMaps []*v1.ConfigMap `json:"configMaps,omitempty" yaml:"configMaps,omitempty"` + ExtensionRefFilters []unstructured.Unstructured `json:"extensionRefFilters,omitempty" yaml:"extensionRefFilters,omitempty"` + BackendTLSPolicies []*gwapiv1a2.BackendTLSPolicy `json:"backendTLSPolicies,omitempty" yaml:"backendTLSPolicies,omitempty"` +} + +func NewResources() *Resources { + return &Resources{ + Gateways: []*gwapiv1.Gateway{}, + HTTPRoutes: []*gwapiv1.HTTPRoute{}, + GRPCRoutes: []*gwapiv1a2.GRPCRoute{}, + TLSRoutes: []*gwapiv1a2.TLSRoute{}, + Services: []*v1.Service{}, + EndpointSlices: []*discoveryv1.EndpointSlice{}, + Secrets: []*v1.Secret{}, + ConfigMaps: []*v1.ConfigMap{}, + ReferenceGrants: []*gwapiv1b1.ReferenceGrant{}, + Namespaces: []*v1.Namespace{}, + ExtensionRefFilters: []unstructured.Unstructured{}, + BackendTLSPolicies: []*gwapiv1a2.BackendTLSPolicy{}, + } +} + +func (r *Resources) GetNamespace(name string) *v1.Namespace { + for _, ns := range r.Namespaces { + if ns.Name == name { + return ns + } + } + + return nil +} + +func (r *Resources) GetService(namespace, name string) *v1.Service { + for _, svc := range r.Services { + if svc.Namespace == namespace && svc.Name == name { + return svc + } + } + + return nil +} + +func (r *Resources) GetSecret(namespace, name string) *v1.Secret { + for _, secret := range r.Secrets { + if secret.Namespace == namespace && secret.Name == name { + return secret + } + } + + return nil +} + +func (r *Resources) GetConfigMap(namespace, name string) *v1.ConfigMap { + for _, configMap := range r.ConfigMaps { + if configMap.Namespace == namespace && configMap.Name == name { + return configMap + } + } + + return nil +} + +// ControllerResources holds all the GatewayAPI resources per GatewayClass +type ControllerResources []*Resources + +// DeepCopy creates a new ControllerResources. +// It is handwritten since the tooling was unable to copy into a new slice +func (c *ControllerResources) DeepCopy() *ControllerResources { + if c == nil { + return nil + } + out := make(ControllerResources, len(*c)) + copy(out, *c) + return &out +} + +// Equal implements the Comparable interface used by watchable.DeepEqual to skip unnecessary updates. +func (c *ControllerResources) Equal(y *ControllerResources) bool { + // Deep copy to avoid modifying the original ordering. + c = c.DeepCopy() + c.sort() + y = y.DeepCopy() + y.sort() + return reflect.DeepEqual(c, y) +} + +func (c *ControllerResources) sort() { + slices.SortFunc(*c, func(c1, c2 *Resources) int { + return cmp.Compare(c1.GatewayClass.Name, c2.GatewayClass.Name) + }) +} + +func (r *Resources) GetEndpointSlicesForBackend(svcNamespace, svcName string, backendKind string) []*discoveryv1.EndpointSlice { + var endpointSlices []*discoveryv1.EndpointSlice + for _, endpointSlice := range r.EndpointSlices { + var backendSelectorLabel string + switch backendKind { + case KindService: + backendSelectorLabel = discoveryv1.LabelServiceName + } + if svcNamespace == endpointSlice.Namespace && + endpointSlice.GetLabels()[backendSelectorLabel] == svcName { + endpointSlices = append(endpointSlices, endpointSlice) + } + } + return endpointSlices +} diff --git a/adapter/internal/operator/gateway-api/route.go b/adapter/internal/operator/gateway-api/route.go new file mode 100644 index 0000000000..2a12e36158 --- /dev/null +++ b/adapter/internal/operator/gateway-api/route.go @@ -0,0 +1,1340 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/status" + "github.com/wso2/apk/adapter/pkg/utils/regex" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + // Following the description in `timeout` section of https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto + // Request timeout, which is defined as Duration, specifies the upstream timeout for the route + // If not specified, the default is 15s + HTTPRequestTimeout = "15s" +) + +var ( + _ RoutesTranslator = (*Translator)(nil) + validServiceName = `(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*` + validMethodName = `[A-Za-z_][A-Za-z_0-9]*` +) + +type RoutesTranslator interface { + ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext + ProcessGRPCRoutes(grpcRoutes []*gwapiv1a2.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext + ProcessTLSRoutes(tlsRoutes []*gwapiv1a2.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext + ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TCPRouteContext + ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*UDPRouteContext +} + +func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*HTTPRouteContext { + var relevantHTTPRoutes []*HTTPRouteContext + + for _, h := range httpRoutes { + if h == nil { + panic("received nil httproute") + } + httpRoute := &HTTPRouteContext{ + GatewayControllerName: t.GatewayControllerName, + HTTPRoute: h.DeepCopy(), + } + + // Find out if this route attaches to one of our Gateway's listeners, + // and if so, get the list of listeners that allow it to attach for each + // parentRef. + relevantRoute := t.processAllowedListenersForParentRefs(httpRoute, gateways, resources) + if !relevantRoute { + continue + } + + relevantHTTPRoutes = append(relevantHTTPRoutes, httpRoute) + + t.processHTTPRouteParentRefs(httpRoute, resources, xdsIR) + } + + return relevantHTTPRoutes +} + +func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1a2.GRPCRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*GRPCRouteContext { + var relevantGRPCRoutes []*GRPCRouteContext + + for _, g := range grpcRoutes { + if g == nil { + panic("received nil grpcroute") + } + grpcRoute := &GRPCRouteContext{ + GatewayControllerName: t.GatewayControllerName, + GRPCRoute: g.DeepCopy(), + } + + // Find out if this route attaches to one of our Gateway's listeners, + // and if so, get the list of listeners that allow it to attach for each + // parentRef. + relevantRoute := t.processAllowedListenersForParentRefs(grpcRoute, gateways, resources) + if !relevantRoute { + continue + } + + relevantGRPCRoutes = append(relevantGRPCRoutes, grpcRoute) + + t.processGRPCRouteParentRefs(grpcRoute, resources, xdsIR) + } + + return relevantGRPCRoutes +} + +func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, resources *Resources, xdsIR XdsIRMap) { + for _, parentRef := range httpRoute.ParentRefs { + // Need to compute Route rules within the parentRef loop because + // any conditions that come out of it have to go on each RouteParentStatus, + // not on the Route as a whole. + routeRoutes, err := t.processHTTPRouteRules(httpRoute, parentRef, resources) + if err != nil { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, // TODO: better reason + status.Error2ConditionMsg(err), + ) + continue + } + + // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" + if !parentRef.HasCondition(httpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.RouteReasonResolvedRefs, + "Resolved all the Object references for the Route", + ) + } + + // Skip parent refs that did not accept the route + if parentRef.HasCondition(httpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { + continue + } + var hasHostnameIntersection = t.processHTTPRouteParentRefListener(httpRoute, routeRoutes, parentRef, xdsIR) + if !hasHostnameIntersection { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonNoMatchingListenerHostname, + "There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).", + ) + } + + // If no negative conditions have been set, the route is considered "Accepted=True". + if parentRef.HTTPRoute != nil && + len(parentRef.HTTPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + + } +} + +func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *Resources) ([]*ir.HTTPRoute, error) { + var routeRoutes []*ir.HTTPRoute + + // compute matches, filters, backends + for ruleIdx, rule := range httpRoute.Spec.Rules { + httpFiltersContext := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, ruleIdx, resources) + + // A rule is matched if any one of its matches + // is satisfied (i.e. a logical "OR"), so generate + // a unique Xds IR HTTPRoute per match. + ruleRoutes, err := t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule) + if err != nil { + return nil, err + } + + dstAddrTypeMap := make(map[ir.DestinationAddressType]int) + + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + ds, backendWeight := t.processDestination(backendRef, parentRef, httpRoute, resources) + if !t.EndpointRoutingDisabled && ds != nil && len(ds.Endpoints) > 0 && ds.AddressType != nil { + dstAddrTypeMap[*ds.AddressType]++ + } + + for _, route := range ruleRoutes { + // If the route already has a direct response or redirect configured, then it was from a filter so skip + // processing any destinations for this route. + if route.DirectResponse == nil && route.Redirect == nil { + if ds != nil && len(ds.Endpoints) > 0 { + if route.Destination == nil { + route.Destination = &ir.RouteDestination{ + Name: irRouteDestinationName(httpRoute, ruleIdx), + } + } + route.Destination.Settings = append(route.Destination.Settings, ds) + route.BackendWeights.Valid += backendWeight + } else { + route.BackendWeights.Invalid += backendWeight + } + } + } + } + + // TODO: support mixed endpointslice address type between backendRefs + if !t.EndpointRoutingDisabled && len(dstAddrTypeMap) > 1 { + parentRef.SetCondition(httpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1a2.RouteReasonResolvedRefs, + "Mixed endpointslice address type between backendRefs is not supported") + } + + // If the route has no valid backends then just use a direct response and don't fuss with weighted responses + for _, ruleRoute := range ruleRoutes { + if ruleRoute.Destination == nil && ruleRoute.Redirect == nil { + ruleRoute.DirectResponse = &ir.DirectResponse{ + StatusCode: 500, + } + } + ruleRoute.IsHTTP2 = false + } + + // TODO handle: + // - sum of weights for valid backend refs is 0 + // - etc. + + routeRoutes = append(routeRoutes, ruleRoutes...) + } + + return routeRoutes, nil +} + +func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { + if rule.Timeouts != nil { + var rto *ir.Timeout + + // Timeout is translated from multiple resources and may already be partially set + if irRoute.Timeout != nil { + rto = irRoute.Timeout.DeepCopy() + } else { + rto = &ir.Timeout{} + } + + if rule.Timeouts.Request != nil { + d, err := time.ParseDuration(string(*rule.Timeouts.Request)) + if err != nil { + d, _ = time.ParseDuration(HTTPRequestTimeout) + } + setRequestTimeout(rto, metav1.Duration{Duration: d}) + } + + // Also set the IR Route Timeout to the backend request timeout + // until we introduce retries, then set it to per try timeout + if rule.Timeouts.BackendRequest != nil { + d, err := time.ParseDuration(string(*rule.Timeouts.BackendRequest)) + if err != nil { + d, _ = time.ParseDuration(HTTPRequestTimeout) + } + setRequestTimeout(rto, metav1.Duration{Duration: d}) + } + + irRoute.Timeout = rto + } +} + +func setRequestTimeout(irTimeout *ir.Timeout, d metav1.Duration) { + switch { + case irTimeout == nil: + irTimeout = &ir.Timeout{ + HTTP: &ir.HTTPTimeout{ + RequestTimeout: ptr.To(d), + }, + } + case irTimeout.HTTP == nil: + irTimeout.HTTP = &ir.HTTPTimeout{ + RequestTimeout: ptr.To(d), + } + default: + irTimeout.HTTP.RequestTimeout = ptr.To(d) + } +} + +func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, error) { + var ruleRoutes []*ir.HTTPRoute + + // If no matches are specified, the implementation MUST match every HTTP request. + if len(rule.Matches) == 0 { + irRoute := &ir.HTTPRoute{ + Name: irRouteName(httpRoute, ruleIdx, -1), + } + processTimeout(irRoute, rule) + applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) + ruleRoutes = append(ruleRoutes, irRoute) + } + + // A rule is matched if any one of its matches + // is satisfied (i.e. a logical "OR"), so generate + // a unique Xds IR HTTPRoute per match. + for matchIdx, match := range rule.Matches { + irRoute := &ir.HTTPRoute{ + Name: irRouteName(httpRoute, ruleIdx, matchIdx), + } + processTimeout(irRoute, rule) + + if match.Path != nil { + switch PathMatchTypeDerefOr(match.Path.Type, gwapiv1.PathMatchPathPrefix) { + case gwapiv1.PathMatchPathPrefix: + irRoute.PathMatch = &ir.StringMatch{ + Prefix: match.Path.Value, + } + case gwapiv1.PathMatchExact: + irRoute.PathMatch = &ir.StringMatch{ + Exact: match.Path.Value, + } + case gwapiv1.PathMatchRegularExpression: + if err := regex.Validate(*match.Path.Value); err != nil { + return nil, err + } + irRoute.PathMatch = &ir.StringMatch{ + SafeRegex: match.Path.Value, + } + } + } + for _, headerMatch := range match.Headers { + switch HeaderMatchTypeDerefOr(headerMatch.Type, gwapiv1.HeaderMatchExact) { + case gwapiv1.HeaderMatchExact: + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: string(headerMatch.Name), + Exact: ptr.To(headerMatch.Value), + }) + case gwapiv1.HeaderMatchRegularExpression: + if err := regex.Validate(headerMatch.Value); err != nil { + return nil, err + } + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: string(headerMatch.Name), + SafeRegex: ptr.To(headerMatch.Value), + }) + } + } + for _, queryParamMatch := range match.QueryParams { + switch QueryParamMatchTypeDerefOr(queryParamMatch.Type, gwapiv1.QueryParamMatchExact) { + case gwapiv1.QueryParamMatchExact: + irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ + Name: string(queryParamMatch.Name), + Exact: ptr.To(queryParamMatch.Value), + }) + case gwapiv1.QueryParamMatchRegularExpression: + if err := regex.Validate(queryParamMatch.Value); err != nil { + return nil, err + } + irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ + Name: string(queryParamMatch.Name), + SafeRegex: ptr.To(queryParamMatch.Value), + }) + } + } + + if match.Method != nil { + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: ":method", + Exact: ptr.To(string(*match.Method)), + }) + } + applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) + ruleRoutes = append(ruleRoutes, irRoute) + } + + return ruleRoutes, nil +} + +func applyHTTPFiltersContextToIRRoute(httpFiltersContext *HTTPFiltersContext, irRoute *ir.HTTPRoute) { + // Add the redirect filter or direct response that were created earlier to all the irRoutes + if httpFiltersContext.RedirectResponse != nil { + irRoute.Redirect = httpFiltersContext.RedirectResponse + } + if httpFiltersContext.DirectResponse != nil { + irRoute.DirectResponse = httpFiltersContext.DirectResponse + } + if httpFiltersContext.URLRewrite != nil { + irRoute.URLRewrite = httpFiltersContext.URLRewrite + } + if len(httpFiltersContext.AddRequestHeaders) > 0 { + irRoute.AddRequestHeaders = httpFiltersContext.AddRequestHeaders + } + if len(httpFiltersContext.RemoveRequestHeaders) > 0 { + irRoute.RemoveRequestHeaders = httpFiltersContext.RemoveRequestHeaders + } + if len(httpFiltersContext.AddResponseHeaders) > 0 { + irRoute.AddResponseHeaders = httpFiltersContext.AddResponseHeaders + } + if len(httpFiltersContext.RemoveResponseHeaders) > 0 { + irRoute.RemoveResponseHeaders = httpFiltersContext.RemoveResponseHeaders + } + if httpFiltersContext.Mirrors != nil { + irRoute.Mirrors = httpFiltersContext.Mirrors + } + + if len(httpFiltersContext.ExtensionRefs) > 0 { + irRoute.ExtensionRefs = httpFiltersContext.ExtensionRefs + } + +} + +func (t *Translator) processGRPCRouteParentRefs(grpcRoute *GRPCRouteContext, resources *Resources, xdsIR XdsIRMap) { + for _, parentRef := range grpcRoute.ParentRefs { + + // Need to compute Route rules within the parentRef loop because + // any conditions that come out of it have to go on each RouteParentStatus, + // not on the Route as a whole. + routeRoutes, err := t.processGRPCRouteRules(grpcRoute, parentRef, resources) + if err != nil { + parentRef.SetCondition(grpcRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, // TODO: better reason + status.Error2ConditionMsg(err), + ) + continue + } + + // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" + if !parentRef.HasCondition(grpcRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + parentRef.SetCondition(grpcRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.RouteReasonResolvedRefs, + "Resolved all the Object references for the Route", + ) + } + + if parentRef.HasCondition(grpcRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { + continue + } + var hasHostnameIntersection = t.processHTTPRouteParentRefListener(grpcRoute, routeRoutes, parentRef, xdsIR) + if !hasHostnameIntersection { + parentRef.SetCondition(grpcRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonNoMatchingListenerHostname, + "There were no hostname intersections between the GRPCRoute and this parent ref's Listener(s).", + ) + } + + // If no negative conditions have been set, the route is considered "Accepted=True". + if parentRef.GRPCRoute != nil && + len(parentRef.GRPCRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { + parentRef.SetCondition(grpcRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + + } +} + +func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRef *RouteParentContext, resources *Resources) ([]*ir.HTTPRoute, error) { + var routeRoutes []*ir.HTTPRoute + + // compute matches, filters, backends + for ruleIdx, rule := range grpcRoute.Spec.Rules { + httpFiltersContext := t.ProcessGRPCFilters(parentRef, grpcRoute, rule.Filters, resources) + + // A rule is matched if any one of its matches + // is satisfied (i.e. a logical "OR"), so generate + // a unique Xds IR HTTPRoute per match. + ruleRoutes, err := t.processGRPCRouteRule(grpcRoute, ruleIdx, httpFiltersContext, rule) + if err != nil { + return nil, err + } + + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + ds, backendWeight := t.processDestination(backendRef, parentRef, grpcRoute, resources) + for _, route := range ruleRoutes { + // If the route already has a direct response or redirect configured, then it was from a filter so skip + // processing any destinations for this route. + if route.DirectResponse == nil && route.Redirect == nil { + if ds != nil && len(ds.Endpoints) > 0 { + if route.Destination == nil { + route.Destination = &ir.RouteDestination{ + Name: irRouteDestinationName(grpcRoute, ruleIdx), + } + } + route.Destination.Settings = append(route.Destination.Settings, ds) + route.BackendWeights.Valid += backendWeight + + } else { + route.BackendWeights.Invalid += backendWeight + } + } + } + } + + // If the route has no valid backends then just use a direct response and don't fuss with weighted responses + for _, ruleRoute := range ruleRoutes { + if ruleRoute.Destination == nil && ruleRoute.Redirect == nil { + ruleRoute.DirectResponse = &ir.DirectResponse{ + StatusCode: 500, + } + } + ruleRoute.IsHTTP2 = true + } + + // TODO handle: + // - sum of weights for valid backend refs is 0 + // - etc. + + routeRoutes = append(routeRoutes, ruleRoutes...) + } + + return routeRoutes, nil +} + +func (t *Translator) processGRPCRouteRule(grpcRoute *GRPCRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1a2.GRPCRouteRule) ([]*ir.HTTPRoute, error) { + var ruleRoutes []*ir.HTTPRoute + + // If no matches are specified, the implementation MUST match every gRPC request. + if len(rule.Matches) == 0 { + irRoute := &ir.HTTPRoute{ + Name: irRouteName(grpcRoute, ruleIdx, -1), + } + applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) + ruleRoutes = append(ruleRoutes, irRoute) + } + + // A rule is matched if any one of its matches + // is satisfied (i.e. a logical "OR"), so generate + // a unique Xds IR HTTPRoute per match. + for matchIdx, match := range rule.Matches { + irRoute := &ir.HTTPRoute{ + Name: irRouteName(grpcRoute, ruleIdx, matchIdx), + } + + for _, headerMatch := range match.Headers { + switch HeaderMatchTypeDerefOr(headerMatch.Type, gwapiv1.HeaderMatchExact) { + case gwapiv1.HeaderMatchExact: + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: string(headerMatch.Name), + Exact: ptr.To(headerMatch.Value), + }) + case gwapiv1.HeaderMatchRegularExpression: + if err := regex.Validate(headerMatch.Value); err != nil { + return nil, err + } + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: string(headerMatch.Name), + SafeRegex: ptr.To(headerMatch.Value), + }) + } + } + + if match.Method != nil { + // GRPC's path is in the form of "//" + switch GRPCMethodMatchTypeDerefOr(match.Method.Type, gwapiv1a2.GRPCMethodMatchExact) { + case gwapiv1a2.GRPCMethodMatchExact: + t.processGRPCRouteMethodExact(match.Method, irRoute) + case gwapiv1a2.GRPCMethodMatchRegularExpression: + if match.Method.Service != nil { + if err := regex.Validate(*match.Method.Service); err != nil { + return nil, err + } + } + if match.Method.Method != nil { + if err := regex.Validate(*match.Method.Method); err != nil { + return nil, err + } + } + t.processGRPCRouteMethodRegularExpression(match.Method, irRoute) + } + } + + ruleRoutes = append(ruleRoutes, irRoute) + applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) + } + return ruleRoutes, nil +} + +func (t *Translator) processGRPCRouteMethodExact(method *gwapiv1a2.GRPCMethodMatch, irRoute *ir.HTTPRoute) { + switch { + case method.Service != nil && method.Method != nil: + irRoute.PathMatch = &ir.StringMatch{ + Exact: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), + } + case method.Method != nil: + // Use a header match since the PathMatch doesn't support Suffix matching + irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ + Name: ":path", + Suffix: ptr.To(fmt.Sprintf("/%s", *method.Method)), + }) + case method.Service != nil: + irRoute.PathMatch = &ir.StringMatch{ + Prefix: ptr.To(fmt.Sprintf("/%s", *method.Service)), + } + } +} + +func (t *Translator) processGRPCRouteMethodRegularExpression(method *gwapiv1a2.GRPCMethodMatch, irRoute *ir.HTTPRoute) { + switch { + case method.Service != nil && method.Method != nil: + irRoute.PathMatch = &ir.StringMatch{ + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, *method.Method)), + } + case method.Method != nil: + irRoute.PathMatch = &ir.StringMatch{ + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", validServiceName, *method.Method)), + } + case method.Service != nil: + irRoute.PathMatch = &ir.StringMatch{ + SafeRegex: ptr.To(fmt.Sprintf("/%s/%s", *method.Service, validMethodName)), + } + } +} + +func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, routeRoutes []*ir.HTTPRoute, parentRef *RouteParentContext, xdsIR XdsIRMap) bool { + var hasHostnameIntersection bool + + for _, listener := range parentRef.listeners { + hosts := computeHosts(GetHostnames(route), listener.Hostname) + if len(hosts) == 0 { + continue + } + hasHostnameIntersection = true + + var perHostRoutes []*ir.HTTPRoute + for _, host := range hosts { + for _, routeRoute := range routeRoutes { + // If the redirect port is not set, the final redirect port must be derived. + if routeRoute.Redirect != nil && routeRoute.Redirect.Port == nil { + redirectPort := uint32(listener.Port) + // If redirect scheme is not-empty, the redirect post must be the + // well-known port associated with the redirect scheme. + if scheme := routeRoute.Redirect.Scheme; scheme != nil { + switch strings.ToLower(*scheme) { + case "http": + redirectPort = 80 + case "https": + redirectPort = 443 + } + } + // If the redirect scheme does not have a well-known port, or + // if the redirect scheme is empty, the redirect port must be the Gateway Listener port. + routeRoute.Redirect.Port = &redirectPort + } + // Remove dots from the hostname before appending it to the IR Route name + // since dots are special chars used in stats tag extraction in Envoy + underscoredHost := strings.ReplaceAll(host, ".", "_") + hostRoute := &ir.HTTPRoute{ + Name: fmt.Sprintf("%s/%s", routeRoute.Name, underscoredHost), + Hostname: host, + PathMatch: routeRoute.PathMatch, + HeaderMatches: routeRoute.HeaderMatches, + QueryParamMatches: routeRoute.QueryParamMatches, + AddRequestHeaders: routeRoute.AddRequestHeaders, + RemoveRequestHeaders: routeRoute.RemoveRequestHeaders, + AddResponseHeaders: routeRoute.AddResponseHeaders, + RemoveResponseHeaders: routeRoute.RemoveResponseHeaders, + Destination: routeRoute.Destination, + Redirect: routeRoute.Redirect, + DirectResponse: routeRoute.DirectResponse, + URLRewrite: routeRoute.URLRewrite, + Mirrors: routeRoute.Mirrors, + ExtensionRefs: routeRoute.ExtensionRefs, + Timeout: routeRoute.Timeout, + Retry: routeRoute.Retry, + IsHTTP2: routeRoute.IsHTTP2, + } + // Don't bother copying over the weights unless the route has invalid backends. + if routeRoute.BackendWeights.Invalid > 0 { + hostRoute.BackendWeights = routeRoute.BackendWeights + } + perHostRoutes = append(perHostRoutes, hostRoute) + } + } + irKey := t.getIRKey(listener.gateway) + irListener := xdsIR[irKey].GetHTTPListener(irHTTPListenerName(listener)) + if irListener != nil { + if GetRouteType(route) == KindGRPCRoute { + irListener.IsHTTP2 = true + } + irListener.Routes = append(irListener.Routes, perHostRoutes...) + } + } + + return hasHostnameIntersection +} + +func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a2.TLSRoute, gateways []*GatewayContext, resources *Resources, xdsIR XdsIRMap) []*TLSRouteContext { + var relevantTLSRoutes []*TLSRouteContext + + for _, tls := range tlsRoutes { + if tls == nil { + panic("received nil tlsroute") + } + tlsRoute := &TLSRouteContext{ + GatewayControllerName: t.GatewayControllerName, + TLSRoute: tls.DeepCopy(), + } + + // Find out if this route attaches to one of our Gateway's listeners, + // and if so, get the list of listeners that allow it to attach for each + // parentRef. + relevantRoute := t.processAllowedListenersForParentRefs(tlsRoute, gateways, resources) + if !relevantRoute { + continue + } + + relevantTLSRoutes = append(relevantTLSRoutes, tlsRoute) + + t.processTLSRouteParentRefs(tlsRoute, resources, xdsIR) + } + + return relevantTLSRoutes +} + +func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resources *Resources, xdsIR XdsIRMap) { + for _, parentRef := range tlsRoute.ParentRefs { + + // Need to compute Route rules within the parentRef loop because + // any conditions that come out of it have to go on each RouteParentStatus, + // not on the Route as a whole. + var destSettings []*ir.DestinationSetting + + // compute backends + for _, rule := range tlsRoute.Spec.Rules { + for _, backendRef := range rule.BackendRefs { + backendRef := backendRef + ds, _ := t.processDestination(backendRef, parentRef, tlsRoute, resources) + if ds != nil { + destSettings = append(destSettings, ds) + } + } + + // TODO handle: + // - no valid backend refs + // - sum of weights for valid backend refs is 0 + // - returning 500's for invalid backend refs + // - etc. + } + + // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" + if !parentRef.HasCondition(tlsRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + parentRef.SetCondition(tlsRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.RouteReasonResolvedRefs, + "Resolved all the Object references for the Route", + ) + } + + // Skip parent refs that did not accept the route + if parentRef.HasCondition(tlsRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { + continue + } + + var hasHostnameIntersection bool + for _, listener := range parentRef.listeners { + hosts := computeHosts(GetHostnames(tlsRoute), listener.Hostname) + if len(hosts) == 0 { + continue + } + + hasHostnameIntersection = true + + irKey := t.getIRKey(listener.gateway) + + containerPort := servicePortToContainerPort(int32(listener.Port)) + // Create the TCP Listener while parsing the TLSRoute since + // the listener directly links to a routeDestination. + irListener := &ir.TCPListener{ + Name: irTLSListenerName(listener, tlsRoute), + Address: "0.0.0.0", + Port: uint32(containerPort), + TLS: &ir.TLS{Passthrough: &ir.TLSInspectorConfig{ + SNIs: hosts, + }}, + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(tlsRoute, -1 /*rule index*/), + Settings: destSettings, + }, + } + gwXdsIR := xdsIR[irKey] + gwXdsIR.TCP = append(gwXdsIR.TCP, irListener) + + } + + if !hasHostnameIntersection { + parentRef.SetCondition(tlsRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonNoMatchingListenerHostname, + "There were no hostname intersections between the HTTPRoute and this parent ref's Listener(s).", + ) + } + + // If no negative conditions have been set, the route is considered "Accepted=True". + if parentRef.TLSRoute != nil && + len(parentRef.TLSRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { + parentRef.SetCondition(tlsRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + } +} + +func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways []*GatewayContext, resources *Resources, + xdsIR XdsIRMap) []*UDPRouteContext { + var relevantUDPRoutes []*UDPRouteContext + + for _, u := range udpRoutes { + if u == nil { + panic("received nil udproute") + } + udpRoute := &UDPRouteContext{ + GatewayControllerName: t.GatewayControllerName, + UDPRoute: u.DeepCopy(), + } + + // Find out if this route attaches to one of our Gateway's listeners, + // and if so, get the list of listeners that allow it to attach for each + // parentRef. + relevantRoute := t.processAllowedListenersForParentRefs(udpRoute, gateways, resources) + if !relevantRoute { + continue + } + + relevantUDPRoutes = append(relevantUDPRoutes, udpRoute) + + t.processUDPRouteParentRefs(udpRoute, resources, xdsIR) + } + + return relevantUDPRoutes +} + +func (t *Translator) processUDPRouteParentRefs(udpRoute *UDPRouteContext, resources *Resources, xdsIR XdsIRMap) { + for _, parentRef := range udpRoute.ParentRefs { + // Need to compute Route rules within the parentRef loop because + // any conditions that come out of it have to go on each RouteParentStatus, + // not on the Route as a whole. + var destSettings []*ir.DestinationSetting + + // compute backends + if len(udpRoute.Spec.Rules) != 1 { + parentRef.SetCondition(udpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "InvalidRule", + "One and only one rule is supported", + ) + continue + } + if len(udpRoute.Spec.Rules[0].BackendRefs) != 1 { + parentRef.SetCondition(udpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "InvalidBackend", + "One and only one backend is supported", + ) + continue + } + + backendRef := udpRoute.Spec.Rules[0].BackendRefs[0] + ds, _ := t.processDestination(backendRef, parentRef, udpRoute, resources) + // Skip further processing if route destination is not valid + if ds == nil || len(ds.Endpoints) == 0 { + continue + } + + destSettings = append(destSettings, ds) + // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" + if !parentRef.HasCondition(udpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + parentRef.SetCondition(udpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.RouteReasonResolvedRefs, + "Resolved all the Object references for the Route", + ) + } + + // Skip parent refs that did not accept the route + if parentRef.HasCondition(udpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { + continue + } + + accepted := false + for _, listener := range parentRef.listeners { + // only one route is allowed for a UDP listener + if listener.AttachedRoutes() > 1 { + continue + } + if !listener.IsReady() { + continue + } + accepted = true + + irKey := t.getIRKey(listener.gateway) + + containerPort := servicePortToContainerPort(int32(listener.Port)) + // Create the UDP Listener while parsing the UDPRoute since + // the listener directly links to a routeDestination. + irListener := &ir.UDPListener{ + Name: irUDPListenerName(listener, udpRoute), + Address: "0.0.0.0", + Port: uint32(containerPort), + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(udpRoute, -1 /*rule index*/), + Settings: destSettings, + }, + } + gwXdsIR := xdsIR[irKey] + gwXdsIR.UDP = append(gwXdsIR.UDP, irListener) + + } + + // If no negative conditions have been set, the route is considered "Accepted=True". + if accepted && parentRef.UDPRoute != nil && + len(parentRef.UDPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { + parentRef.SetCondition(udpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + + if !accepted { + parentRef.SetCondition(udpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "Multiple routes on the same UDP listener", + ) + } + } +} + +func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways []*GatewayContext, resources *Resources, + xdsIR XdsIRMap) []*TCPRouteContext { + var relevantTCPRoutes []*TCPRouteContext + + for _, tcp := range tcpRoutes { + if tcp == nil { + panic("received nil tcproute") + } + tcpRoute := &TCPRouteContext{ + GatewayControllerName: t.GatewayControllerName, + TCPRoute: tcp.DeepCopy(), + } + + // Find out if this route attaches to one of our Gateway's listeners, + // and if so, get the list of listeners that allow it to attach for each + // parentRef. + relevantRoute := t.processAllowedListenersForParentRefs(tcpRoute, gateways, resources) + if !relevantRoute { + continue + } + + relevantTCPRoutes = append(relevantTCPRoutes, tcpRoute) + + t.processTCPRouteParentRefs(tcpRoute, resources, xdsIR) + } + + return relevantTCPRoutes +} + +func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resources *Resources, xdsIR XdsIRMap) { + for _, parentRef := range tcpRoute.ParentRefs { + + // Need to compute Route rules within the parentRef loop because + // any conditions that come out of it have to go on each RouteParentStatus, + // not on the Route as a whole. + var destSettings []*ir.DestinationSetting + + // compute backends + if len(tcpRoute.Spec.Rules) != 1 { + parentRef.SetCondition(tcpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "InvalidRule", + "One and only one rule is supported", + ) + continue + } + if len(tcpRoute.Spec.Rules[0].BackendRefs) != 1 { + parentRef.SetCondition(tcpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "InvalidBackend", + "One and only one backend is supported", + ) + continue + } + + backendRef := tcpRoute.Spec.Rules[0].BackendRefs[0] + ds, _ := t.processDestination(backendRef, parentRef, tcpRoute, resources) + // Skip further processing if route destination is not valid + if ds == nil || len(ds.Endpoints) == 0 { + continue + } + destSettings = append(destSettings, ds) + // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" + if !parentRef.HasCondition(tcpRoute, gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse) { + parentRef.SetCondition(tcpRoute, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.RouteReasonResolvedRefs, + "Resolved all the Object references for the Route", + ) + } + + // Skip parent refs that did not accept the route + if parentRef.HasCondition(tcpRoute, gwapiv1.RouteConditionAccepted, metav1.ConditionFalse) { + continue + } + + accepted := false + for _, listener := range parentRef.listeners { + // only one route is allowed for a TCP listener + if listener.AttachedRoutes() > 1 { + continue + } + if !listener.IsReady() { + continue + } + accepted = true + irKey := t.getIRKey(listener.gateway) + + containerPort := servicePortToContainerPort(int32(listener.Port)) + // Create the TCP Listener while parsing the TCPRoute since + // the listener directly links to a routeDestination. + irListener := &ir.TCPListener{ + Name: irTCPListenerName(listener, tcpRoute), + Address: "0.0.0.0", + Port: uint32(containerPort), + Destination: &ir.RouteDestination{ + Name: irRouteDestinationName(tcpRoute, -1 /*rule index*/), + Settings: destSettings, + }, + TLS: &ir.TLS{Terminate: irTLSConfigs(listener.tlsSecrets)}, + } + gwXdsIR := xdsIR[irKey] + gwXdsIR.TCP = append(gwXdsIR.TCP, irListener) + + } + + // If no negative conditions have been set, the route is considered "Accepted=True". + if accepted && parentRef.TCPRoute != nil && + len(parentRef.TCPRoute.Status.Parents[parentRef.routeParentStatusIdx].Conditions) == 0 { + parentRef.SetCondition(tcpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + if !accepted { + parentRef.SetCondition(tcpRoute, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonUnsupportedValue, + "Multiple routes on the same TCP listener", + ) + } + + } +} + +// processDestination takes a backendRef and translates it into destination setting or sets error statuses and +// returns the weight for the backend so that 500 error responses can be returned for invalid backends in +// the same proportion as the backend would have otherwise received +func (t *Translator) processDestination(backendRefContext BackendRefContext, + parentRef *RouteParentContext, + route RouteContext, + resources *Resources) (ds *ir.DestinationSetting, backendWeight uint32) { + routeType := GetRouteType(route) + weight := uint32(1) + backendRef := GetBackendRef(backendRefContext) + if backendRef.Weight != nil { + weight = uint32(*backendRef.Weight) + } + + backendNamespace := NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()) + if !t.validateBackendRef(backendRefContext, parentRef, route, resources, backendNamespace, routeType) { + return nil, weight + } + + // Skip processing backends with 0 weight + if weight == 0 { + return nil, weight + } + + var ( + endpoints []*ir.DestinationEndpoint + addrType *ir.DestinationAddressType + ) + protocol := inspectAppProtocolByRouteKind(routeType) + var backendTLS *ir.TLSUpstreamConfig + switch KindDerefOr(backendRef.Kind, KindService) { + case KindService: + service := resources.GetService(backendNamespace, string(backendRef.Name)) + var servicePort corev1.ServicePort + for _, port := range service.Spec.Ports { + if port.Port == int32(*backendRef.Port) { + servicePort = port + break + } + } + + // support HTTPRouteBackendProtocolH2C + if servicePort.AppProtocol != nil && + *servicePort.AppProtocol == "kubernetes.io/h2c" { + protocol = ir.HTTP2 + } + + // Route to endpoints by default + if !t.EndpointRoutingDisabled { + endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, KindService)) + endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, servicePort.Protocol) + } else { + // Fall back to Service ClusterIP routing + ep := ir.NewDestEndpoint( + service.Spec.ClusterIP, + uint32(*backendRef.Port)) + endpoints = append(endpoints, ep) + } + } + + // TODO: support mixed endpointslice address type for the same backendRef + if !t.EndpointRoutingDisabled && addrType != nil && *addrType == ir.MIXED { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1a2.RouteReasonResolvedRefs, + "Mixed endpointslice address type for the same backendRef is not supported") + } + + ds = &ir.DestinationSetting{ + Weight: &weight, + Protocol: protocol, + Endpoints: endpoints, + AddressType: addrType, + TLS: backendTLS, + } + return ds, weight +} + +func inspectAppProtocolByRouteKind(kind gwapiv1.Kind) ir.AppProtocol { + switch kind { + case KindUDPRoute: + return ir.UDP + case KindHTTPRoute: + return ir.HTTP + case KindTCPRoute: + return ir.TCP + case KindGRPCRoute: + return ir.GRPC + case KindTLSRoute: + return ir.HTTPS + } + return ir.TCP +} + +// processAllowedListenersForParentRefs finds out if the route attaches to one of our +// Gateways' listeners, and if so, gets the list of listeners that allow it to +// attach for each parentRef. +func (t *Translator) processAllowedListenersForParentRefs(routeContext RouteContext, gateways []*GatewayContext, resources *Resources) bool { + var relevantRoute bool + + for _, parentRef := range GetParentReferences(routeContext) { + isRelevantParentRef, selectedListeners := GetReferencedListeners(parentRef, gateways) + + // Parent ref is not to a Gateway that we control: skip it + if !isRelevantParentRef { + continue + } + relevantRoute = true + + parentRefCtx := GetRouteParentContext(routeContext, parentRef) + // Reset conditions since they will be recomputed during translation + parentRefCtx.ResetConditions(routeContext) + + if len(selectedListeners) == 0 { + parentRefCtx.SetCondition(routeContext, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonNoMatchingParent, + "No listeners match this parent ref", + ) + continue + } + + var allowedListeners []*ListenerContext + for _, listener := range selectedListeners { + acceptedKind := GetRouteType(routeContext) + if listener.AllowsKind(gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: acceptedKind}) && + listener.AllowsNamespace(resources.GetNamespace(routeContext.GetNamespace())) { + allowedListeners = append(allowedListeners, listener) + } + } + + if len(allowedListeners) == 0 { + parentRefCtx.SetCondition(routeContext, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + gwapiv1.RouteReasonNotAllowedByListeners, + "No listeners included by this parent ref allowed this attachment.", + ) + continue + } + + // Its safe to increment AttachedRoutes since we've found a valid parentRef + // and the listener allows this Route kind + + // Theoretically there should only be one parent ref per + // Route that attaches to a given Listener, so fine to just increment here, but we + // might want to check to ensure we're not double-counting. + for _, listener := range allowedListeners { + listener.IncrementAttachedRoutes() + } + + if !HasReadyListener(selectedListeners) { + parentRefCtx.SetCondition(routeContext, + gwapiv1.RouteConditionAccepted, + metav1.ConditionFalse, + "NoReadyListeners", + "There are no ready listeners for this parent ref", + ) + continue + } + + parentRefCtx.SetListeners(allowedListeners...) + + parentRefCtx.SetCondition(routeContext, + gwapiv1.RouteConditionAccepted, + metav1.ConditionTrue, + gwapiv1.RouteReasonAccepted, + "Route is accepted", + ) + } + return relevantRoute +} + +func getIREndpointsFromEndpointSlices(endpointSlices []*discoveryv1.EndpointSlice, portName string, portProtocol corev1.Protocol) ([]*ir.DestinationEndpoint, *ir.DestinationAddressType) { + var ( + dstEndpoints []*ir.DestinationEndpoint + dstAddrType *ir.DestinationAddressType + ) + + addrTypeMap := make(map[ir.DestinationAddressType]int) + for _, endpointSlice := range endpointSlices { + if endpointSlice.AddressType == discoveryv1.AddressTypeFQDN { + addrTypeMap[ir.FQDN]++ + } else { + addrTypeMap[ir.IP]++ + } + endpoints := getIREndpointsFromEndpointSlice(endpointSlice, portName, portProtocol) + dstEndpoints = append(dstEndpoints, endpoints...) + } + + for addrTypeState, addrTypeCounts := range addrTypeMap { + if addrTypeCounts == len(endpointSlices) { + dstAddrType = ptr.To(addrTypeState) + break + } + } + + if len(addrTypeMap) > 0 && dstAddrType == nil { + dstAddrType = ptr.To(ir.MIXED) + } + + return dstEndpoints, dstAddrType +} + +func getIREndpointsFromEndpointSlice(endpointSlice *discoveryv1.EndpointSlice, portName string, portProtocol corev1.Protocol) []*ir.DestinationEndpoint { + var endpoints []*ir.DestinationEndpoint + for _, endpoint := range endpointSlice.Endpoints { + for _, endpointPort := range endpointSlice.Ports { + // Check if the endpoint port matches the service port + // and if endpoint is Ready + if *endpointPort.Name == portName && + *endpointPort.Protocol == portProtocol && + // Unknown state (nil) should be interpreted as Ready, see https://pkg.go.dev/k8s.io/api/discovery/v1#EndpointConditions + (endpoint.Conditions.Ready == nil || *endpoint.Conditions.Ready) { + for _, address := range endpoint.Addresses { + ep := ir.NewDestEndpoint( + address, + uint32(*endpointPort.Port)) + endpoints = append(endpoints, ep) + } + } + } + } + + return endpoints +} + +func GetTargetBackendReference(backendRef gwapiv1a2.BackendObjectReference, namespace string) gwapiv1a2.PolicyTargetReferenceWithSectionName { + ref := gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: func() gwapiv1a2.Group { + if backendRef.Group == nil { + return "" + } + return *backendRef.Group + }(), + Kind: func() gwapiv1.Kind { + if backendRef.Kind == nil { + return "Service" + } + return *backendRef.Kind + }(), + Name: backendRef.Name, + Namespace: NamespacePtr(NamespaceDerefOr(backendRef.Namespace, namespace)), + }, + SectionName: func() *gwapiv1.SectionName { + if backendRef.Port != nil { + return SectionNamePtr(strconv.Itoa(int(*backendRef.Port))) + } + return nil + }(), + } + return ref +} diff --git a/adapter/internal/operator/gateway-api/secrets.go b/adapter/internal/operator/gateway-api/secrets.go new file mode 100644 index 0000000000..46b8a6e7f8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/secrets.go @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "context" + "errors" + "fmt" + "reflect" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/crypto" + "github.com/wso2/apk/adapter/internal/operator/utils" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + ErrSecretExists = errors.New("skipped creating secret since it already exists") +) + +// caCertificateKey is the key name for accessing TLS CA certificate bundles +// in Kubernetes Secrets. +const ( + caCertificateKey = "ca.crt" + hmacSecretKey = "hmac-secret" +) + +func newSecret(secretType corev1.SecretType, name string, namespace string, data map[string][]byte) corev1.Secret { + return corev1.Secret{ + Type: secretType, + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "control-plane": "envoy-gateway", + }, + }, + Data: data, + } +} + +// CertsToSecret creates secrets in the provided namespace, in compact form, from the provided certs. +func CertsToSecret(namespace string, certs *crypto.Certificates) []corev1.Secret { + return []corev1.Secret{ + newSecret( + corev1.SecretTypeTLS, + "envoy-gateway", + namespace, + map[string][]byte{ + caCertificateKey: certs.CACertificate, + corev1.TLSCertKey: certs.EnvoyGatewayCertificate, + corev1.TLSPrivateKeyKey: certs.EnvoyGatewayPrivateKey, + }), + newSecret( + corev1.SecretTypeTLS, + "envoy", + namespace, + map[string][]byte{ + caCertificateKey: certs.CACertificate, + corev1.TLSCertKey: certs.EnvoyCertificate, + corev1.TLSPrivateKeyKey: certs.EnvoyPrivateKey, + }), + newSecret( + corev1.SecretTypeTLS, + "envoy-rate-limit", + namespace, + map[string][]byte{ + caCertificateKey: certs.CACertificate, + corev1.TLSCertKey: certs.EnvoyRateLimitCertificate, + corev1.TLSPrivateKeyKey: certs.EnvoyRateLimitPrivateKey, + }), + newSecret( + corev1.SecretTypeOpaque, + "envoy-oidc-hmac", + namespace, + map[string][]byte{ + hmacSecretKey: certs.OIDCHMACSecret, + }), + } +} + +// CreateOrUpdateSecrets creates the provided secrets if they don't exist or updates +// them if they do. +func CreateOrUpdateSecrets(ctx context.Context, client client.Client, secrets []corev1.Secret, update bool) ([]corev1.Secret, error) { + var ( + tidySecrets []corev1.Secret + existingSecrets []string + ) + + for secret := range secrets { + secret := secrets[secret] + current := new(corev1.Secret) + key := utils.NamespacedName(&secret) + if err := client.Get(ctx, key, current); err != nil { + // Create if not found. + if kerrors.IsNotFound(err) { + if err := client.Create(ctx, &secret); err != nil { + return nil, fmt.Errorf("failed to create secret %s/%s: %w", secret.Namespace, secret.Name, err) + } + } else { + return nil, fmt.Errorf("failed to get secret %s/%s: %w", secret.Namespace, secret.Name, err) + } + // Update if current value is different and update arg is set. + } else { + if !update { + existingSecrets = append(existingSecrets, fmt.Sprintf("%s/%s", secret.Namespace, secret.Name)) + continue + } + fmt.Println() + + if !reflect.DeepEqual(secret.Data, current.Data) { + if err := client.Update(ctx, &secret); err != nil { + return nil, fmt.Errorf("failed to update secret %s/%s: %w", secret.Namespace, secret.Name, err) + } + } + } + tidySecrets = append(tidySecrets, secret) + } + + if len(existingSecrets) > 0 { + return tidySecrets, fmt.Errorf("%v: %w;"+ + "Either update the secrets manually or set overwriteControlPlaneCerts "+ + "in the EnvoyGateway config", existingSecrets, ErrSecretExists) + } + + return tidySecrets, nil +} diff --git a/adapter/internal/operator/gateway-api/secrets_test.go b/adapter/internal/operator/gateway-api/secrets_test.go new file mode 100644 index 0000000000..6715eae4b4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/secrets_test.go @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var ( + envoyGatewaySecret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "envoy-gateway", + Namespace: "apk", + }, + } + + envoySecret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "envoy", + Namespace: "apk", + }, + } + + envoyRateLimitSecret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "envoy-rate-limit", + Namespace: "apk", + }, + } + + oidcHMACSecret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "envoy-oidc-hmac", + Namespace: "apk", + }, + } + + existingSecretsWithoutHMAC = []client.Object{ + &envoyGatewaySecret, + &envoySecret, + &envoyRateLimitSecret, + } + + existingSecretsWithHMAC = []client.Object{ + &envoyGatewaySecret, + &envoySecret, + &envoyRateLimitSecret, + &oidcHMACSecret, + } + + SecretsToCreate = []corev1.Secret{ + envoyGatewaySecret, + envoySecret, + envoyRateLimitSecret, + oidcHMACSecret, + } +) + +func TestCreateSecretsWhenUpgrade(t *testing.T) { + t.Run("create HMAC secret when it does not exist", func(t *testing.T) { + cli := fakeclient.NewClientBuilder().WithObjects(existingSecretsWithoutHMAC...).Build() + + created, err := CreateOrUpdateSecrets(context.Background(), cli, SecretsToCreate, false) + require.ErrorIs(t, err, ErrSecretExists) + require.Len(t, created, 1) + require.Equal(t, "envoy-oidc-hmac", created[0].Name) + + err = cli.Get(context.Background(), client.ObjectKeyFromObject(&oidcHMACSecret), &corev1.Secret{}) + require.NoError(t, err) + }) + + t.Run("skip HMAC secret when it exist", func(t *testing.T) { + cli := fakeclient.NewClientBuilder().WithObjects(existingSecretsWithHMAC...).Build() + + created, err := CreateOrUpdateSecrets(context.Background(), cli, SecretsToCreate, false) + require.ErrorIs(t, err, ErrSecretExists) + require.Emptyf(t, created, "expected no secrets to be created, got %v", created) + }) + + t.Run("update secrets when they exist", func(t *testing.T) { + cli := fakeclient.NewClientBuilder().WithObjects(existingSecretsWithHMAC...).Build() + + created, err := CreateOrUpdateSecrets(context.Background(), cli, SecretsToCreate, true) + require.NoError(t, err) + require.Len(t, created, 4) + }) +} diff --git a/adapter/internal/operator/gateway-api/sort.go b/adapter/internal/operator/gateway-api/sort.go new file mode 100644 index 0000000000..eef7d36484 --- /dev/null +++ b/adapter/internal/operator/gateway-api/sort.go @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "sort" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" +) + +type XdsIRRoutes []*ir.HTTPRoute + +func (x XdsIRRoutes) Len() int { return len(x) } +func (x XdsIRRoutes) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x XdsIRRoutes) Less(i, j int) bool { + + // 1. Sort based on path match type + // Exact > RegularExpression > PathPrefix + if x[i].PathMatch != nil && x[i].PathMatch.Exact != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.SafeRegex != nil { + return false + } + if x[j].PathMatch.Prefix != nil { + return false + } + } + } + if x[i].PathMatch != nil && x[i].PathMatch.SafeRegex != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.Exact != nil { + return true + } + if x[j].PathMatch.Prefix != nil { + return false + } + } + } + if x[i].PathMatch != nil && x[i].PathMatch.Prefix != nil { + if x[j].PathMatch != nil { + if x[j].PathMatch.Exact != nil { + return true + } + if x[j].PathMatch.SafeRegex != nil { + return true + } + } + } + // Equal case + + // 2. Sort based on characters in a matching path. + pCountI := pathMatchCount(x[i].PathMatch) + pCountJ := pathMatchCount(x[j].PathMatch) + if pCountI < pCountJ { + return true + } + if pCountI > pCountJ { + return false + } + // Equal case + + // 3. Sort based on the number of Header matches. + hCountI := len(x[i].HeaderMatches) + hCountJ := len(x[j].HeaderMatches) + if hCountI < hCountJ { + return true + } + if hCountI > hCountJ { + return false + } + // Equal case + + // 4. Sort based on the number of Query param matches. + qCountI := len(x[i].QueryParamMatches) + qCountJ := len(x[j].QueryParamMatches) + return qCountI < qCountJ +} + +// sortXdsIR sorts the xdsIR based on the match precedence +// defined in the Gateway API spec. +// https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule +func sortXdsIRMap(xdsIR XdsIRMap) { + for _, irItem := range xdsIR { + for _, http := range irItem.HTTP { + // descending order + sort.Sort(sort.Reverse(XdsIRRoutes(http.Routes))) + } + } +} + +func pathMatchCount(pathMatch *ir.StringMatch) int { + if pathMatch != nil { + if pathMatch.Exact != nil { + return len(*pathMatch.Exact) + } + if pathMatch.SafeRegex != nil { + return len(*pathMatch.SafeRegex) + } + if pathMatch.Prefix != nil { + return len(*pathMatch.Prefix) + } + } + return 0 +} diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.in.yaml new file mode 100644 index 0000000000..391eeb060b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.in.yaml @@ -0,0 +1,137 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: backends + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: backends + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: BackendTLSPolicy + namespace: policies + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: backends + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: backends + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ca-cmap + namespace: policies + data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIDJzCCAg+gAwIBAgIUAl6UKIuKmzte81cllz5PfdN2IlIwDQYJKoZIhvcNAQEL + BQAwIzEQMA4GA1UEAwwHbXljaWVudDEPMA0GA1UECgwGa3ViZWRiMB4XDTIzMTAw + MjA1NDE1N1oXDTI0MTAwMTA1NDE1N1owIzEQMA4GA1UEAwwHbXljaWVudDEPMA0G + A1UECgwGa3ViZWRiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSTc + 1yj8HW62nynkFbXo4VXKv2jC0PM7dPVky87FweZcTKLoWQVPQE2p2kLDK6OEszmM + yyr+xxWtyiveremrWqnKkNTYhLfYPhgQkczib7eUalmFjUbhWdLvHakbEgCodn3b + kz57mInX2VpiDOKg4kyHfiuXWpiBqrCx0KNLpxo3DEQcFcsQTeTHzh4752GV04RU + Ti/GEWyzIsl4Rg7tGtAwmcIPgUNUfY2Q390FGqdH4ahn+mw/6aFbW31W63d9YJVq + ioyOVcaMIpM5B/c7Qc8SuhCI1YGhUyg4cRHLEw5VtikioyE3X04kna3jQAj54YbR + bpEhc35apKLB21HOUQIDAQABo1MwUTAdBgNVHQ4EFgQUyvl0VI5vJVSuYFXu7B48 + 6PbMEAowHwYDVR0jBBgwFoAUyvl0VI5vJVSuYFXu7B486PbMEAowDwYDVR0TAQH/ + BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMLxrgFVMuNRq2wAwcBt7SnNR5Cfz + 2MvXq5EUmuawIUi9kaYjwdViDREGSjk7JW17vl576HjDkdfRwi4E28SydRInZf6J + i8HZcZ7caH6DxR335fgHVzLi5NiTce/OjNBQzQ2MJXVDd8DBmG5fyatJiOJQ4bWE + A7FlP0RdP3CO3GWE0M5iXOB2m1qWkE2eyO4UHvwTqNQLdrdAXgDQlbam9e4BG3Gg + d/6thAkWDbt/QNT+EJHDCvhDRKh1RuGHyg+Y+/nebTWWrFWsktRrbOoHCZiCpXI1 + 3eXE6nt0YkgtDxG22KqnhpAg9gUSs2hlhoxyvkzyF0mu6NhPlwAgnq7+/Q== + -----END CERTIFICATE----- +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: policies + spec: + targetRef: + group: '' + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - name: ca-cmap + group: '' + kind: ConfigMap + hostname: example.com diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.out.yaml new file mode 100644 index 0000000000..bda2c196ac --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-ca-only.out.yaml @@ -0,0 +1,169 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: policies + spec: + targetRef: + group: "" + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: example.com + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: BackendTLSPolicy is Accepted + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-btls + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/policies-ca + sni: example.com + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.in.yaml new file mode 100644 index 0000000000..f935b78dc7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.in.yaml @@ -0,0 +1,137 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: default + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: default + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: BackendTLSPolicy + namespace: default + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: default + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: default + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + + +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ca-cmap + namespace: default + data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIDJzCCAg+gAwIBAgIUAl6UKIuKmzte81cllz5PfdN2IlIwDQYJKoZIhvcNAQEL + BQAwIzEQMA4GA1UEAwwHbXljaWVudDEPMA0GA1UECgwGa3ViZWRiMB4XDTIzMTAw + MjA1NDE1N1oXDTI0MTAwMTA1NDE1N1owIzEQMA4GA1UEAwwHbXljaWVudDEPMA0G + A1UECgwGa3ViZWRiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSTc + 1yj8HW62nynkFbXo4VXKv2jC0PM7dPVky87FweZcTKLoWQVPQE2p2kLDK6OEszmM + yyr+xxWtyiveremrWqnKkNTYhLfYPhgQkczib7eUalmFjUbhWdLvHakbEgCodn3b + kz57mInX2VpiDOKg4kyHfiuXWpiBqrCx0KNLpxo3DEQcFcsQTeTHzh4752GV04RU + Ti/GEWyzIsl4Rg7tGtAwmcIPgUNUfY2Q390FGqdH4ahn+mw/6aFbW31W63d9YJVq + ioyOVcaMIpM5B/c7Qc8SuhCI1YGhUyg4cRHLEw5VtikioyE3X04kna3jQAj54YbR + bpEhc35apKLB21HOUQIDAQABo1MwUTAdBgNVHQ4EFgQUyvl0VI5vJVSuYFXu7B48 + 6PbMEAowHwYDVR0jBBgwFoAUyvl0VI5vJVSuYFXu7B486PbMEAowDwYDVR0TAQH/ + BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMLxrgFVMuNRq2wAwcBt7SnNR5Cfz + 2MvXq5EUmuawIUi9kaYjwdViDREGSjk7JW17vl576HjDkdfRwi4E28SydRInZf6J + i8HZcZ7caH6DxR335fgHVzLi5NiTce/OjNBQzQ2MJXVDd8DBmG5fyatJiOJQ4bWE + A7FlP0RdP3CO3GWE0M5iXOB2m1qWkE2eyO4UHvwTqNQLdrdAXgDQlbam9e4BG3Gg + d/6thAkWDbt/QNT+EJHDCvhDRKh1RuGHyg+Y+/nebTWWrFWsktRrbOoHCZiCpXI1 + 3eXE6nt0YkgtDxG22KqnhpAg9gUSs2hlhoxyvkzyF0mu6NhPlwAgnq7+/Q== + -----END CERTIFICATE----- +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: default + spec: + targetRef: + group: '' + kind: Service + name: http-backend + sectionName: "8080" + tls: + caCertRefs: + - name: ca-cmap + group: '' + kind: ConfigMap + hostname: example.com diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.out.yaml new file mode 100644 index 0000000000..21375f0799 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-default-ns.out.yaml @@ -0,0 +1,168 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: default + spec: + targetRef: + group: "" + kind: Service + name: http-backend + sectionName: "8080" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: example.com + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: BackendTLSPolicy is Accepted + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: default + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-btls + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/default-ca + sni: example.com + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.in.yaml new file mode 100644 index 0000000000..d11c1a5f28 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.in.yaml @@ -0,0 +1,119 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: backends + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: backends + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: BackendTLSPolicy + namespace: policies + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: backends + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: backends + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: no-ca-cmap + namespace: policies + data: + garbage.crt: | + itsAllGarbage +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: policies + spec: + targetRef: + group: '' + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - name: no-ca-cmap + group: '' + kind: ConfigMap + hostname: example.com diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.out.yaml new file mode 100644 index 0000000000..0d234e3cae --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-invalid-ca.out.yaml @@ -0,0 +1,164 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: policies + spec: + targetRef: + group: "" + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: no-ca-cmap + hostname: example.com + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: No ca found in configmap no-ca-cmap + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-btls + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.in.yaml new file mode 100644 index 0000000000..88fb94bc11 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.in.yaml @@ -0,0 +1,105 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: default + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: default + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: BackendTLSPolicy + namespace: default + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: default + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: default + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: default + spec: + targetRef: + group: '' + kind: Service + name: http-backend + sectionName: "8080" + tls: + wellKnownCACerts: System + hostname: example.com diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.out.yaml new file mode 100644 index 0000000000..57bc73c40e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-system-truststore.out.yaml @@ -0,0 +1,163 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: default + spec: + targetRef: + group: "" + kind: Service + name: http-backend + sectionName: "8080" + tls: + hostname: example.com + wellKnownCACerts: System + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: BackendTLSPolicy is Accepted + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: default + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-btls + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + tls: + sni: example.com + useSystemTrustStore: true + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.in.yaml new file mode 100644 index 0000000000..f773f20088 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.in.yaml @@ -0,0 +1,134 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-btls + sectionName: http + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: http-backend + namespace: backends + port: 8080 + +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + name: refg-route-svc + namespace: backends + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: envoy-gateway + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + to: + - group: "" + kind: Service + +services: + - apiVersion: v1 + kind: Service + metadata: + name: http-backend + namespace: backends + spec: + clusterIP: 10.11.12.13 + ports: + - port: 8080 + name: http + protocol: TCP + targetPort: 8080 + + +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: backends + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ca-cmap + namespace: policies + data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIDJzCCAg+gAwIBAgIUAl6UKIuKmzte81cllz5PfdN2IlIwDQYJKoZIhvcNAQEL + BQAwIzEQMA4GA1UEAwwHbXljaWVudDEPMA0GA1UECgwGa3ViZWRiMB4XDTIzMTAw + MjA1NDE1N1oXDTI0MTAwMTA1NDE1N1owIzEQMA4GA1UEAwwHbXljaWVudDEPMA0G + A1UECgwGa3ViZWRiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSTc + 1yj8HW62nynkFbXo4VXKv2jC0PM7dPVky87FweZcTKLoWQVPQE2p2kLDK6OEszmM + yyr+xxWtyiveremrWqnKkNTYhLfYPhgQkczib7eUalmFjUbhWdLvHakbEgCodn3b + kz57mInX2VpiDOKg4kyHfiuXWpiBqrCx0KNLpxo3DEQcFcsQTeTHzh4752GV04RU + Ti/GEWyzIsl4Rg7tGtAwmcIPgUNUfY2Q390FGqdH4ahn+mw/6aFbW31W63d9YJVq + ioyOVcaMIpM5B/c7Qc8SuhCI1YGhUyg4cRHLEw5VtikioyE3X04kna3jQAj54YbR + bpEhc35apKLB21HOUQIDAQABo1MwUTAdBgNVHQ4EFgQUyvl0VI5vJVSuYFXu7B48 + 6PbMEAowHwYDVR0jBBgwFoAUyvl0VI5vJVSuYFXu7B486PbMEAowDwYDVR0TAQH/ + BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMLxrgFVMuNRq2wAwcBt7SnNR5Cfz + 2MvXq5EUmuawIUi9kaYjwdViDREGSjk7JW17vl576HjDkdfRwi4E28SydRInZf6J + i8HZcZ7caH6DxR335fgHVzLi5NiTce/OjNBQzQ2MJXVDd8DBmG5fyatJiOJQ4bWE + A7FlP0RdP3CO3GWE0M5iXOB2m1qWkE2eyO4UHvwTqNQLdrdAXgDQlbam9e4BG3Gg + d/6thAkWDbt/QNT+EJHDCvhDRKh1RuGHyg+Y+/nebTWWrFWsktRrbOoHCZiCpXI1 + 3eXE6nt0YkgtDxG22KqnhpAg9gUSs2hlhoxyvkzyF0mu6NhPlwAgnq7+/Q== + -----END CERTIFICATE----- +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls + namespace: policies + spec: + targetRef: + group: '' + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - name: ca-cmap + group: '' + kind: ConfigMap + hostname: example.com diff --git a/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.out.yaml new file mode 100755 index 0000000000..2cc528d759 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtlspolicy-without-referencegrant.out.yaml @@ -0,0 +1,165 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls + namespace: policies + spec: + targetRef: + group: "" + kind: Service + name: http-backend + namespace: backends + sectionName: "8080" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: example.com + status: + ancestors: + - ancestorRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: target ref to Service backends/http-backend not permitted by any + ReferenceGrant + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-btls + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-btls + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-btls + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: http-backend + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-btls + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-btls: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-btls/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-btls + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-btls +xdsIR: + envoy-gateway/gateway-btls: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.in.yaml new file mode 100644 index 0000000000..ea9b2f65a4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.in.yaml @@ -0,0 +1,89 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + loadBalancer: + type: Random + timeout: + tcp: + connectTimeout: 20s + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + loadBalancer: + type: ConsistentHash + consistentHash: + type: SourceIP diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.out.yaml new file mode 100755 index 0000000000..4b9a5428d5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-override-replace.out.yaml @@ -0,0 +1,271 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-1 + namespace: default + spec: + loadBalancer: + consistentHash: + type: SourceIP + type: ConsistentHash + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: envoy-gateway + spec: + loadBalancer: + type: Random + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s + tcp: + connectTimeout: 20s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other backendTrafficPolicies + for these routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + loadBalancer: + consistentHash: + sourceIP: true + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + loadBalancer: + random: {} + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /bar + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s + tcp: + connectTimeout: 20s diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.in.yaml new file mode 100644 index 0000000000..ed13bb09c2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.in.yaml @@ -0,0 +1,207 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-unknown-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: unknown-gateway + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: not-same-namespace-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: not-same-namespace-gateway + namespace: another-namespace +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: also-target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-grpcroute-in-gateway-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-unknown-httproute + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: unknown-httproute + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: not-same-namespace-httproute + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: not-same-namespace-httproute + namespace: another-namespace +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: another-namespace + name: not-same-namespace-httproute + spec: + parentRefs: + - namespace: another-namespace + name: not-same-namespace-gateway + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: envoy-gateway + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - matches: + - headers: + - type: Exact + name: magic + value: foo + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + - name: tcp + protocol: TCP + port: 53 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: another-namespace + name: not-same-namespace-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.out.yaml new file mode 100644 index 0000000000..f0ea790e4a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-conditions.out.yaml @@ -0,0 +1,621 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: also-target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Unable to target HTTPRoute, another BackendTrafficPolicy has already + attached to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-grpcroute-in-gateway-2 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-unknown-httproute + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: unknown-httproute + namespace: envoy-gateway + status: + ancestors: null +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: not-same-namespace-httproute + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: not-same-namespace-httproute + namespace: another-namespace + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: not-same-namespace-gateway + namespace: another-namespace + conditions: + - lastTransitionTime: null + message: Namespace:envoy-gateway TargetRef.Namespace:another-namespace, BackendTrafficPolicy + can only target a resource in the same namespace. + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other backendTrafficPolicies + for these routes: [envoy-gateway/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Unable to target Gateway, another BackendTrafficPolicy has already + attached to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: target-unknown-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: unknown-gateway + namespace: envoy-gateway + status: + ancestors: null +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: not-same-namespace-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: not-same-namespace-gateway + namespace: another-namespace + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: not-same-namespace-gateway + namespace: another-namespace + conditions: + - lastTransitionTime: null + message: Namespace:envoy-gateway TargetRef.Namespace:another-namespace, BackendTrafficPolicy + can only target a resource in the same namespace. + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + - allowedRoutes: + namespaces: + from: Same + name: tcp + port: 53 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: not-same-namespace-gateway + namespace: another-namespace + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: magic + type: Exact + value: foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: not-same-namespace-httproute + namespace: another-namespace + spec: + parentRefs: + - name: not-same-namespace-gateway + namespace: another-namespace + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Service another-namespace/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: not-same-namespace-gateway + namespace: another-namespace +infraIR: + another-namespace/not-same-namespace-gateway: + proxy: + listeners: + - address: null + name: another-namespace/not-same-namespace-gateway/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: not-same-namespace-gateway + apk.wso2.com/owning-gateway-namespace: another-namespace + name: another-namespace/not-same-namespace-gateway + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-2/tcp + ports: + - containerPort: 10053 + name: tcp + protocol: TCP + servicePort: 53 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + another-namespace/not-same-namespace-gateway: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: another-namespace/not-same-namespace-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + headerMatches: + - distinct: false + exact: foo + name: magic + hostname: '*' + isHTTP2: true + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/* diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.in.yaml new file mode 100644 index 0000000000..8104c47f6f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.in.yaml @@ -0,0 +1,135 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/route2" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + faultInjection: + abort: + httpStatus: 14 + percentage: 0.01 +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + faultInjection: + abort: + httpStatus: 500 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-grpcroute + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: default + faultInjection: + abort: + grpcStatus: 14 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.out.yaml new file mode 100644 index 0000000000..401442b05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-status-fault-injection.out.yaml @@ -0,0 +1,430 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + faultInjection: + abort: + httpStatus: 500 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-grpcroute + namespace: default + spec: + faultInjection: + abort: + grpcStatus: 14 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + faultInjection: + abort: + httpStatus: 14 + percentage: 0.01 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other backendTrafficPolicies + for these routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /route2 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + faultInjection: + abort: + grpcStatus: 14 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + faultInjection: + abort: + httpStatus: 14 + percentage: 0.01 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /route2 + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + faultInjection: + abort: + httpStatus: 500 + percentage: 100 + delay: + fixedDelay: 5.4s + percentage: 80 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.in.yaml new file mode 100644 index 0000000000..589e63b244 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.in.yaml @@ -0,0 +1,123 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + circuitBreaker: + maxConnections: -1 + - apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + circuitBreaker: + maxRequestsPerConnection: -1 + - apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + circuitBreaker: + maxParallelRetries: -1 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.out.yaml new file mode 100644 index 0000000000..4197b59611 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers-error.out.yaml @@ -0,0 +1,394 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + circuitBreaker: + maxRequestsPerConnection: -1 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: 'CircuitBreaker: invalid MaxRequestsPerConnection value -1' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route2 + namespace: default + spec: + circuitBreaker: + maxParallelRetries: -1 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: 'CircuitBreaker: invalid MaxParallelRetries value -1' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + circuitBreaker: + maxConnections: -1 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'CircuitBreaker: invalid MaxConnections value -1' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.in.yaml new file mode 100644 index 0000000000..c1d23a03a4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.in.yaml @@ -0,0 +1,99 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + circuitBreaker: + maxConnections: 2048 + maxPendingRequests: 1 + maxParallelRequests: 4294967295 + maxParallelRetries: 1024 + maxRequestsPerConnection: 1 +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + circuitBreaker: + maxConnections: 42 + maxPendingRequests: 42 + maxParallelRequests: 42 + maxParallelRetries: 24 + maxRequestsPerConnection: 42 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml new file mode 100644 index 0000000000..1529015780 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-circuitbreakers.out.yaml @@ -0,0 +1,328 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + circuitBreaker: + maxConnections: 42 + maxParallelRequests: 42 + maxParallelRetries: 24 + maxPendingRequests: 42 + maxRequestsPerConnection: 42 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + circuitBreaker: + maxConnections: 2048 + maxParallelRequests: 4294967295 + maxParallelRetries: 1024 + maxPendingRequests: 1 + maxRequestsPerConnection: 1 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + circuitBreaker: + maxConnections: 2048 + maxParallelRequests: 4294967295 + maxParallelRetries: 1024 + maxPendingRequests: 1 + maxRequestsPerConnection: 1 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + circuitBreaker: + maxConnections: 42 + maxParallelRequests: 42 + maxParallelRetries: 24 + maxPendingRequests: 42 + maxRequestsPerConnection: 42 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.in.yaml new file mode 100644 index 0000000000..3ba0127fac --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.in.yaml @@ -0,0 +1,239 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/v2" + backendRefs: + - name: service-2 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-3 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/v3" + backendRefs: + - name: service-3 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + healthCheck: + active: + timeout: "500ms" + interval: "3s" + unhealthyThreshold: 3 + healthyThreshold: 1 + type: HTTP + http: + path: "/healthz" + method: "GET" + expectedStatuses: + - 200 + - 300 + expectedResponse: + type: Binary + binary: RXZlcnl0aGluZyBPSw== + passive: + baseEjectionTime: 160s + interval: 2s + maxEjectionPercent: 100 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + healthCheck: + active: + timeout: "1s" + interval: "5s" + unhealthyThreshold: 3 + healthyThreshold: 3 + type: HTTP + http: + path: "/healthz" + method: "GET" + expectedStatuses: + - 200 + - 201 + expectedResponse: + type: Text + text: pong + passive: + baseEjectionTime: 150s + interval: 1s + maxEjectionPercent: 100 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + healthCheck: + active: + timeout: "1s" + interval: "5s" + unhealthyThreshold: 3 + healthyThreshold: 3 + type: TCP + tcp: + send: + type: Text + text: ping + receive: + type: Text + text: pong + passive: + baseEjectionTime: 10s + interval: 10s + maxEjectionPercent: 10 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-3 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-3 + namespace: default + healthCheck: + active: + timeout: 1s + interval: 3s + unhealthyThreshold: 3 + healthyThreshold: 1 + type: TCP + tcp: + send: + type: Binary + binary: cGluZw== + receive: + type: Binary + binary: RXZlcnl0aGluZyBPSw== + passive: + baseEjectionTime: 160s + interval: 8ms + maxEjectionPercent: 11 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.out.yaml new file mode 100644 index 0000000000..75d9cce14f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-healthcheck.out.yaml @@ -0,0 +1,648 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-1 + namespace: default + spec: + healthCheck: + active: + healthyThreshold: 3 + http: + expectedResponse: + text: pong + type: Text + expectedStatuses: + - 200 + - 201 + method: GET + path: /healthz + interval: 5s + timeout: 1s + type: HTTP + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m30s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 1s + maxEjectionPercent: 100 + splitExternalLocalOriginErrors: false + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-2 + namespace: default + spec: + healthCheck: + active: + healthyThreshold: 3 + interval: 5s + tcp: + receive: + text: pong + type: Text + send: + text: ping + type: Text + timeout: 1s + type: TCP + unhealthyThreshold: 3 + passive: + baseEjectionTime: 10s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 10s + maxEjectionPercent: 10 + splitExternalLocalOriginErrors: false + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-3 + namespace: default + spec: + healthCheck: + active: + healthyThreshold: 1 + interval: 3s + tcp: + receive: + binary: RXZlcnl0aGluZyBPSw== + type: Binary + send: + binary: cGluZw== + type: Binary + timeout: 1s + type: TCP + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m40s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 8ms + maxEjectionPercent: 11 + splitExternalLocalOriginErrors: false + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-3 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + healthCheck: + active: + healthyThreshold: 1 + http: + expectedResponse: + binary: RXZlcnl0aGluZyBPSw== + type: Binary + expectedStatuses: + - 200 + - 300 + method: GET + path: /healthz + interval: 3s + timeout: 500ms + type: HTTP + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m40s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 2s + maxEjectionPercent: 100 + splitExternalLocalOriginErrors: false + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 3 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /v2 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-3 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-3 + port: 8080 + matches: + - path: + value: /v3 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + healthCheck: + active: + healthyThreshold: 1 + http: + expectedResponse: + binary: RXZlcnl0aGluZyBPSw== + expectedStatuses: + - 200 + - 300 + host: '*' + method: GET + path: /healthz + interval: 3s + timeout: 500ms + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m40s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 2s + maxEjectionPercent: 100 + splitExternalLocalOriginErrors: false + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + healthCheck: + active: + healthyThreshold: 3 + interval: 5s + tcp: + receive: + text: pong + send: + text: ping + timeout: 1s + unhealthyThreshold: 3 + passive: + baseEjectionTime: 10s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 10s + maxEjectionPercent: 10 + splitExternalLocalOriginErrors: false + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /v2 + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + healthCheck: + active: + healthyThreshold: 1 + interval: 3s + tcp: + receive: + binary: RXZlcnl0aGluZyBPSw== + send: + binary: cGluZw== + timeout: 1s + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m40s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 8ms + maxEjectionPercent: 11 + splitExternalLocalOriginErrors: false + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-3/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /v3 + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + healthCheck: + active: + healthyThreshold: 3 + http: + expectedResponse: + text: pong + expectedStatuses: + - 200 + - 201 + host: apk.wso2.com + method: GET + path: /healthz + interval: 5s + timeout: 1s + unhealthyThreshold: 3 + passive: + baseEjectionTime: 2m30s + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + interval: 1s + maxEjectionPercent: 100 + splitExternalLocalOriginErrors: false + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml new file mode 100644 index 0000000000..5744e68130 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.in.yaml @@ -0,0 +1,142 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/test2" + backendRefs: + - name: service-2 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + loadBalancer: + type: Random +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + loadBalancer: + type: ConsistentHash + consistentHash: + type: SourceIP +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + loadBalancer: + type: RoundRobin + slowStart: + window: 300s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + loadBalancer: + type: LeastRequest + slowStart: + window: 300s diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml new file mode 100644 index 0000000000..4fbeddb3c8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-loadbalancer.out.yaml @@ -0,0 +1,443 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + loadBalancer: + consistentHash: + type: SourceIP + type: ConsistentHash + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route2 + namespace: default + spec: + loadBalancer: + slowStart: + window: 5m0s + type: LeastRequest + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + loadBalancer: + type: Random + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway2 + namespace: envoy-gateway + spec: + loadBalancer: + slowStart: + window: 5m0s + type: RoundRobin + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other backendTrafficPolicies + for these routes: [default/httproute-1 default/httproute-2]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /test2 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + loadBalancer: + random: {} + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + loadBalancer: + leastRequest: + slowStart: + window: 5m0s + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /test2 + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + loadBalancer: + consistentHash: + sourceIP: true + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.in.yaml new file mode 100644 index 0000000000..8b3e605761 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.in.yaml @@ -0,0 +1,71 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Local + local: + rules: # No Rule without selector is specified, so int32 max is used as default. + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml new file mode 100644 index 0000000000..82bec953e8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-default-route-level-limit.out.yaml @@ -0,0 +1,213 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + rateLimit: + local: + default: + requests: 4294967295 + unit: Second + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + - distinct: false + exact: foo + name: x-org-id + limit: + requests: 10 + unit: Hour + - cidrMatch: + cidr: 192.168.0.0/16 + distinct: false + ipv6: false + maskLen: 16 + headerMatches: + - distinct: false + exact: two + name: x-user-id + - distinct: false + exact: bar + name: x-org-id + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.in.yaml new file mode 100644 index 0000000000..9d2345eb22 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.in.yaml @@ -0,0 +1,74 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Local + local: + rules: + - limit: + requests: 1000 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute # Local rateLimit rule limit unit is not a multiple of the default limit unit. diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.out.yaml new file mode 100644 index 0000000000..4abdf1c81d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-limit-unit.out.yaml @@ -0,0 +1,186 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + local: + rules: + - limit: + requests: 1000 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'RateLimit: local rateLimit rule limit unit must be a multiple of + the default limit unit' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.in.yaml new file mode 100644 index 0000000000..4ce51ffbd1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.in.yaml @@ -0,0 +1,71 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Local + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.out.yaml new file mode 100644 index 0000000000..2e5bc0d3b9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-match-type.out.yaml @@ -0,0 +1,182 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'RateLimit: local rateLimit does not support distinct HeaderMatch' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.in.yaml new file mode 100644 index 0000000000..ac31643f8a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.in.yaml @@ -0,0 +1,77 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Local + local: + rules: + - limit: # There are two Rule without selector, so this is invalid. + requests: 10 + unit: Minute + - limit: + requests: 20 + unit: Minute + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.out.yaml new file mode 100644 index 0000000000..8b1a6ef2d1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit-invalid-multiple-route-level-limits.out.yaml @@ -0,0 +1,189 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + local: + rules: + - limit: + requests: 10 + unit: Minute + - limit: + requests: 20 + unit: Minute + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'RateLimit: local rateLimit can not have more than one rule without + clientSelectors' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.in.yaml new file mode 100644 index 0000000000..99a4608cc9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.in.yaml @@ -0,0 +1,74 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Local + local: + rules: + - limit: # The Rule without selector is used as default + requests: 10 + unit: Minute + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml new file mode 100644 index 0000000000..a5f92bcc25 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-local-ratelimit.out.yaml @@ -0,0 +1,216 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + local: + rules: + - limit: + requests: 10 + unit: Minute + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: foo + limit: + requests: 10 + unit: Hour + - clientSelectors: + - headers: + - name: x-user-id + value: two + - name: x-org-id + value: bar + sourceCIDR: + value: 192.168.0.0/16 + limit: + requests: 10 + unit: Minute + type: Local + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + rateLimit: + local: + default: + requests: 10 + unit: Minute + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + - distinct: false + exact: foo + name: x-org-id + limit: + requests: 10 + unit: Hour + - cidrMatch: + cidr: 192.168.0.0/16 + distinct: false + ipv6: false + maskLen: 16 + headerMatches: + - distinct: false + exact: two + name: x-user-id + - distinct: false + exact: bar + name: x-org-id + limit: + requests: 10 + unit: Minute diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml new file mode 100644 index 0000000000..582a4e05bc --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.in.yaml @@ -0,0 +1,91 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + proxyProtocol: + version: V1 +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + proxyProtocol: + version: V2 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml new file mode 100644 index 0000000000..f41eecc30f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-proxyprotocol.out.yaml @@ -0,0 +1,312 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + proxyProtocol: + version: V2 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + proxyProtocol: + version: V1 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + proxyProtocol: + version: V1 + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + proxyProtocol: + version: V2 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.in.yaml new file mode 100644 index 0000000000..18dfda14b4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.in.yaml @@ -0,0 +1,56 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: RegularExpression + value: "*.illegal.regex" + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.out.yaml new file mode 100644 index 0000000000..46444f14d9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit-invalid-regex.out.yaml @@ -0,0 +1,164 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: RegularExpression + value: '*.illegal.regex' + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'RateLimit: regex "*.illegal.regex" is invalid: error parsing regexp: + missing argument to repetition operator: `*`' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.in.yaml new file mode 100644 index 0000000000..1e075f595c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.in.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - sourceCIDR: + type: "Distinct" + value: 192.168.0.0/16 + limit: + requests: 20 + unit: Hour diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.out.yaml new file mode 100644 index 0000000000..1d185ce014 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-ratelimit.out.yaml @@ -0,0 +1,352 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + rateLimit: + global: + rules: + - clientSelectors: + - sourceCIDR: + type: Distinct + value: 192.168.0.0/16 + limit: + requests: 20 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + rateLimit: + global: + rules: + - headerMatches: + - distinct: false + exact: one + name: x-user-id + - distinct: true + name: x-org-id + limit: + requests: 10 + unit: Hour + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + rateLimit: + global: + rules: + - cidrMatch: + cidr: 192.168.0.0/16 + distinct: true + ipv6: false + maskLen: 16 + headerMatches: [] + limit: + requests: 20 + unit: Hour diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.in.yaml new file mode 100644 index 0000000000..b1554fcf37 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.in.yaml @@ -0,0 +1,110 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + retry: + retryOn: + triggers: + - cancelled + perRetry: + timeout: 250ms + backoff: + baseInterval: 100ms + maxInterval: 10s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + retry: + numRetries: 5 + retryOn: + httpStatusCodes: + - 429 + - 503 + triggers: + - connect-failure + - retriable-status-codes + perRetry: + timeout: 250ms + backoff: + baseInterval: 100ms + maxInterval: 10s diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.out.yaml new file mode 100644 index 0000000000..064910c07b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-retries.out.yaml @@ -0,0 +1,350 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + retry: + numRetries: 5 + perRetry: + backOff: + baseInterval: 100ms + maxInterval: 10s + timeout: 250ms + retryOn: + httpStatusCodes: + - 429 + - 503 + triggers: + - connect-failure + - retriable-status-codes + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + retry: + perRetry: + backOff: + baseInterval: 100ms + maxInterval: 10s + timeout: 250ms + retryOn: + triggers: + - cancelled + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + retry: + perRetry: + backOff: + baseInterval: 100ms + maxInterval: 10s + timeout: 250ms + retryOn: + triggers: + - cancelled + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + retry: + numRetries: 5 + perRetry: + backOff: + baseInterval: 100ms + maxInterval: 10s + timeout: 250ms + retryOn: + httpStatusCodes: + - 429 + - 503 + triggers: + - connect-failure + - retriable-status-codes diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.in.yaml new file mode 100644 index 0000000000..5bba0fd43c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.in.yaml @@ -0,0 +1,70 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-httproute-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + circuitBreaker: + maxConnections: 2048 + maxPendingRequests: 1 + maxParallelRequests: 4294967295 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml new file mode 100644 index 0000000000..22ebc5603a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml @@ -0,0 +1,224 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-httproute-1 + namespace: default + spec: + circuitBreaker: + maxConnections: 2048 + maxParallelRequests: 4294967295 + maxPendingRequests: 1 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + circuitBreaker: + maxConnections: 2048 + maxParallelRequests: 4294967295 + maxPendingRequests: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.in.yaml new file mode 100644 index 0000000000..4587b05186 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.in.yaml @@ -0,0 +1,95 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + tcpKeepalive: + probes: 3 + idleTime: 20m + interval: 60s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + tcpKeepalive: + probes: 6 + idleTime: 10s + interval: 30m diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml new file mode 100644 index 0000000000..4c90cb268d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-tcpkeepalive.out.yaml @@ -0,0 +1,320 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + tcpKeepalive: + idleTime: 10s + interval: 30m + probes: 6 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + tcpKeepalive: + idleTime: 20m + interval: 60s + probes: 3 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + tcpKeepalive: + idleTime: 1200 + interval: 60 + probes: 3 + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + tcpKeepalive: + idleTime: 10 + interval: 1800 + probes: 6 diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.in.yaml new file mode 100644 index 0000000000..62ff310c30 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.in.yaml @@ -0,0 +1,49 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22mib + tcp: + connectTimeout: 20s + diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.out.yaml new file mode 100644 index 0000000000..fa8ae48d64 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout-error.out.yaml @@ -0,0 +1,155 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22mib + tcp: + connectTimeout: 20s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'Timeout: invalid MaxConnectionDuration value 22mib' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.in.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.in.yaml new file mode 100644 index 0000000000..14abe58a78 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.in.yaml @@ -0,0 +1,99 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + tcp: + connectTimeout: 15s + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + tcp: + connectTimeout: 20s + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s diff --git a/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.out.yaml b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.out.yaml new file mode 100644 index 0000000000..508f965d8c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/backendtrafficpolicy-with-timeout.out.yaml @@ -0,0 +1,328 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s + tcp: + connectTimeout: 20s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + timeout: + http: + connectionIdleTimeout: 21s + maxConnectionDuration: 22s + tcp: + connectTimeout: 20s diff --git a/adapter/internal/operator/gateway-api/testdata/disable-accesslog.in.yaml b/adapter/internal/operator/gateway-api/testdata/disable-accesslog.in.yaml new file mode 100644 index 0000000000..0987579b23 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/disable-accesslog.in.yaml @@ -0,0 +1,73 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + telemetry: + accessLog: + disable: true + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/disable-accesslog.out.yaml b/adapter/internal/operator/gateway-api/testdata/disable-accesslog.out.yaml new file mode 100644 index 0000000000..61c561c068 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/disable-accesslog.out.yaml @@ -0,0 +1,130 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + disable: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.in.yaml new file mode 100644 index 0000000000..43b3279afc --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.in.yaml @@ -0,0 +1,35 @@ +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway-2 + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.out.yaml new file mode 100644 index 0000000000..4d89fe3087 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-cross-ns-target.out.yaml @@ -0,0 +1,83 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - name: edit-conn-buffer-bytes + namespace: envoy-gateway-2 + status: + conditions: + - lastTransitionTime: null + message: Namespace:envoy-gateway-2 TargetRef.Namespace:envoy-gateway, EnvoyPatchPolicy + can only target a Gateway in the same namespace. + reason: Invalid + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.in.yaml new file mode 100644 index 0000000000..bc81721c41 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.in.yaml @@ -0,0 +1,43 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: GatewayClass + name: envoy-gateway-class + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml new file mode 100644 index 0000000000..c66e108528 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml @@ -0,0 +1,92 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - name: edit-conn-buffer-bytes + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: EnvoyPatchPolicy is disabled in the EnvoyGateway configuration + reason: Disabled + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.in.yaml new file mode 100644 index 0000000000..7a96a3833a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.in.yaml @@ -0,0 +1,43 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml new file mode 100644 index 0000000000..994a00695b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml @@ -0,0 +1,94 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - name: edit-conn-buffer-bytes + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: TargetRef.Group:gateway.networking.k8s.io TargetRef.Kind:Gateway, + only TargetRef.Group:gateway.networking.k8s.io and TargetRef.Kind:GatewayClass + is supported. + reason: Invalid + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.in.yaml new file mode 100644 index 0000000000..d0f8d05308 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.in.yaml @@ -0,0 +1,35 @@ +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: MyGateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.out.yaml new file mode 100644 index 0000000000..84c1f20f3b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-invalid-target-kind.out.yaml @@ -0,0 +1,84 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - name: edit-conn-buffer-bytes + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: TargetRef.Group:gateway.networking.k8s.io TargetRef.Kind:MyGateway, + only TargetRef.Group:gateway.networking.k8s.io and TargetRef.Kind:Gateway + is supported. + reason: Invalid + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.in.yaml new file mode 100644 index 0000000000..f0cd4b9888 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.in.yaml @@ -0,0 +1,13 @@ +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: target-unknown-gateway + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: unknown-gateway + namespace: envoy-gateway diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml new file mode 100644 index 0000000000..fca40f1fe3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml @@ -0,0 +1,2 @@ +infraIR: {} +xdsIR: {} diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.in.yaml new file mode 100644 index 0000000000..303f4a233c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.in.yaml @@ -0,0 +1,64 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: GatewayClass + name: envoy-gateway-class + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-ignore-global-limit + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: GatewayClass + name: envoy-gateway-class + namespace: envoy-gateway + # Higher priority + priority: -1 + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/ignore_global_conn_limit" + value: "true" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml new file mode 100644 index 0000000000..ea6ec6609e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml @@ -0,0 +1,115 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /ignore_global_conn_limit + value: "true" + type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-ignore-global-limit + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /per_connection_buffer_limit_bytes + value: "1024" + type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-conn-buffer-bytes + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.in.yaml new file mode 100644 index 0000000000..4924ee7ac5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.in.yaml @@ -0,0 +1,56 @@ +envoyPatchPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +- apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-ignore-global-limit + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + # Higher priority + priority: -1 + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/ignore_global_conn_limit" + value: "true" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.out.yaml new file mode 100644 index 0000000000..bcd4a2d070 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoypatchpolicy-valid.out.yaml @@ -0,0 +1,105 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /ignore_global_conn_limit + value: "true" + type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-ignore-global-limit + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /per_connection_buffer_limit_bytes + value: "1024" + type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-conn-buffer-bytes + namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: null + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml new file mode 100644 index 0000000000..507bc3162b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + sinks: + - type: File + file: + path: /dev/stdout + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml new file mode 100644 index 0000000000..8fddd3e69c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json-no-format.out.yaml @@ -0,0 +1,137 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + type: JSON + sinks: + - file: + path: /dev/stdout + type: File + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: {} + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.in.yaml new file mode 100644 index 0000000000..f004ed31f2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.in.yaml @@ -0,0 +1,82 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: JSON + json: + protocol: "%PROTOCOL%" + duration: "%DURATION%" + sinks: + - type: File + file: + path: /dev/stdout + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.out.yaml new file mode 100644 index 0000000000..73009d6795 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-file-json.out.yaml @@ -0,0 +1,145 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + json: + duration: '%DURATION%' + protocol: '%PROTOCOL%' + type: JSON + sinks: + - file: + path: /dev/stdout + type: File + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - json: + duration: '%DURATION%' + protocol: '%PROTOCOL%' + path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml new file mode 100644 index 0000000000..6c0255abfa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.in.yaml @@ -0,0 +1,80 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + sinks: + - type: File + - type: OpenTelemetry + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml new file mode 100644 index 0000000000..b88c60c056 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog-with-bad-sinks.out.yaml @@ -0,0 +1,138 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + type: Text + sinks: + - type: File + - type: OpenTelemetry + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: {} + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.in.yaml new file mode 100644 index 0000000000..d9e516557b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.in.yaml @@ -0,0 +1,87 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + sinks: + - type: File + file: + path: /dev/stdout + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.out.yaml new file mode 100644 index 0000000000..353265e363 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-accesslog.out.yaml @@ -0,0 +1,156 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + type: Text + sinks: + - file: + path: /dev/stdout + type: File + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: cluster-1 + type: OpenTelemetry + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + openTelemetry: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: cluster-1 + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + text: + - format: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.in.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.in.yaml new file mode 100644 index 0000000000..24d8302c33 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.in.yaml @@ -0,0 +1,70 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-v1.29-latest" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.out.yaml b/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.out.yaml new file mode 100644 index 0000000000..4a69b9bc66 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/envoyproxy-valid.out.yaml @@ -0,0 +1,130 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-v1.29-latest + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml new file mode 100644 index 0000000000..9e910c6e9f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.in.yaml @@ -0,0 +1,50 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ExtensionRef + extensionRef: + group: foo.example.io + kind: Foo + name: test +extensionRefFilters: +- apiVersion: foo.example.io + kind: Foo + metadata: + name: test + namespace: default + spec: + data: "stuff" diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml new file mode 100644 index 0000000000..afd662552c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-extension-filter-invalid-group.out.yaml @@ -0,0 +1,120 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - extensionRef: + group: foo.example.io + kind: Foo + name: test + type: ExtensionRef + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Unable to translate APIVersion for Extension Filter: kind: Foo, + default/test' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: 'Unable to translate APIVersion for Extension Filter: kind: Foo, + default/test' + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml new file mode 100644 index 0000000000..410d26bfe0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.in.yaml @@ -0,0 +1,50 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ExtensionRef + extensionRef: + group: foo.example.io + kind: Foo + name: example +extensionRefFilters: +- apiVersion: foo.example.io/v1alpha1 + kind: Foo + metadata: + name: test + namespace: default + spec: + data: "stuff" diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml new file mode 100644 index 0000000000..eead7496d8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-non-matching-extension-filter.out.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - extensionRef: + group: foo.example.io + kind: Foo + name: example + type: ExtensionRef + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Reference default/example not found for filter type: Foo' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: 'Reference default/example not found for filter type: Foo' + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml new file mode 100644 index 0000000000..3021a18ae7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.in.yaml @@ -0,0 +1,50 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ExtensionRef + extensionRef: + group: foo.example.io + kind: Unsupported + name: test +extensionRefFilters: +- apiVersion: foo.example.io/v1alpha1 + kind: Unsupported + metadata: + name: test + namespace: default + spec: + data: "stuff" diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml new file mode 100644 index 0000000000..7de3b2833f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-unsupported-extension-filter.out.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - extensionRef: + group: foo.example.io + kind: Unsupported + name: test + type: ExtensionRef + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Invalid filter ExtensionRef: unknown kind foo.example.io/Unsupported' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.in.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.in.yaml new file mode 100644 index 0000000000..5bd190c9c2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.in.yaml @@ -0,0 +1,50 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ExtensionRef + extensionRef: + group: foo.example.io + kind: Foo + name: test +extensionRefFilters: +- apiVersion: foo.example.io/v1alpha1 + kind: Foo + metadata: + name: test + namespace: default + spec: + data: "stuff" diff --git a/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.out.yaml b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.out.yaml new file mode 100644 index 0000000000..b9dc22fc1c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/extensions/httproute-with-valid-extension-filter.out.yaml @@ -0,0 +1,147 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - extensionRef: + group: foo.example.io + kind: Foo + name: test + type: ExtensionRef + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + extensionRefs: + - object: + apiVersion: foo.example.io/v1alpha1 + kind: Foo + metadata: + name: test + namespace: default + spec: + data: stuff + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml new file mode 100644 index 0000000000..0dd2519dc9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.in.yaml @@ -0,0 +1,63 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + clusterIP: 1.1.1.1 + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml new file mode 100644 index 0000000000..e7427151b4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-allowed-httproute.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml new file mode 100644 index 0000000000..40c54eff3c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml new file mode 100644 index 0000000000..6a890eedfd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-allows-same-namespace-with-disallowed-httproute.out.yaml @@ -0,0 +1,107 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.in.yaml new file mode 100644 index 0000000000..fa6d645512 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.in.yaml @@ -0,0 +1,76 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + infrastructure: + labels: + infra-label1: infra-val1 + infra-label2: infra-val2 + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + listeners: + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + status: + listeners: + - name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated + - name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.out.yaml new file mode 100644 index 0000000000..5ea3b0e026 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-infrastructure.out.yaml @@ -0,0 +1,150 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + infrastructure: + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + labels: + infra-label1: infra-val1 + infra-label2: infra-val2 + listeners: + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/https + ports: + - containerPort: 10443 + name: https + protocol: HTTPS + servicePort: 443 + metadata: + annotations: + infra-anno-key1: infra-anno-val1 + infra-anno-key2: infra-anno-val2 + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + infra-label1: infra-val1 + infra-label2: infra-val2 + name: default/gateway-1 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/https + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: default/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.in.yaml new file mode 100644 index 0000000000..e4194dd153 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.in.yaml @@ -0,0 +1,19 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 80 + addresses: + - type: IPAddress + value: 1.2.3.4 + - type: IPAddress + value: 5.6.7.8 + - type: Hostname + value: foo.bar diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.out.yaml new file mode 100644 index 0000000000..3c0b792d56 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-addresses-with-ipaddress.out.yaml @@ -0,0 +1,67 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + addresses: + - type: IPAddress + value: 1.2.3.4 + - type: IPAddress + value: 5.6.7.8 + - type: Hostname + value: foo.bar + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + addresses: + - 1.2.3.4 + - 5.6.7.8 + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10080 + name: tcp + protocol: TCP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml new file mode 100644 index 0000000000..7c71b1eaa6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.in.yaml @@ -0,0 +1,38 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Selector + selector: + matchExpressions: + - key: foo + operator: Exists + values: + - bar +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml new file mode 100644 index 0000000000..9ba997089e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-namespaces-selector.out.yaml @@ -0,0 +1,92 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Selector + selector: + matchExpressions: + - key: foo + operator: Exists + values: + - bar + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: 'The allowedRoutes.namespaces.selector could not be parsed: values: + Invalid value: []string{"bar"}: values set must be empty for exists and + does not exist.' + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml new file mode 100644 index 0000000000..e31eab2696 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + kinds: + - group: foo.io + kind: HTTPRoute +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml new file mode 100644 index 0000000000..6869b02ad1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-group.out.yaml @@ -0,0 +1,83 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + kinds: + - group: foo.io + kind: HTTPRoute + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Group is not supported, group must be gateway.networking.k8s.io + reason: InvalidRouteKinds + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: http + supportedKinds: [] +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml new file mode 100644 index 0000000000..3c1d72a73f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.in.yaml @@ -0,0 +1,37 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + kinds: + - group: gateway.networking.k8s.io + kind: FooRoute + - group: + kind: HTTPRoute +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml new file mode 100644 index 0000000000..235dcf33c3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind-and-supported.out.yaml @@ -0,0 +1,85 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + kinds: + - group: gateway.networking.k8s.io + kind: FooRoute + - kind: HTTPRoute + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: FooRoute is not supported, kind must be one of [HTTPRoute] + reason: InvalidRouteKinds + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: http + supportedKinds: + - kind: HTTPRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml new file mode 100644 index 0000000000..dd3061ecf2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + kinds: + - group: gateway.networking.k8s.io + kind: FooRoute +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml new file mode 100644 index 0000000000..14f2414296 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-routes-kind.out.yaml @@ -0,0 +1,83 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + kinds: + - group: gateway.networking.k8s.io + kind: FooRoute + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: FooRoute is not supported, kind must be one of [HTTPRoute GRPCRoute] + reason: InvalidRouteKinds + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: http + supportedKinds: [] +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml new file mode 100644 index 0000000000..ee989f7fa0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + hostname: foo.com + protocol: TLS + port: 80 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All + kinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml new file mode 100644 index 0000000000..80ee8a1cfb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-allowed-tls-route-kind.out.yaml @@ -0,0 +1,83 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + kinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: All + hostname: foo.com + name: tls + port: 80 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: HTTPRoute is not supported, kind must be one of [TLSRoute] + reason: InvalidRouteKinds + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: [] +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml new file mode 100644 index 0000000000..be3ac0e7b8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.in.yaml @@ -0,0 +1,66 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-ecdsa-1 + - name: tls-secret-ecdsa-2 + - name: tls-secret-1 + +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-ecdsa-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnVENDQVNlZ0F3SUJBZ0lVRm1sOExCRzBvL1FLNFErWjdrODI0c0MyaUZ3d0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJMYVl2cUt1VlZveERvNTJlV3p2WUI1anc3RU1GODZybXlvaTVadWF5emRNdnBnNHpCcjgKUktCak5zK1QxakI4T0t1Y1MvN1JVRHgwcHorOTc2ek0zaU9qVXpCUk1CMEdBMVVkRGdRV0JCVE82K2NnMFIwZAp3dHJ6SlFQRzZnNzZoQkJVelRBZkJnTlZIU01FR0RBV2dCVE82K2NnMFIwZHd0cnpKUVBHNmc3NmhCQlV6VEFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFDMlhwUFFnUXpXYWUzYjVwWnQKR2N1TWZESjBjME9QS2NuZWdrWFoyQzRCM2dJZ1Uvc1Jrd0lwTFFOUlYrRWFZdzRQNVQ1Z1BFNlkrVnBtQzk4aApvVmpaL3pRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-ecdsa-2 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2VENDQVVTZ0F3SUJBZ0lVU2l2SkdURHdva1M3aGVZLzJjc1JzejR2SkIwd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCMk1CQUdCeXFHU000OUFnRUdCU3VCCkJBQWlBMklBQkZ2cnZSZWJhYVd1UzZNQUVJZDZ3WmZPS3Z1Q1R5VU1PbFpKcUZDRjlUa3pNWWw4Q2lvZnluT3QKQ3JzMHZ2YTlrZC9QMkpNR0JKcWdZZXZid290clJpMTJxTG5IMDQvam9HSWpqVE9LbzNJb2ZyK0ZrOHdMdkFlMwpPMVpLdFI5c3pxTlRNRkV3SFFZRFZSME9CQllFRklLczFRRm5vRHQ5K3Fva1I0T0RXYk16MWYrUE1COEdBMVVkCkl3UVlNQmFBRklLczFRRm5vRHQ5K3Fva1I0T0RXYk16MWYrUE1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0NnWUkKS29aSXpqMEVBd0lEWndBd1pBSXdIMzF0SHlmVVAwNFhIcGJXR2UxWjFJYUJaQndJdGg1NURVNGhqZlB1OG0rSgpXdjdiVzh6VFNnd0xpcW9yZmN1bkFqQTBnaE5KQkExWDJYdElLRG1sM3M3L1Z4OEZKY1MwNHZwQ2hoK2xBYkxTCnZlYWEyOFIzVExFWTNVK1FUWEkvd0lrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.key: YmFyCg== +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml new file mode 100644 index 0000000000..1c7ce3aa5e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-multiple-tls-configuration.out.yaml @@ -0,0 +1,98 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-ecdsa-1 + - group: null + kind: null + name: tls-secret-ecdsa-2 + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/tls-secret-ecdsa-2 public key algorithm must + be unique, matched certificate FQDN [foo.bar.com] has a conflicting algorithm + [ECDSA]. + reason: InvalidCertificateRef + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml new file mode 100644 index 0000000000..e0f69d01a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.in.yaml @@ -0,0 +1,47 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + hostname: foo.com + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Passthrough + certificateRefs: + - name: tls-secret-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: Zm9vCg== + tls.key: YmFyCg== +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml new file mode 100644 index 0000000000..d4cf640709 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-invalid-mode.out.yaml @@ -0,0 +1,91 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: TLS Passthrough mode is not supported, TLS mode must be Terminate. + reason: UnsupportedTLSMode + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml new file mode 100644 index 0000000000..54feff7469 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.in.yaml @@ -0,0 +1,44 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: Zm9vCg== + tls.key: YmFyCg== +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml new file mode 100644 index 0000000000..a6291fdcdd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-certificate-refs.out.yaml @@ -0,0 +1,86 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have at least 1 TLS certificate ref + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml new file mode 100644 index 0000000000..0c3a5060b8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.in.yaml @@ -0,0 +1,47 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + hostname: example.com + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml new file mode 100644 index 0000000000..ec5a8b858e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-no-valid-certificate-for-fqdn.out.yaml @@ -0,0 +1,93 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: example.com + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/tls-secret-1 must contain valid tls.crt and + tls.key, hostname example.com does not match Common Name or DNS Names in + the certificate tls.crt. + reason: InvalidCertificateRef + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml new file mode 100644 index 0000000000..95bffc38bb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.in.yaml @@ -0,0 +1,36 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml new file mode 100644 index 0000000000..358feaa325 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-does-not-exist.out.yaml @@ -0,0 +1,90 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/tls-secret-1 does not exist. + reason: InvalidCertificateRef + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml new file mode 100644 index 0000000000..caab5079f9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.in.yaml @@ -0,0 +1,47 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + namespace: default +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: Zm9vCg== + tls.key: YmFyCg== +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml new file mode 100644 index 0000000000..0cd7c47e50 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-in-other-namespace.out.yaml @@ -0,0 +1,92 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + namespace: default + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Certificate ref to secret default/tls-secret-1 not permitted by any + ReferenceGrant. + reason: RefNotPermitted + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml new file mode 100644 index 0000000000..75e6051a21 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.in.yaml @@ -0,0 +1,45 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.key: YmFyCg== +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml new file mode 100644 index 0000000000..8bfcf8097a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-invalid-tls-configuration-secret-is-not-valid.out.yaml @@ -0,0 +1,90 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/tls-secret-1 must contain tls.crt and tls.key. + reason: InvalidCertificateRef + status: "False" + type: ResolvedRefs + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml new file mode 100644 index 0000000000..f24ab41fe4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Selector +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml new file mode 100644 index 0000000000..86b2c8cb22 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-missing-allowed-namespaces-selector.out.yaml @@ -0,0 +1,85 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Selector + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: The allowedRoutes.namespaces.selector field must be specified when + allowedRoutes.namespaces.from is set to "Selector". + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml new file mode 100644 index 0000000000..f027c23e67 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.in.yaml @@ -0,0 +1,16 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + hostname: foo.com + protocol: TCP + port: 80 + allowedRoutes: + namespaces: + from: All diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml new file mode 100644 index 0000000000..4e6bc7a181 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcp-with-hostname.out.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Listener must not have hostname set when protocol is TCP. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml new file mode 100644 index 0000000000..1cc29af9d3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 162 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8081 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml new file mode 100644 index 0000000000..62e423b7f6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml @@ -0,0 +1,92 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 162 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10162 + name: tcp + protocol: TCP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8081 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: TCP Port 8081 not found on service default/service-1 + reason: PortNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml new file mode 100644 index 0000000000..f0291a5316 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.in.yaml @@ -0,0 +1,33 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 80 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - name: service-2 + port: 8080 + weight: 50 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml new file mode 100644 index 0000000000..df0712fb8f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-backends.out.yaml @@ -0,0 +1,96 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10080 + name: tcp + protocol: TCP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - name: service-2 + port: 8080 + weight: 50 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: One and only one backend is supported + reason: InvalidBackend + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml new file mode 100644 index 0000000000..179769001d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.in.yaml @@ -0,0 +1,34 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 80 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - backendRefs: + - name: service-2 + port: 8080 + weight: 50 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml new file mode 100644 index 0000000000..5f95991a28 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tcproute-with-multiple-rules.out.yaml @@ -0,0 +1,97 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10080 + name: tcp + protocol: TCP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - backendRefs: + - name: service-2 + port: 8080 + weight: 50 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: One and only one rule is supported + reason: InvalidRule + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml new file mode 100644 index 0000000000..2882a44c18 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.in.yaml @@ -0,0 +1,61 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + namespace: default +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: default + name: referencegrant-1 + spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: envoy-gateway + to: + - group: "" + kind: Secret diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml new file mode 100644 index 0000000000..26dedcf510 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-secret-in-other-namespace-allowed-by-refgrant.out.yaml @@ -0,0 +1,139 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + namespace: default + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10443 + name: tls + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: default/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml new file mode 100644 index 0000000000..09c8d21373 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.in.yaml @@ -0,0 +1,70 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls-passthrough + protocol: TLS + port: 90 + hostname: foo.bar.com + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All + - name: tls-terminate + protocol: HTTPS + port: 443 + hostname: foo.bar.com + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + allowedRoutes: + namespaces: + from: All +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-2 + port: 8080 +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml new file mode 100644 index 0000000000..1cfa15a3c4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-tls-terminate-and-passthrough.out.yaml @@ -0,0 +1,225 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.bar.com + name: tls-passthrough + port: 90 + protocol: TLS + tls: + mode: Passthrough + - allowedRoutes: + namespaces: + from: All + hostname: foo.bar.com + name: tls-terminate + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls-passthrough + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls-terminate + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls-passthrough + ports: + - containerPort: 10090 + name: tls-passthrough + protocol: TLS + servicePort: 90 + - address: null + name: envoy-gateway/gateway-1/tls-terminate + ports: + - containerPort: 10443 + name: tls-terminate + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/tls-terminate + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo_bar_com + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls-passthrough/tlsroute-1 + port: 10090 + tls: + passthrough: + snis: + - foo.bar.com diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml new file mode 100644 index 0000000000..f8e9d3859d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.in.yaml @@ -0,0 +1,16 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + hostname: foo.com + protocol: UDP + port: 80 + allowedRoutes: + namespaces: + from: All diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml new file mode 100644 index 0000000000..cf80aeec7e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udp-with-hostname.out.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Listener must not have hostname set when protocol is UDP. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml new file mode 100644 index 0000000000..440e8c9fd5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml new file mode 100644 index 0000000000..71c6547418 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml @@ -0,0 +1,92 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp + port: 162 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10162 + name: udp + protocol: UDP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: UDP Port 8080 not found on service default/service-1 + reason: PortNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml new file mode 100644 index 0000000000..6dfe97fba5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.in.yaml @@ -0,0 +1,33 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + protocol: UDP + port: 80 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - name: service-2 + port: 8080 + weight: 50 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml new file mode 100644 index 0000000000..7ee99d0345 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-backends.out.yaml @@ -0,0 +1,96 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10080 + name: udp + protocol: UDP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - name: service-2 + port: 8080 + weight: 50 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: One and only one backend is supported + reason: InvalidBackend + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml new file mode 100644 index 0000000000..ecb2d33e09 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.in.yaml @@ -0,0 +1,34 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + protocol: UDP + port: 80 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - backendRefs: + - name: service-2 + port: 8080 + weight: 50 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml new file mode 100644 index 0000000000..619a17b0e3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-udproute-with-multiple-rules.out.yaml @@ -0,0 +1,97 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10080 + name: udp + protocol: UDP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 50 + - backendRefs: + - name: service-2 + port: 8080 + weight: 50 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: One and only one rule is supported + reason: InvalidRule + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml new file mode 100644 index 0000000000..bba9524935 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 80 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml new file mode 100644 index 0000000000..d5205682ca --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-tcproute.out.yaml @@ -0,0 +1,60 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10080 + name: tcp + protocol: TCP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml new file mode 100644 index 0000000000..2f7fa01b58 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + protocol: UDP + port: 80 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml new file mode 100644 index 0000000000..2c5574c694 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unmatched-udproute.out.yaml @@ -0,0 +1,60 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10080 + name: udp + protocol: UDP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml new file mode 100644 index 0000000000..12598d5c29 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: unsupported + protocol: GRPC + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml new file mode 100644 index 0000000000..bc6bca89e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-unsupported-protocol.out.yaml @@ -0,0 +1,85 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: unsupported + port: 80 + protocol: GRPC + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Protocol GRPC is unsupported, must be HTTP, HTTPS, TCP or UDP. + reason: UnsupportedProtocol + status: "False" + type: Accepted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: unsupported + supportedKinds: null +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners included by this parent ref allowed this attachment. + reason: NotAllowedByListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml new file mode 100644 index 0000000000..8274366417 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.in.yaml @@ -0,0 +1,56 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-ecdsa-1 + - name: tls-secret-ecdsa-2 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-ecdsa-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnVENDQVNlZ0F3SUJBZ0lVRm1sOExCRzBvL1FLNFErWjdrODI0c0MyaUZ3d0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJMYVl2cUt1VlZveERvNTJlV3p2WUI1anc3RU1GODZybXlvaTVadWF5emRNdnBnNHpCcjgKUktCak5zK1QxakI4T0t1Y1MvN1JVRHgwcHorOTc2ek0zaU9qVXpCUk1CMEdBMVVkRGdRV0JCVE82K2NnMFIwZAp3dHJ6SlFQRzZnNzZoQkJVelRBZkJnTlZIU01FR0RBV2dCVE82K2NnMFIwZHd0cnpKUVBHNmc3NmhCQlV6VEFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFDMlhwUFFnUXpXYWUzYjVwWnQKR2N1TWZESjBjME9QS2NuZWdrWFoyQzRCM2dJZ1Uvc1Jrd0lwTFFOUlYrRWFZdzRQNVQ1Z1BFNlkrVnBtQzk4aApvVmpaL3pRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-ecdsa-2 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZakNCNlFJSkFNcDhYTGNsWGJ2NU1Bb0dDQ3FHU000OUJBTUNNQnN4R1RBWEJnTlZCQU1NRUhSbGMzUXUKWlhoaGJYQnNaUzVqYjIwd0hoY05Nak13TlRJMU1UUXhNRFF5V2hjTk1qUXdOVEkwTVRReE1EUXlXakFiTVJrdwpGd1lEVlFRRERCQjBaWE4wTG1WNFlXMXdiR1V1WTI5dE1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFCmVNdEhDM2hJZXEyUXNka3RTaS9aTlIvTUtOYXZTbTNITm43dEdvZ2ZxYXFuOEFTZ0hJYTd5VVUrK1Bzb0RENmsKZisrQ2U3dXNkMG1RTzFTbmRZSVdqMlFMaUl6ME5aSDhCL1FyNGk3SjBJS3dzUWxVbnVuWnF2NUtZOVRMUWEwbgpNQW9HQ0NxR1NNNDlCQU1DQTJnQU1HVUNNUUNHVGpQa2hqZE1KcWUrVjFGRWpteUk2andEV0FDalhucFRuaXhVCnM2dUNKZjVNNUw1TmpGYmkydGplMGlES0UxVUNNQXdxSjZmOUs2bUhUd2JxVGkzTVNFMmQxODl6aUcyWVZCaUQKYmVXcHc0UjF5ZTdHRFJvVm9veG9ob2lERjdsNm13PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1JR2tBZ0VCQkRDUUE5VWo0SkR5c0Q0MlJIMGI2cjU5NTlXTmlXU2ZKZlMxK2RvTjk0TzZCUGdaQUJiUTI4eTIKUTZsM3pZdklLeFNnQndZRks0RUVBQ0toWkFOaUFBUjR5MGNMZUVoNnJaQ3gyUzFLTDlrMUg4d28xcTlLYmNjMgpmdTBhaUIrcHFxZndCS0FjaHJ2SlJUNzQreWdNUHFSLzc0Sjd1NngzU1pBN1ZLZDFnaGFQWkF1SWpQUTFrZndICjlDdmlMc25RZ3JDeENWU2U2ZG1xL2twajFNdEJyU2M9Ci0tLS0tRU5EIEVDIFBSSVZBVEUgS0VZLS0tLS0K +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml new file mode 100644 index 0000000000..c5972d5f6d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration-with-same-algorithm-different-fqdn.out.yaml @@ -0,0 +1,144 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-ecdsa-1 + - group: null + kind: null + name: tls-secret-ecdsa-2 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10443 + name: tls + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway/tls-secret-ecdsa-1 + privateKey: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnVENDQVNlZ0F3SUJBZ0lVRm1sOExCRzBvL1FLNFErWjdrODI0c0MyaUZ3d0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJMYVl2cUt1VlZveERvNTJlV3p2WUI1anc3RU1GODZybXlvaTVadWF5emRNdnBnNHpCcjgKUktCak5zK1QxakI4T0t1Y1MvN1JVRHgwcHorOTc2ek0zaU9qVXpCUk1CMEdBMVVkRGdRV0JCVE82K2NnMFIwZAp3dHJ6SlFQRzZnNzZoQkJVelRBZkJnTlZIU01FR0RBV2dCVE82K2NnMFIwZHd0cnpKUVBHNmc3NmhCQlV6VEFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFDMlhwUFFnUXpXYWUzYjVwWnQKR2N1TWZESjBjME9QS2NuZWdrWFoyQzRCM2dJZ1Uvc1Jrd0lwTFFOUlYrRWFZdzRQNVQ1Z1BFNlkrVnBtQzk4aApvVmpaL3pRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + - name: envoy-gateway/tls-secret-ecdsa-2 + privateKey: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1JR2tBZ0VCQkRDUUE5VWo0SkR5c0Q0MlJIMGI2cjU5NTlXTmlXU2ZKZlMxK2RvTjk0TzZCUGdaQUJiUTI4eTIKUTZsM3pZdklLeFNnQndZRks0RUVBQ0toWkFOaUFBUjR5MGNMZUVoNnJaQ3gyUzFLTDlrMUg4d28xcTlLYmNjMgpmdTBhaUIrcHFxZndCS0FjaHJ2SlJUNzQreWdNUHFSLzc0Sjd1NngzU1pBN1ZLZDFnaGFQWkF1SWpQUTFrZndICjlDdmlMc25RZ3JDeENWU2U2ZG1xL2twajFNdEJyU2M9Ci0tLS0tRU5EIEVDIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZakNCNlFJSkFNcDhYTGNsWGJ2NU1Bb0dDQ3FHU000OUJBTUNNQnN4R1RBWEJnTlZCQU1NRUhSbGMzUXUKWlhoaGJYQnNaUzVqYjIwd0hoY05Nak13TlRJMU1UUXhNRFF5V2hjTk1qUXdOVEkwTVRReE1EUXlXakFiTVJrdwpGd1lEVlFRRERCQjBaWE4wTG1WNFlXMXdiR1V1WTI5dE1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFCmVNdEhDM2hJZXEyUXNka3RTaS9aTlIvTUtOYXZTbTNITm43dEdvZ2ZxYXFuOEFTZ0hJYTd5VVUrK1Bzb0RENmsKZisrQ2U3dXNkMG1RTzFTbmRZSVdqMlFMaUl6ME5aSDhCL1FyNGk3SjBJS3dzUWxVbnVuWnF2NUtZOVRMUWEwbgpNQW9HQ0NxR1NNNDlCQU1DQTJnQU1HVUNNUUNHVGpQa2hqZE1KcWUrVjFGRWpteUk2andEV0FDalhucFRuaXhVCnM2dUNKZjVNNUw1TmpGYmkydGplMGlES0UxVUNNQXdxSjZmOUs2bUhUd2JxVGkzTVNFMmQxODl6aUcyWVZCaUQKYmVXcHc0UjF5ZTdHRFJvVm9veG9ob2lERjdsNm13PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml new file mode 100644 index 0000000000..58abae4129 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.in.yaml @@ -0,0 +1,56 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + - name: tls-secret-ecdsa-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-ecdsa-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnVENDQVNlZ0F3SUJBZ0lVRm1sOExCRzBvL1FLNFErWjdrODI0c0MyaUZ3d0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJMYVl2cUt1VlZveERvNTJlV3p2WUI1anc3RU1GODZybXlvaTVadWF5emRNdnBnNHpCcjgKUktCak5zK1QxakI4T0t1Y1MvN1JVRHgwcHorOTc2ek0zaU9qVXpCUk1CMEdBMVVkRGdRV0JCVE82K2NnMFIwZAp3dHJ6SlFQRzZnNzZoQkJVelRBZkJnTlZIU01FR0RBV2dCVE82K2NnMFIwZHd0cnpKUVBHNmc3NmhCQlV6VEFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFDMlhwUFFnUXpXYWUzYjVwWnQKR2N1TWZESjBjME9QS2NuZWdrWFoyQzRCM2dJZ1Uvc1Jrd0lwTFFOUlYrRWFZdzRQNVQ1Z1BFNlkrVnBtQzk4aApvVmpaL3pRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml new file mode 100644 index 0000000000..9b1b705209 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-multiple-tls-configuration.out.yaml @@ -0,0 +1,144 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + - group: null + kind: null + name: tls-secret-ecdsa-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10443 + name: tls + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + - name: envoy-gateway/tls-secret-ecdsa-1 + privateKey: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxEbnZNM1RKM3NHYm9EeTF4T3dqSVppVFNWeWZXVWF5YVExcWdrdUdacEtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSDVWdHJjenJQS091alV5RTMyaDU2UnVrdHUzSVhTVnJJMkNibXh5UUpqcEY3di9rNVNqTQpSVXZjUnBCdmpnQWROaGhUNGNUMXV4YW1TMFlmQ2JXMVhRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnVENDQVNlZ0F3SUJBZ0lVRm1sOExCRzBvL1FLNFErWjdrODI0c0MyaUZ3d0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFptOXZMbUpoY2k1amIyMHdIaGNOTWpRd01qSTVNRGt6TURFd1doY05NelF3TWpJMgpNRGt6TURFd1dqQVdNUlF3RWdZRFZRUUREQXRtYjI4dVltRnlMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJMYVl2cUt1VlZveERvNTJlV3p2WUI1anc3RU1GODZybXlvaTVadWF5emRNdnBnNHpCcjgKUktCak5zK1QxakI4T0t1Y1MvN1JVRHgwcHorOTc2ek0zaU9qVXpCUk1CMEdBMVVkRGdRV0JCVE82K2NnMFIwZAp3dHJ6SlFQRzZnNzZoQkJVelRBZkJnTlZIU01FR0RBV2dCVE82K2NnMFIwZHd0cnpKUVBHNmc3NmhCQlV6VEFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFDMlhwUFFnUXpXYWUzYjVwWnQKR2N1TWZESjBjME9QS2NuZWdrWFoyQzRCM2dJZ1Uvc1Jrd0lwTFFOUlYrRWFZdzRQNVQ1Z1BFNlkrVnBtQzk4aApvVmpaL3pRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml new file mode 100644 index 0000000000..6949425c35 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.in.yaml @@ -0,0 +1,46 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml new file mode 100644 index 0000000000..fbf4cd0da0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-listener-with-valid-tls-configuration.out.yaml @@ -0,0 +1,138 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10443 + name: tls + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.in.yaml new file mode 100644 index 0000000000..da4aeea8d7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.in.yaml @@ -0,0 +1,55 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + status: + listeners: + - name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: default + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - parentRef: + namespace: default + name: gateway-1 + controllerName: apk.wso2.com/gatewayclass-controller + conditions: + - type: Accepted + status: "True" + reason: Accepted + message: Route is accepted diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.out.yaml new file mode 100644 index 0000000000..e6c5dec57d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-preexisting-status-condition.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml new file mode 100644 index 0000000000..27fc4622a0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.in.yaml @@ -0,0 +1,42 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp + protocol: TCP + port: 162 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8163 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-2 + port: 8163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml new file mode 100644 index 0000000000..5f12b5c6c6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-tcproutes.out.yaml @@ -0,0 +1,137 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 162 + protocol: TCP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10162 + name: tcp + protocol: TCP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Multiple routes on the same TCP listener + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp/tcproute-1 + port: 10162 + tls: {} diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml new file mode 100644 index 0000000000..0d8be29055 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.in.yaml @@ -0,0 +1,42 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8162 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-2 + port: 8162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml new file mode 100644 index 0000000000..90932e5faf --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-single-listener-with-multiple-udproutes.out.yaml @@ -0,0 +1,136 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp + port: 162 + protocol: UDP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10162 + name: udp + protocol: UDP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Multiple routes on the same UDP listener + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + udp: + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp/udproute-1 + port: 10162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.in.yaml new file mode 100644 index 0000000000..f40fbbe830 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.in.yaml @@ -0,0 +1,69 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 + status: + listeners: + - name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated + - name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + attachedRoutes: 1 + conditions: + - type: Programmed + status: "True" + reason: Programmed + message: Listener has been successfully translated +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.out.yaml new file mode 100644 index 0000000000..10ae0979ef --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-stale-status-condition.out.yaml @@ -0,0 +1,138 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/https + ports: + - containerPort: 10443 + name: https + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/https + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: default/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml new file mode 100644 index 0000000000..049c797413 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp1 + protocol: TCP + port: 162 + allowedRoutes: + namespaces: + from: All + - name: tcp2 + protocol: TLS + port: 162 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml new file mode 100644 index 0000000000..ca1cdd348d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-tcp-or-tls-port.out.yaml @@ -0,0 +1,135 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp1 + port: 162 + protocol: TCP + - allowedRoutes: + namespaces: + from: All + name: tcp2 + port: 162 + protocol: TLS + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Only one TCP/TLS listener is allowed in a given port + reason: ProtocolConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener must have TLS set when protocol is TLS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp1 + ports: + - containerPort: 10162 + name: tcp1 + protocol: TCP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp1/tcproute-1 + port: 10162 + tls: {} diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml new file mode 100644 index 0000000000..50601282fc --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp1 + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All + - name: udp2 + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml new file mode 100644 index 0000000000..fbb6bdc8c0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-on-same-udp-port.out.yaml @@ -0,0 +1,132 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp1 + port: 162 + protocol: UDP + - allowedRoutes: + namespaces: + from: All + name: udp2 + port: 162 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Only one UDP listener is allowed in a given port + reason: ProtocolConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp1 + ports: + - containerPort: 10162 + name: udp1 + protocol: UDP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + udp: + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp1/udproute-1 + port: 10162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml new file mode 100644 index 0000000000..90fe76523f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.in.yaml @@ -0,0 +1,56 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: tls-1 + protocol: TLS + tls: + mode: Passthrough + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml new file mode 100644 index 0000000000..20716b62a9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-http-and-tlsroute-same-hostname-and-port.out.yaml @@ -0,0 +1,152 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tls-1 + port: 80 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a unique hostname + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a unique hostname + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml new file mode 100644 index 0000000000..d0cadfda02 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.in.yaml @@ -0,0 +1,56 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 81 + hostname: bar.com + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/test" + backendRefs: + - name: service-2 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml new file mode 100644 index 0000000000..51077a7f7d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-multiple-httproutes.out.yaml @@ -0,0 +1,266 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 81 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /test + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-1/http-2 + ports: + - containerPort: 10081 + name: http-2 + protocol: HTTP + servicePort: 81 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/foo_com + pathMatch: + distinct: false + name: "" + prefix: /test + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10081 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: /test + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml new file mode 100644 index 0000000000..69b93f3b1a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.in.yaml @@ -0,0 +1,40 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml new file mode 100644 index 0000000000..c620ac15ed --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-hostname.out.yaml @@ -0,0 +1,120 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-2 + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a unique hostname + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a unique hostname + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml new file mode 100644 index 0000000000..37c0a342f3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.in.yaml @@ -0,0 +1,40 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTPS + port: 80 + hostname: bar.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml new file mode 100644 index 0000000000..0052f3f6f0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-and-incompatible-protocol.out.yaml @@ -0,0 +1,120 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 80 + protocol: HTTPS + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a compatible protocol + reason: ProtocolConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: All listeners for a given port must use a compatible protocol + reason: ProtocolConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml new file mode 100644 index 0000000000..34bd7a43e6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.in.yaml @@ -0,0 +1,53 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*" + allowedRoutes: + namespaces: + from: All + - name: tcp + protocol: TCP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml new file mode 100644 index 0000000000..19401c8218 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml @@ -0,0 +1,208 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*' + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: tcp + port: 80 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-1/tcp + ports: + - containerPort: 10080 + name: tcp + protocol: TCP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp/tcproute-1 + port: 10080 + tls: {} diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml new file mode 100644 index 0000000000..d4ef5be9e3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.in.yaml @@ -0,0 +1,53 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*" + allowedRoutes: + namespaces: + from: All + - name: udp + protocol: UDP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml new file mode 100644 index 0000000000..9d48e0cddb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-same-port-http-udp-protocol.out.yaml @@ -0,0 +1,207 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*' + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-1/udp + ports: + - containerPort: 10080 + name: udp + protocol: UDP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + udp: + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp/udproute-1 + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml new file mode 100644 index 0000000000..a4bb69bb76 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.in.yaml @@ -0,0 +1,50 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp1 + protocol: TCP + port: 162 + allowedRoutes: + namespaces: + from: All + - name: tcp2 + protocol: TCP + port: 163 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: tcp1 + rules: + - backendRefs: + - name: service-1 + port: 8163 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: tcp2 + rules: + - backendRefs: + - name: service-2 + port: 8163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml new file mode 100644 index 0000000000..5771128610 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-with-sectionname.out.yaml @@ -0,0 +1,188 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp1 + port: 162 + protocol: TCP + - allowedRoutes: + namespaces: + from: All + name: tcp2 + port: 163 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp1 + ports: + - containerPort: 10162 + name: tcp1 + protocol: TCP + servicePort: 162 + - address: null + name: envoy-gateway/gateway-1/tcp2 + ports: + - containerPort: 10163 + name: tcp2 + protocol: TCP + servicePort: 163 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: tcp1 + rules: + - backendRefs: + - name: service-1 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: tcp1 +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: tcp2 + rules: + - backendRefs: + - name: service-2 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: tcp2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp1/tcproute-1 + port: 10162 + tls: {} + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp2/tcproute-2 + port: 10163 + tls: {} diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml new file mode 100644 index 0000000000..1ba01ba6cd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.in.yaml @@ -0,0 +1,48 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tcp1 + protocol: TCP + port: 161 + allowedRoutes: + namespaces: + from: All + - name: tcp2 + protocol: TCP + port: 162 + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8163 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-2 + port: 8163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml new file mode 100644 index 0000000000..9c3c7ba545 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-tcproutes-without-sectionname.out.yaml @@ -0,0 +1,184 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tcp1 + port: 161 + protocol: TCP + - allowedRoutes: + namespaces: + from: All + name: tcp2 + port: 162 + protocol: TCP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tcp1 + ports: + - containerPort: 10161 + name: tcp1 + protocol: TCP + servicePort: 161 + - address: null + name: envoy-gateway/gateway-1/tcp2 + ports: + - containerPort: 10162 + name: tcp2 + protocol: TCP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8163 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Multiple routes on the same TCP listener + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp1/tcproute-1 + port: 10161 + tls: {} + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8163 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tcp2/tcproute-1 + port: 10162 + tls: {} diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml new file mode 100644 index 0000000000..dc3a42ab70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.in.yaml @@ -0,0 +1,50 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp1 + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All + - name: udp2 + protocol: UDP + port: 163 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: udp1 + rules: + - backendRefs: + - name: service-1 + port: 8162 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: udp2 + rules: + - backendRefs: + - name: service-2 + port: 8162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml new file mode 100644 index 0000000000..b0e951037c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-with-sectionname.out.yaml @@ -0,0 +1,186 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp1 + port: 162 + protocol: UDP + - allowedRoutes: + namespaces: + from: All + name: udp2 + port: 163 + protocol: UDP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp1 + ports: + - containerPort: 10162 + name: udp1 + protocol: UDP + servicePort: 162 + - address: null + name: envoy-gateway/gateway-1/udp2 + ports: + - containerPort: 10163 + name: udp2 + protocol: UDP + servicePort: 163 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: udp1 + rules: + - backendRefs: + - name: service-1 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: udp1 +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: udp2 + rules: + - backendRefs: + - name: service-2 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: udp2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + udp: + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp1/udproute-1 + port: 10162 + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp2/udproute-2 + port: 10163 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml new file mode 100644 index 0000000000..2638f0f19d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.in.yaml @@ -0,0 +1,48 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: udp1 + protocol: UDP + port: 161 + allowedRoutes: + namespaces: + from: All + - name: udp2 + protocol: UDP + port: 162 + allowedRoutes: + namespaces: + from: All +udpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8162 + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + namespace: default + name: udproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-2 + port: 8162 diff --git a/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml new file mode 100644 index 0000000000..999c2e7164 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/gateway-with-two-listeners-with-udproutes-without-sectionname.out.yaml @@ -0,0 +1,182 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: udp1 + port: 161 + protocol: UDP + - allowedRoutes: + namespaces: + from: All + name: udp2 + port: 162 + protocol: UDP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/udp1 + ports: + - containerPort: 10161 + name: udp1 + protocol: UDP + servicePort: 161 + - address: null + name: envoy-gateway/gateway-1/udp2 + ports: + - containerPort: 10162 + name: udp2 + protocol: UDP + servicePort: 162 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +udpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: UDPRoute + metadata: + creationTimestamp: null + name: udproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8162 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Multiple routes on the same UDP listener + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + udp: + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp1/udproute-1 + port: 10161 + - address: 0.0.0.0 + destination: + name: udproute/default/udproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8162 + protocol: UDP + weight: 1 + name: envoy-gateway/gateway-1/udp2/udproute-1 + port: 10162 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.in.yaml new file mode 100644 index 0000000000..69bfd7a1f2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.in.yaml @@ -0,0 +1,31 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - method: + service: com.ExampleExact + type: Exact diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.out.yaml new file mode 100644 index 0000000000..df188f66a2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-empty-backends.out.yaml @@ -0,0 +1,120 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - matches: + - method: + service: com.ExampleExact + type: Exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /com.ExampleExact diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.in.yaml new file mode 100644 index 0000000000..ae2baa8dca --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.in.yaml @@ -0,0 +1,35 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - headers: + - type: Exact + name: magic + value: foo + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.out.yaml new file mode 100644 index 0000000000..33d91c16f0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-header-match.out.yaml @@ -0,0 +1,131 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: magic + type: Exact + value: foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + headerMatches: + - distinct: false + exact: foo + name: magic + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/0/* diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.in.yaml new file mode 100644 index 0000000000..82e319fd26 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.in.yaml @@ -0,0 +1,39 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - method: + method: "Example" + service: "com.example" + type: Exact + - method: + method: "Foobar" + service: "foo.*" + type: RegularExpression + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.out.yaml new file mode 100644 index 0000000000..79a8f24a63 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-and-service-match.out.yaml @@ -0,0 +1,154 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - method: + method: Example + service: com.example + type: Exact + - method: + method: Foobar + service: foo.* + type: RegularExpression + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /com.example/Example + name: "" + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/1/* + pathMatch: + distinct: false + name: "" + safeRegex: /foo.*/Foobar diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.in.yaml new file mode 100644 index 0000000000..e2a374d149 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.in.yaml @@ -0,0 +1,37 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - method: + method: ExampleExact + type: Exact + - method: + method: FooBar[0-9]+ + type: RegularExpression + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.out.yaml new file mode 100644 index 0000000000..e63d54ce4c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-method-match.out.yaml @@ -0,0 +1,152 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - method: + method: ExampleExact + type: Exact + - method: + method: FooBar[0-9]+ + type: RegularExpression + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/1/* + pathMatch: + distinct: false + name: "" + safeRegex: /(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*/FooBar[0-9]+ + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + headerMatches: + - distinct: false + name: :path + suffix: /ExampleExact + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/0/* diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.in.yaml new file mode 100644 index 0000000000..2c48dad582 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.in.yaml @@ -0,0 +1,36 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - filters: + - type: "RequestHeaderModifier" + requestHeaderModifier: + add: + - name: "my-header" + value: "foo" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.out.yaml new file mode 100644 index 0000000000..6bc3ca36f2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-request-header-modifier.out.yaml @@ -0,0 +1,132 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: my-header + value: foo + type: RequestHeaderModifier + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addRequestHeaders: + - append: true + name: my-header + value: foo + backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.in.yaml new file mode 100644 index 0000000000..ad245cb182 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.in.yaml @@ -0,0 +1,37 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - method: + service: com.ExampleExact + type: Exact + - method: + service: com.[A-Z]+ + type: RegularExpression + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.out.yaml new file mode 100644 index 0000000000..0c7c7c7435 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/grpcroute-with-service-match.out.yaml @@ -0,0 +1,152 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - method: + service: com.ExampleExact + type: Exact + - method: + service: com.[A-Z]+ + type: RegularExpression + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/1/* + pathMatch: + distinct: false + name: "" + safeRegex: /com.[A-Z]+/[A-Za-z_][A-Za-z_0-9]* + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /com.ExampleExact diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.in.yaml new file mode 100644 index 0000000000..3d3adbd5a9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.in.yaml @@ -0,0 +1,54 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + request: 1s + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + tcp: + connectTimeout: 20kib + http: + maxConnectionDuration: 22s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml new file mode 100644 index 0000000000..7573ae6229 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml @@ -0,0 +1,169 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + http: + maxConnectionDuration: 22s + tcp: + connectTimeout: 20kib + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: 'Timeout: invalid ConnectTimeout value 20kib' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + request: 1s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + timeout: + http: + requestTimeout: 1s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.in.yaml new file mode 100644 index 0000000000..d399d2f6e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.in.yaml @@ -0,0 +1,100 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + request: 1s + backendRefs: + - name: service-1 + port: 8080 +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + tcp: + connectTimeout: 15s + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + tcp: + connectTimeout: 20s + http: + maxConnectionDuration: 22s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml new file mode 100644 index 0000000000..82870b6b5b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml @@ -0,0 +1,329 @@ +backendTrafficPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + timeout: + http: + maxConnectionDuration: 22s + tcp: + connectTimeout: 20s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + request: 1s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + timeout: + http: + connectionIdleTimeout: 16s + maxConnectionDuration: 17s + tcp: + connectTimeout: 15s + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + timeout: + http: + maxConnectionDuration: 22s + requestTimeout: 1s + tcp: + connectTimeout: 20s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml new file mode 100644 index 0000000000..d44d02d168 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.in.yaml @@ -0,0 +1,82 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 81 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 82 + hostname: bar.com + allowedRoutes: + namespaces: + from: All + - name: http-3 + protocol: HTTP + port: 83 + hostname: foo1.com + allowedRoutes: + namespaces: + from: All + - name: http-4 + protocol: HTTP + port: 84 + hostname: bar1.com + allowedRoutes: + namespaces: + from: All + - name: http-5 + protocol: HTTP + port: 85 + hostname: foo2.com + allowedRoutes: + namespaces: + from: All + - name: http-6 + protocol: HTTP + port: 86 + hostname: bar2.com + allowedRoutes: + namespaces: + from: All + - name: http-7 + protocol: HTTP + port: 87 + hostname: foo3.com + allowedRoutes: + namespaces: + from: All + - name: http-8 + protocol: HTTP + port: 88 + hostname: bar3.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml new file mode 100644 index 0000000000..e7aaca9004 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-different-listeners.out.yaml @@ -0,0 +1,590 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 81 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 82 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo1.com + name: http-3 + port: 83 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar1.com + name: http-4 + port: 84 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo2.com + name: http-5 + port: 85 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar2.com + name: http-6 + port: 86 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo3.com + name: http-7 + port: 87 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar3.com + name: http-8 + port: 88 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-4 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-5 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-6 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-7 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-8 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10081 + name: http-1 + protocol: HTTP + servicePort: 81 + - address: null + name: envoy-gateway/gateway-1/http-2 + ports: + - containerPort: 10082 + name: http-2 + protocol: HTTP + servicePort: 82 + - address: null + name: envoy-gateway/gateway-1/http-3 + ports: + - containerPort: 10083 + name: http-3 + protocol: HTTP + servicePort: 83 + - address: null + name: envoy-gateway/gateway-1/http-4 + ports: + - containerPort: 10084 + name: http-4 + protocol: HTTP + servicePort: 84 + - address: null + name: envoy-gateway/gateway-1/http-5 + ports: + - containerPort: 10085 + name: http-5 + protocol: HTTP + servicePort: 85 + - address: null + name: envoy-gateway/gateway-1/http-6 + ports: + - containerPort: 10086 + name: http-6 + protocol: HTTP + servicePort: 86 + - address: null + name: envoy-gateway/gateway-1/http-7 + ports: + - containerPort: 10087 + name: http-7 + protocol: HTTP + servicePort: 87 + - address: null + name: envoy-gateway/gateway-1/http-8 + ports: + - containerPort: 10088 + name: http-8 + protocol: HTTP + servicePort: 88 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10081 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10082 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo1.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-3 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10083 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo1.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo1_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar1.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-4 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10084 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar1.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar1_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo2.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-5 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10085 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo2_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar2.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-6 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10086 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar2_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo3.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-7 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10087 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo3.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo3_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar3.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-8 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10088 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar3.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar3_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml new file mode 100644 index 0000000000..d2078deb76 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.in.yaml @@ -0,0 +1,82 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 80 + hostname: bar.com + allowedRoutes: + namespaces: + from: All + - name: http-3 + protocol: HTTP + port: 80 + hostname: foo1.com + allowedRoutes: + namespaces: + from: All + - name: http-4 + protocol: HTTP + port: 80 + hostname: bar1.com + allowedRoutes: + namespaces: + from: All + - name: http-5 + protocol: HTTP + port: 80 + hostname: foo2.com + allowedRoutes: + namespaces: + from: All + - name: http-6 + protocol: HTTP + port: 80 + hostname: bar2.com + allowedRoutes: + namespaces: + from: All + - name: http-7 + protocol: HTTP + port: 80 + hostname: foo3.com + allowedRoutes: + namespaces: + from: All + - name: http-8 + protocol: HTTP + port: 80 + hostname: bar3.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml new file mode 100644 index 0000000000..5f127b4a2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-more-listeners.out.yaml @@ -0,0 +1,541 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo1.com + name: http-3 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar1.com + name: http-4 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo2.com + name: http-5 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar2.com + name: http-6 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: foo3.com + name: http-7 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar3.com + name: http-8 + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-4 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-5 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-6 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-7 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-8 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo1.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-3 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo1.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo1_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar1.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-4 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar1.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar1_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo2.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-5 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo2_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar2.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-6 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar2_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - foo3.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-7 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo3.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo3_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar3.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-8 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar3.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar3_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml new file mode 100644 index 0000000000..2a5c0e34b5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.in.yaml @@ -0,0 +1,52 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - name: tls + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: tls-secret-1 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml new file mode 100644 index 0000000000..3825b8ac8b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners-with-different-ports.out.yaml @@ -0,0 +1,203 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + name: tls + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10443 + name: tls + protocol: HTTPS + servicePort: 443 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10443 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml new file mode 100644 index 0000000000..8b11c8da7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.in.yaml @@ -0,0 +1,40 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 80 + hostname: bar.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml new file mode 100644 index 0000000000..b419de26e2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway-with-two-listeners.out.yaml @@ -0,0 +1,187 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/foo_com + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.in.yaml new file mode 100644 index 0000000000..2946dfeb10 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.out.yaml new file mode 100644 index 0000000000..691ad86910 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-gateway.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.in.yaml new file mode 100644 index 0000000000..ec02f88815 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.in.yaml @@ -0,0 +1,34 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + port: 80 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.out.yaml new file mode 100644 index 0000000000..e7b2a70533 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-matching-port.out.yaml @@ -0,0 +1,131 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + port: 80 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + port: 80 + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml new file mode 100644 index 0000000000..bc9aba4dd2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.in.yaml @@ -0,0 +1,41 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All + - name: http-2 + protocol: HTTP + port: 80 + hostname: bar.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http-2 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml new file mode 100644 index 0000000000..6b6523e552 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-on-gateway-with-two-listeners.out.yaml @@ -0,0 +1,169 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: bar.com + name: http-2 + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http-2 + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http-2 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + - address: 0.0.0.0 + hostnames: + - bar.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: bar.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/bar_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml new file mode 100644 index 0000000000..0c00de607f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.in.yaml @@ -0,0 +1,99 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-2 + spec: + ips: + - 8.8.8.8 + ports: + - port: 8081 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-2 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml new file mode 100644 index 0000000000..87823b3a94 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml @@ -0,0 +1,141 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Mixed endpointslice address type between backendRefs is not supported + reason: ResolvedRefs + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 1.2.3.4 + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml new file mode 100644 index 0000000000..8ad5fa1290 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.in.yaml @@ -0,0 +1,99 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-2 + spec: + ips: + - 8.8.8.8 + ports: + - port: 8081 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-2 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "bar.foo" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml new file mode 100644 index 0000000000..5fe7303ccf --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-same-address-type.out.yaml @@ -0,0 +1,141 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-2 + port: 8081 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + - addressType: FQDN + endpoints: + - host: bar.foo + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml new file mode 100644 index 0000000000..7fa56a4ae0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.in.yaml @@ -0,0 +1,66 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml new file mode 100644 index 0000000000..d243d7e8d9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-fqdn-address-type.out.yaml @@ -0,0 +1,131 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: FQDN + endpoints: + - host: foo.bar + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml new file mode 100644 index 0000000000..c58ed15d3f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.in.yaml @@ -0,0 +1,83 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "1.2.3.4" + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-2 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: FQDN + ports: + - name: http + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "foo.bar" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml new file mode 100644 index 0000000000..690b29467e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml @@ -0,0 +1,133 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Mixed endpointslice address type for the same backendRef is not supported + reason: ResolvedRefs + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: Mixed + endpoints: + - host: 1.2.3.4 + port: 8080 + - host: foo.bar + port: 8081 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml new file mode 100644 index 0000000000..26a816b93c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.in.yaml @@ -0,0 +1,66 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: default + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: default + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml new file mode 100644 index 0000000000..6188955f50 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener-with-serviceimport-backendref.out.yaml @@ -0,0 +1,131 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.in.yaml new file mode 100644 index 0000000000..6766b18f54 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.in.yaml @@ -0,0 +1,33 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.out.yaml new file mode 100644 index 0000000000..d4796986ce --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-attaching-to-listener.out.yaml @@ -0,0 +1,129 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.in.yaml new file mode 100644 index 0000000000..f16077de32 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + backendRequest: 1s + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.out.yaml new file mode 100644 index 0000000000..8d3b1b91e4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-backend-request-timeout.out.yaml @@ -0,0 +1,134 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + backendRequest: 1s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + timeout: + http: + requestTimeout: 1s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml new file mode 100644 index 0000000000..0060019963 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.in.yaml @@ -0,0 +1,34 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + hostname: foo.com + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + port: 81 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml new file mode 100644 index 0000000000..776f3dc5c7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-not-attaching-to-listener-non-matching-port.out.yaml @@ -0,0 +1,110 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: http-1 + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + port: 81 + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: No listeners match this parent ref + reason: NoMatchingParent + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + port: 81 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http-1 + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - foo.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.in.yaml new file mode 100644 index 0000000000..6cf2210aa3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + timeouts: + request: 5s + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.out.yaml new file mode 100644 index 0000000000..6318ac4122 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-request-timeout.out.yaml @@ -0,0 +1,134 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + request: 5s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + timeout: + http: + requestTimeout: 5s diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.in.yaml new file mode 100644 index 0000000000..ba4acbbc75 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.out.yaml new file mode 100644 index 0000000000..6ab06d6c22 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-empty-backends-and-no-filters.out.yaml @@ -0,0 +1,117 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml new file mode 100644 index 0000000000..2e9cddb580 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.in.yaml @@ -0,0 +1,36 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + - name: service-2 + port: 8080 + - name: service-3 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml new file mode 100644 index 0000000000..6f1fc51017 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-no-weights.out.yaml @@ -0,0 +1,143 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + - name: service-2 + port: 8080 + - name: service-3 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml new file mode 100644 index 0000000000..e3419de8d9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.in.yaml @@ -0,0 +1,42 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + weight: 1 + - name: service-2 + port: 8080 + weight: 2 + - name: service-3 + port: 8080 + weight: 3 + - name: service-4 + port: 8080 + weight: 0 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml new file mode 100644 index 0000000000..cb32420be4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-rule-with-multiple-backends-and-weights.out.yaml @@ -0,0 +1,149 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + weight: 1 + - name: service-2 + port: 8080 + weight: 2 + - name: service-3 + port: 8080 + weight: 3 + - name: service-4 + port: 8080 + weight: 0 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 2 + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 3 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml new file mode 100644 index 0000000000..c1d5723e74 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml @@ -0,0 +1,79 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + namespace: backends + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: backends + name: service-1 + spec: + clusterIP: 1.1.1.1 + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: backends + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: backends + name: referencegrant-1 + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: default + to: + - group: "" + kind: Service diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml new file mode 100644 index 0000000000..faf1be2da0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml @@ -0,0 +1,129 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml new file mode 100644 index 0000000000..e445c56366 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.in.yaml @@ -0,0 +1,81 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + namespace: backends + port: 8080 +serviceImports: + - apiVersion: multicluster.x-k8s.io/v1alpha1 + kind: ServiceImport + metadata: + namespace: backends + name: service-import-1 + spec: + ips: + - 7.7.7.7 + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: service-import-1 + namespace: backends + labels: + multicluster.kubernetes.io/service-name: service-import-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: backends + name: referencegrant-1 + spec: + from: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: default + to: + - group: multicluster.x-k8s.io + kind: ServiceImport diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml new file mode 100644 index 0000000000..5eed2bb32e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-backendref-serviceimport-in-other-namespace-allowed-by-refgrant.out.yaml @@ -0,0 +1,131 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-1 + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.in.yaml new file mode 100644 index 0000000000..14755bccba --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.in.yaml @@ -0,0 +1,31 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.out.yaml new file mode 100644 index 0000000000..bcf7af32cd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-empty-matches.out.yaml @@ -0,0 +1,122 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/-1/* diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml new file mode 100644 index 0000000000..d98ef69056 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.in.yaml @@ -0,0 +1,52 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + add: + - name: "add-header-1" + value: "some-value" + - name: "add-header-2" + value: "some-value" + - type: RequestHeaderModifier + requestHeaderModifier: + add: + - name: "add-header-1" + value: "some-value" + - name: "add-header-3" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml new file mode 100644 index 0000000000..b9e0b9be4f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-add-multiple-filters.out.yaml @@ -0,0 +1,157 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: add-header-1 + value: some-value + - name: add-header-2 + value: some-value + type: RequestHeaderModifier + - requestHeaderModifier: + add: + - name: add-header-1 + value: some-value + - name: add-header-3 + value: some-value + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addRequestHeaders: + - append: true + name: add-header-1 + value: some-value + - append: true + name: add-header-2 + value: some-value + - append: true + name: add-header-3 + value: some-value + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.in.yaml new file mode 100644 index 0000000000..ccae4c43ad --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.in.yaml @@ -0,0 +1,61 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: "set-header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-4" + value: "some-value" + - name: "set-header-4" + value: "some-value" + add: + - name: "Set-Header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-5" + value: "some-value" + - name: "set-header-5" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.out.yaml new file mode 100644 index 0000000000..69c6f158cb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-adds.out.yaml @@ -0,0 +1,173 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: Set-Header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-5 + value: some-value + - name: set-header-5 + value: some-value + set: + - name: set-header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-4 + value: some-value + - name: set-header-4 + value: some-value + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addRequestHeaders: + - append: true + name: Set-Header-1 + value: some-value + - append: true + name: set-header-2 + value: some-value + - append: true + name: set-header-3 + value: some-value + - append: true + name: set-header-5 + value: some-value + - append: false + name: set-header-4 + value: some-value + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml new file mode 100644 index 0000000000..9f142279ae --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + remove: + - "rem-header-1" + - "rem-header-2" + - type: RequestHeaderModifier + requestHeaderModifier: + remove: + - "rem-header-1" + - "rem-header-3" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml new file mode 100644 index 0000000000..8e57d7c083 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-remove-multiple-filters.out.yaml @@ -0,0 +1,147 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + remove: + - rem-header-1 + - rem-header-2 + type: RequestHeaderModifier + - requestHeaderModifier: + remove: + - rem-header-1 + - rem-header-3 + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeRequestHeaders: + - rem-header-1 + - rem-header-2 + - rem-header-3 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.in.yaml new file mode 100644 index 0000000000..347d58dca7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + remove: + - "some-header-1" + - "some-header-1" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.out.yaml new file mode 100644 index 0000000000..62bc219c03 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-duplicate-removes.out.yaml @@ -0,0 +1,140 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + remove: + - some-header-1 + - some-header-1 + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeRequestHeaders: + - some-header-1 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.in.yaml new file mode 100644 index 0000000000..5c81038611 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.in.yaml @@ -0,0 +1,46 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: "example-header-1" + value: "" + add: + - name: "example-header-2" + value: "" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.out.yaml new file mode 100644 index 0000000000..600bf0fa83 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-header-values.out.yaml @@ -0,0 +1,148 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: example-header-2 + value: "" + set: + - name: example-header-1 + value: "" + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addRequestHeaders: + - append: true + name: example-header-2 + value: "" + - append: false + name: example-header-1 + value: "" + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.in.yaml new file mode 100644 index 0000000000..b6659fa32d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: "" + value: "some-value" + add: + - name: "" + value: "some-value" + - name: "good-header" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.out.yaml new file mode 100644 index 0000000000..fc890c1f43 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-empty-headers.out.yaml @@ -0,0 +1,123 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: "" + value: some-value + - name: good-header + value: some-value + set: + - name: "" + value: some-value + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: RequestHeaderModifier Filter cannot set a header with an empty name + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.in.yaml new file mode 100644 index 0000000000..cec0b77021 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: "example:1" + value: "some-value" + - name: "good-header" + value: "some-value" + add: + - name: "example/2" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.out.yaml new file mode 100644 index 0000000000..a224d94400 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-invalid-headers.out.yaml @@ -0,0 +1,124 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: example/2 + value: some-value + set: + - name: example:1 + value: some-value + - name: good-header + value: some-value + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'RequestHeaderModifier Filter cannot set headers with a ''/'' or + '':'' character in them. Header: ''example:1''' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.in.yaml new file mode 100644 index 0000000000..c7b7cc8a8c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + add: + set: + remove: diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.out.yaml new file mode 100644 index 0000000000..8da34b9aa6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-headers.out.yaml @@ -0,0 +1,135 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: {} + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.in.yaml new file mode 100644 index 0000000000..982138ca27 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + add: + - name: "bad:header" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.out.yaml new file mode 100644 index 0000000000..26599e6ce0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-no-valid-headers.out.yaml @@ -0,0 +1,119 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: bad:header + value: some-value + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: RequestHeaderModifier Filter did not provide valid configuration + to add/set/remove any headers + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.in.yaml new file mode 100644 index 0000000000..48d62f8d08 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + remove: + - "example-header-1" + - "example-header-2" + - "example-header-3" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.out.yaml new file mode 100644 index 0000000000..800dbf826a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-header-filter-remove.out.yaml @@ -0,0 +1,143 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + remove: + - example-header-1 + - example-header-2 + - example-header-3 + type: RequestHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeRequestHeaders: + - example-header-1 + - example-header-2 + - example-header-3 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml new file mode 100644 index 0000000000..85d2eabc26 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + port: 8888 +services: +- apiVersion: v1 + kind: Service + metadata: + name: service-1 + spec: + clusterIP: 7.7.7.7 + ports: + - port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml new file mode 100644 index 0000000000..8f1ea3e607 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml @@ -0,0 +1,121 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8888 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: TCP Port 8888 not found on service default/service-1 + reason: PortNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml new file mode 100644 index 0000000000..66905669ee --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.in.yaml @@ -0,0 +1,45 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + group: "invalid" + namespace: backends + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: backends + name: service-1 + spec: + clusterIP: 7.7.7.7 + ports: + - port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml new file mode 100644 index 0000000000..59dc0c3dd7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml @@ -0,0 +1,125 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: invalid + name: service-1 + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Group is invalid, only the core API group (specified by omitting + the group field or setting it to an empty string) and multicluster.x-k8s.io + are supported + reason: InvalidKind + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml new file mode 100644 index 0000000000..8914c502c5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + kind: "URL" + port: 8080 + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml new file mode 100644 index 0000000000..0009e0dc33 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml @@ -0,0 +1,122 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - kind: URL + name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Kind is invalid, only Service and MCS ServiceImport are supported + reason: InvalidKind + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml new file mode 100644 index 0000000000..90664bbd47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.in.yaml @@ -0,0 +1,41 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 +services: + - apiVersion: v1 + kind: Service + metadata: + name: service-1 + spec: + clusterIP: 7.7.7.7 + ports: + - port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml new file mode 100644 index 0000000000..1a5fdd9e7c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml @@ -0,0 +1,121 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: A valid port number corresponding to a port on the Service must be + specified + reason: PortNotSpecified + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml new file mode 100644 index 0000000000..35593cde20 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-that-doesnt-exist + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml new file mode 100644 index 0000000000..0fdd262541 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml @@ -0,0 +1,123 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: multicluster.x-k8s.io + kind: ServiceImport + name: service-import-that-doesnt-exist + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: ServiceImport default/service-import-that-doesnt-exist not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml new file mode 100644 index 0000000000..d56d23d0c9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.in.yaml @@ -0,0 +1,33 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-that-doesnt-exist + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml new file mode 100644 index 0000000000..73fbe7166e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml @@ -0,0 +1,121 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-that-doesnt-exist + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service default/service-that-doesnt-exist not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.in.yaml new file mode 100644 index 0000000000..236a10736d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.in.yaml @@ -0,0 +1,39 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "set-header-1" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml new file mode 100644 index 0000000000..30cdf96733 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - filters: + - responseHeaderModifier: + set: + - name: set-header-1 + value: some-value + type: ResponseHeaderModifier + name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: The filters field within BackendRef is not supported + reason: UnsupportedRefValue + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml new file mode 100644 index 0000000000..833ca4ad07 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.in.yaml @@ -0,0 +1,44 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + namespace: backends + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: backends + name: service-1 + spec: + clusterIP: 7.7.7.7 + ports: + - port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml new file mode 100644 index 0000000000..b74619520f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml @@ -0,0 +1,122 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + namespace: backends + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Backend ref to Service backends/service-1 not permitted by any ReferenceGrant. + reason: RefNotPermitted + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.in.yaml new file mode 100644 index 0000000000..1b114bdc58 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.in.yaml @@ -0,0 +1,48 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 81 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + + rules: + - matches: + - path: + type: RegularExpression + value: "*.foo.bar.com" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.out.yaml new file mode 100644 index 0000000000..a71a4cee66 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-invalid-regex.out.yaml @@ -0,0 +1,173 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 81 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + type: RegularExpression + value: '*.foo.bar.com' + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Regex "*.foo.bar.com" is invalid: error parsing regexp: missing + argument to repetition operator: `*`' + reason: UnsupportedValue + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10081 + name: http + protocol: HTTP + servicePort: 81 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10081 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.in.yaml new file mode 100644 index 0000000000..9b21f53cec --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.in.yaml @@ -0,0 +1,50 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.out.yaml new file mode 100644 index 0000000000..ad7c1b8327 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-duplicates.out.yaml @@ -0,0 +1,162 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + type: RequestMirror + - requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + type: RequestMirror + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + mirrors: + - name: httproute/default/httproute-1/rule/0-mirror-0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - name: httproute/default/httproute-1/rule/0-mirror-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.in.yaml new file mode 100644 index 0000000000..0e7fe4865a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.in.yaml @@ -0,0 +1,62 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Header-Set + value: set-overwrites-values + add: + - name: X-Header-Add + value: header-val-1 + - name: X-Header-Add-Append + value: header-val-2 + remove: + - X-Header-Remove + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: mirror-service + port: 8080 + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.out.yaml new file mode 100644 index 0000000000..9c1575888e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-multiple.out.yaml @@ -0,0 +1,186 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestHeaderModifier: + add: + - name: X-Header-Add + value: header-val-1 + - name: X-Header-Add-Append + value: header-val-2 + remove: + - X-Header-Remove + set: + - name: X-Header-Set + value: set-overwrites-values + type: RequestHeaderModifier + - requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + type: RequestMirror + - requestMirror: + backendRef: + kind: Service + name: mirror-service + port: 8080 + type: RequestMirror + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addRequestHeaders: + - append: true + name: X-Header-Add + value: header-val-1 + - append: true + name: X-Header-Add-Append + value: header-val-2 + - append: false + name: X-Header-Set + value: set-overwrites-values + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + mirrors: + - name: httproute/default/httproute-1/rule/0-mirror-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + - name: httproute/default/httproute-1/rule/0-mirror-2 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeRequestHeaders: + - X-Header-Remove diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.in.yaml new file mode 100644 index 0000000000..3c5204ca24 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-1 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.out.yaml new file mode 100644 index 0000000000..8eaec664be --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -0,0 +1,139 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestMirror: + backendRef: + kind: Service + name: service-1 + type: RequestMirror + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: A valid port number corresponding to a port on the Service must be + specified + reason: PortNotSpecified + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.in.yaml new file mode 100644 index 0000000000..bd9b63a40a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-unknown + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.out.yaml new file mode 100644 index 0000000000..0255c22dc1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter-service-not-found.out.yaml @@ -0,0 +1,139 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestMirror: + backendRef: + kind: Service + name: service-unknown + port: 8080 + type: RequestMirror + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service default/service-unknown not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.in.yaml new file mode 100644 index 0000000000..526e5c01c0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: RequestMirror + requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.out.yaml new file mode 100644 index 0000000000..139d70fcab --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-mirror-filter.out.yaml @@ -0,0 +1,148 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - requestMirror: + backendRef: + kind: Service + name: service-1 + port: 8080 + type: RequestMirror + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + mirrors: + - name: httproute/default/httproute-1/rule/0-mirror-0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml new file mode 100644 index 0000000000..51dd2e3d2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - whales.kubernetes.io + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml new file mode 100644 index 0000000000..0f9e160ff7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-non-matching-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - whales.kubernetes.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: There were no hostname intersections between the HTTPRoute and this + parent ref's Listener(s). + reason: NoMatchingListenerHostname + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml new file mode 100644 index 0000000000..679d88528e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.in.yaml @@ -0,0 +1,41 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + path: + type: ReplaceFullPath + replaceFullPath: /redirected diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml new file mode 100644 index 0000000000..52c8fddab8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml @@ -0,0 +1,136 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - filters: + - requestRedirect: + path: + replaceFullPath: /redirected + type: ReplaceFullPath + scheme: https + statusCode: 301 + type: RequestRedirect + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + redirect: + hostname: null + path: + fullReplace: /redirected + prefixMatchReplace: null + port: 443 + scheme: https + statusCode: 301 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.in.yaml new file mode 100644 index 0000000000..ab244b058f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.in.yaml @@ -0,0 +1,39 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + hostname: "redirected.com" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.out.yaml new file mode 100644 index 0000000000..95c55f2d1f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-hostname.out.yaml @@ -0,0 +1,132 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - filters: + - requestRedirect: + hostname: redirected.com + scheme: https + statusCode: 301 + type: RequestRedirect + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + redirect: + hostname: redirected.com + path: null + port: 443 + scheme: https + statusCode: 301 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml new file mode 100644 index 0000000000..09583b2b69 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ExtensionRef + extensionRef: + group: unsupported.group.io + kind: UnsupportedKind + name: unsupported + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml new file mode 100644 index 0000000000..ae17d799a2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-filter-type.out.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - extensionRef: + group: unsupported.group.io + kind: UnsupportedKind + name: unsupported + type: ExtensionRef + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Invalid filter ExtensionRef: unknown kind unsupported.group.io/UnsupportedKind' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml new file mode 100644 index 0000000000..00fe8b6f79 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.in.yaml @@ -0,0 +1,38 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + filters: + - type: RequestRedirect + requestRedirect: + scheme: unknown + statusCode: 301 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml new file mode 100644 index 0000000000..9836a529fa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-scheme.out.yaml @@ -0,0 +1,115 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - filters: + - requestRedirect: + scheme: unknown + statusCode: 301 + type: RequestRedirect + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Scheme: unknown is unsupported, only ''https'' and ''http'' are + supported' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.in.yaml new file mode 100644 index 0000000000..134a1b3f97 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.in.yaml @@ -0,0 +1,39 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 666 + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.out.yaml new file mode 100644 index 0000000000..66948bda27 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-invalid-status.out.yaml @@ -0,0 +1,114 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - filters: + - requestRedirect: + scheme: https + statusCode: 666 + type: RequestRedirect + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Status code 666 is invalid, only 302 and 301 are supported + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml new file mode 100644 index 0000000000..593e057f7c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + filters: + - type: RequestRedirect + requestRedirect: + scheme: http + statusCode: 302 + port: 8080 + path: + type: ReplacePrefixMatch + replacePrefixMatch: /redirected diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml new file mode 100644 index 0000000000..abbfe08b64 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml @@ -0,0 +1,137 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - filters: + - requestRedirect: + path: + replacePrefixMatch: /redirected + type: ReplacePrefixMatch + port: 8080 + scheme: http + statusCode: 302 + type: RequestRedirect + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + redirect: + hostname: null + path: + fullReplace: null + prefixMatchReplace: /redirected + port: 8080 + scheme: http + statusCode: 302 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.in.yaml new file mode 100644 index 0000000000..73d50049d8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.in.yaml @@ -0,0 +1,57 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "set-header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-4" + value: "some-value" + add: + - name: "Set-Header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-5" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.out.yaml new file mode 100644 index 0000000000..99721e5b02 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-adds.out.yaml @@ -0,0 +1,169 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: Set-Header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-5 + value: some-value + set: + - name: set-header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-4 + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addResponseHeaders: + - append: true + name: Set-Header-1 + value: some-value + - append: true + name: set-header-2 + value: some-value + - append: true + name: set-header-3 + value: some-value + - append: true + name: set-header-5 + value: some-value + - append: false + name: set-header-4 + value: some-value + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml new file mode 100644 index 0000000000..d0d7618f7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.in.yaml @@ -0,0 +1,52 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: "add-header-1" + value: "some-value" + - name: "add-header-2" + value: "some-value" + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: "add-header-1" + value: "some-value" + - name: "add-header-3" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml new file mode 100644 index 0000000000..60e8c9fd16 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-add-multiple-filters.out.yaml @@ -0,0 +1,157 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: add-header-1 + value: some-value + - name: add-header-2 + value: some-value + type: ResponseHeaderModifier + - responseHeaderModifier: + add: + - name: add-header-1 + value: some-value + - name: add-header-3 + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addResponseHeaders: + - append: true + name: add-header-1 + value: some-value + - append: true + name: add-header-2 + value: some-value + - append: true + name: add-header-3 + value: some-value + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml new file mode 100644 index 0000000000..882b82282e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.in.yaml @@ -0,0 +1,61 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "set-header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-4" + value: "some-value" + - name: "set-header-4" + value: "some-value" + add: + - name: "Set-Header-1" + value: "some-value" + - name: "set-header-2" + value: "some-value" + - name: "set-header-3" + value: "some-value" + - name: "set-header-5" + value: "some-value" + - name: "set-header-5" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml new file mode 100644 index 0000000000..91a37eb93e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-adds.out.yaml @@ -0,0 +1,173 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: Set-Header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-5 + value: some-value + - name: set-header-5 + value: some-value + set: + - name: set-header-1 + value: some-value + - name: set-header-2 + value: some-value + - name: set-header-3 + value: some-value + - name: set-header-4 + value: some-value + - name: set-header-4 + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addResponseHeaders: + - append: true + name: Set-Header-1 + value: some-value + - append: true + name: set-header-2 + value: some-value + - append: true + name: set-header-3 + value: some-value + - append: true + name: set-header-5 + value: some-value + - append: false + name: set-header-4 + value: some-value + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml new file mode 100644 index 0000000000..c1f62250c9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + remove: + - "rem-header-1" + - "rem-header-2" + - type: ResponseHeaderModifier + responseHeaderModifier: + remove: + - "rem-header-1" + - "rem-header-3" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml new file mode 100644 index 0000000000..ce339d0d7c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-remove-multiple-filters.out.yaml @@ -0,0 +1,147 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + remove: + - rem-header-1 + - rem-header-2 + type: ResponseHeaderModifier + - responseHeaderModifier: + remove: + - rem-header-1 + - rem-header-3 + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeResponseHeaders: + - rem-header-1 + - rem-header-2 + - rem-header-3 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml new file mode 100644 index 0000000000..e77287f9ee --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + remove: + - "some-header-1" + - "some-header-1" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml new file mode 100644 index 0000000000..07eb5708da --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-duplicate-removes.out.yaml @@ -0,0 +1,140 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + remove: + - some-header-1 + - some-header-1 + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeResponseHeaders: + - some-header-1 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml new file mode 100644 index 0000000000..8a13837527 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml @@ -0,0 +1,46 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "example-header-1" + value: "" + add: + - name: "example-header-2" + value: "" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml new file mode 100644 index 0000000000..5e6273a77d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml @@ -0,0 +1,148 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: example-header-2 + value: "" + set: + - name: example-header-1 + value: "" + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - addResponseHeaders: + - append: true + name: example-header-2 + value: "" + - append: false + name: example-header-1 + value: "" + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.in.yaml new file mode 100644 index 0000000000..edddd1b5a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "" + value: "some-value" + add: + - name: "" + value: "some-value" + - name: "good-header" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.out.yaml new file mode 100644 index 0000000000..7f394b2737 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-empty-headers.out.yaml @@ -0,0 +1,123 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: "" + value: some-value + - name: good-header + value: some-value + set: + - name: "" + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: ResponseHeaderModifier Filter cannot set a header with an empty name + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml new file mode 100644 index 0000000000..93a13bbba6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: "example:1" + value: "some-value" + - name: "good-header" + value: "some-value" + add: + - name: "example/2" + value: "some-value" + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml new file mode 100644 index 0000000000..9c731253e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml @@ -0,0 +1,124 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: example/2 + value: some-value + set: + - name: example:1 + value: some-value + - name: good-header + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'ResponseHeaderModifier Filter cannot set headers with a ''/'' or + '':'' character in them. Header: ''example:1''' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.in.yaml new file mode 100644 index 0000000000..c2331bdad7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + set: + remove: diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.out.yaml new file mode 100644 index 0000000000..06d127d5fc --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-headers.out.yaml @@ -0,0 +1,135 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: {} + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml new file mode 100644 index 0000000000..4c29af176f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: "bad:header" + value: "some-value" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml new file mode 100644 index 0000000000..72358a1303 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-no-valid-headers.out.yaml @@ -0,0 +1,119 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + add: + - name: bad:header + value: some-value + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: ResponseHeaderModifier Filter did not provide valid configuration + to add/set/remove any headers + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.in.yaml new file mode 100644 index 0000000000..1a793c4c09 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + remove: + - "example-header-1" + - "example-header-2" + - "example-header-3" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.out.yaml new file mode 100644 index 0000000000..5665378d9c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-response-header-filter-remove.out.yaml @@ -0,0 +1,143 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - responseHeaderModifier: + remove: + - example-header-1 + - example-header-2 + - example-header-3 + type: ResponseHeaderModifier + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + removeResponseHeaders: + - example-header-1 + - example-header-2 + - example-header-3 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml new file mode 100644 index 0000000000..af7ae36282 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.in.yaml @@ -0,0 +1,33 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml new file mode 100644 index 0000000000..04b1c1ec37 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-exact-path-match.out.yaml @@ -0,0 +1,128 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.in.yaml new file mode 100644 index 0000000000..ee03208164 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.in.yaml @@ -0,0 +1,31 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - method: POST + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.out.yaml new file mode 100644 index 0000000000..9f1a599e71 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-http-method-match.out.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - method: POST + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + headerMatches: + - distinct: false + exact: POST + name: :method + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml new file mode 100644 index 0000000000..2ec00c37a8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.in.yaml @@ -0,0 +1,63 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + headers: + - name: Header-1 + type: Exact + value: "exact" + queryParams: + - name: QueryParam-1 + type: Exact + value: "exact" + backendRefs: + - name: service-1 + port: 8080 + - matches: + - path: + type: PathPrefix + value: "/prefix" + backendRefs: + - name: service-2 + port: 8080 + - matches: + - path: + type: RegularExpression + value: "*regex*" + headers: + - name: Header-1 + type: RegularExpression + value: "*regex*" + queryParams: + - name: QueryParam-1 + type: RegularExpression + value: "*regex*" + backendRefs: + - name: service-3 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml new file mode 100644 index 0000000000..96d72ccdeb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml @@ -0,0 +1,134 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: Header-1 + type: Exact + value: exact + path: + type: Exact + value: /exact + queryParams: + - name: QueryParam-1 + type: Exact + value: exact + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + type: PathPrefix + value: /prefix + - backendRefs: + - name: service-3 + port: 8080 + matches: + - headers: + - name: Header-1 + type: RegularExpression + value: '*regex*' + path: + type: RegularExpression + value: '*regex*' + queryParams: + - name: QueryParam-1 + type: RegularExpression + value: '*regex*' + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Regex "*regex*" is invalid: error parsing regexp: missing argument + to repetition operator: `*`' + reason: UnsupportedValue + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml new file mode 100644 index 0000000000..7709a6945d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.in.yaml @@ -0,0 +1,37 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/pathprefix" + headers: + - name: Header-1 + value: Val-1 + - name: Header-2 + value: Val-2 + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml new file mode 100644 index 0000000000..eb419ab3f3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-single-rule-with-path-prefix-and-exact-header-matches.out.yaml @@ -0,0 +1,139 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: Header-1 + value: Val-1 + - name: Header-2 + value: Val-2 + path: + value: /pathprefix + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + headerMatches: + - distinct: false + exact: Val-1 + name: Header-1 + - distinct: false + exact: Val-2 + name: Header-2 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /pathprefix diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml new file mode 100644 index 0000000000..dbd94a92b5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.in.yaml @@ -0,0 +1,46 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-that-doesnt-exist + port: 8080 + - name: service-that-doesnt-exist-2 + port: 8080 + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + name: service-1 + spec: + clusterIP: 7.7.7.7 + ports: + - port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml new file mode 100644 index 0000000000..50eeb2a32a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml @@ -0,0 +1,132 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-that-doesnt-exist + port: 8080 + - name: service-that-doesnt-exist-2 + port: 8080 + - name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service default/service-that-doesnt-exist-2 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 2 + valid: 1 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml new file mode 100644 index 0000000000..2ac69230aa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -0,0 +1,35 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - apk.wso2.com + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml new file mode 100644 index 0000000000..bad0357f6d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-specific-hostname-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -0,0 +1,130 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml new file mode 100644 index 0000000000..acdb93654d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.in.yaml @@ -0,0 +1,36 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - apk.wso2.com + - whales.envoyproxy.io + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml new file mode 100644 index 0000000000..12d7569350 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-two-specific-hostnames-attaching-to-gateway-with-wildcard-hostname.out.yaml @@ -0,0 +1,150 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + - whales.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: whales.envoyproxy.io + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/whales_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml new file mode 100644 index 0000000000..571a20a80e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml new file mode 100644 index 0000000000..c2572699c2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-full-path-replace-http.out.yaml @@ -0,0 +1,142 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + replaceFullPath: /rewrite + type: ReplaceFullPath + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + urlRewrite: + path: + fullReplace: /rewrite + prefixMatchReplace: null diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml new file mode 100644 index 0000000000..d1e4d2a0ec --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: "rewrite.com" + path: + type: ReplacePrefixMatch + replacePrefixMatch: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml new file mode 100644 index 0000000000..b699829a2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml @@ -0,0 +1,144 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: rewrite.com + path: + replacePrefixMatch: /rewrite + type: ReplacePrefixMatch + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + urlRewrite: + hostname: rewrite.com + path: + fullReplace: null + prefixMatchReplace: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml new file mode 100644 index 0000000000..bd20f47a98 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.in.yaml @@ -0,0 +1,40 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: "rewrite.com" diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml new file mode 100644 index 0000000000..6eb871dafa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml @@ -0,0 +1,138 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: rewrite.com + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + urlRewrite: + hostname: rewrite.com diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml new file mode 100644 index 0000000000..26f9897758 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.in.yaml @@ -0,0 +1,41 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: urlrewrite.envoyproxy.io + diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml new file mode 100644 index 0000000000..01d889c79f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml @@ -0,0 +1,138 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: urlrewrite.envoyproxy.io + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + urlRewrite: + hostname: urlrewrite.envoyproxy.io diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml new file mode 100644 index 0000000000..4226c48e02 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: "-rewrite.com" + path: + type: ReplacePrefixMatch + replacePrefixMatch: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml new file mode 100644 index 0000000000..1ae1248028 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-hostname.out.yaml @@ -0,0 +1,122 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: -rewrite.com + path: + replacePrefixMatch: /rewrite + type: ReplacePrefixMatch + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'hostname "-rewrite.com" is invalid: [a lowercase RFC 1123 subdomain + must consist of lower case alphanumeric characters, ''-'' or ''.'', and + must start and end with an alphanumeric character (e.g. ''example.com'', + regex used for validation is ''[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*'')]' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml new file mode 100644 index 0000000000..86d5f6bb17 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: "rewrite.com" + path: + type: ReplacePrefixMatch + replacePrefixMatch: /rewrite + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml new file mode 100644 index 0000000000..a56ea1e8f0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-multiple-filters.out.yaml @@ -0,0 +1,124 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: rewrite.com + path: + replacePrefixMatch: /rewrite + type: ReplacePrefixMatch + - type: URLRewrite + urlRewrite: + path: + replaceFullPath: /rewrite + type: ReplaceFullPath + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml new file mode 100644 index 0000000000..4da7aa9822 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: "rewrite.com" + path: + type: ReplacePrefixMatches + replacePrefixMatch: /rewrites diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml new file mode 100644 index 0000000000..6f119ee915 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path-type.out.yaml @@ -0,0 +1,120 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + hostname: rewrite.com + path: + replacePrefixMatch: /rewrites + type: ReplacePrefixMatches + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: 'Rewrite path type: ReplacePrefixMatches is invalid, only "ReplaceFullPath" + and "ReplacePrefixMatch" are supported' + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml new file mode 100644 index 0000000000..63c23fa10e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.in.yaml @@ -0,0 +1,43 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /rewrite + replaceFullPath: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml new file mode 100644 index 0000000000..d5c133ea99 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-invalid-path.out.yaml @@ -0,0 +1,119 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + replaceFullPath: /rewrite + replacePrefixMatch: /rewrite + type: ReplacePrefixMatch + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: ReplaceFullPath cannot be set when rewrite path type is "ReplacePrefixMatch" + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml new file mode 100644 index 0000000000..1b3c47b9ae --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.in.yaml @@ -0,0 +1,41 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml new file mode 100644 index 0000000000..99390c3fbf --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-missing-path.out.yaml @@ -0,0 +1,117 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: ReplacePrefixMatch must be set when rewrite path type is "ReplacePrefixMatch" + reason: UnsupportedValue + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml new file mode 100644 index 0000000000..0575619389 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml new file mode 100644 index 0000000000..26845a2d15 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-urlrewrite-filter-prefix-replace-http.out.yaml @@ -0,0 +1,142 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + replacePrefixMatch: /rewrite + type: ReplacePrefixMatch + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + urlRewrite: + path: + fullReplace: null + prefixMatchReplace: /rewrite diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml new file mode 100644 index 0000000000..d61cae0499 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.in.yaml @@ -0,0 +1,34 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "*.envoyproxy.io" + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml new file mode 100644 index 0000000000..ddfe368cde --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproute-with-wildcard-hostname-attaching-to-gateway-with-unset-hostname.out.yaml @@ -0,0 +1,129 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - '*.envoyproxy.io' + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.envoyproxy.io' + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/*_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.in.yaml b/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.in.yaml new file mode 100644 index 0000000000..7315f3f4fa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.in.yaml @@ -0,0 +1,189 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "*.com" + - "*.net" + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - example.com + rules: + - matches: + - path: + value: "/v1/example" + queryParams: + - name: "debug" + value: "yes" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-3 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - example.com + rules: + - matches: + - path: + value: "/v1/example" + backendRefs: + - name: service-2 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-4 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - example.net + rules: + - matches: + - path: + value: "/v1/status" + headers: + - name: "version" + value: "one" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-5 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - example.net + rules: + - matches: + - path: + value: "/v1/status" + backendRefs: + - name: service-2 + port: 8080 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + clusterIP: 1.1.1.1 + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-2 + spec: + clusterIP: 2.2.2.2 + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-2 + namespace: envoy-gateway + labels: + kubernetes.io/service-name: service-2 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "8.8.8.8" + conditions: + ready: true diff --git a/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.out.yaml b/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.out.yaml new file mode 100644 index 0000000000..242b637144 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/httproutes-with-multiple-matches.out.yaml @@ -0,0 +1,436 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 6 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + hostnames: + - '*.com' + - '*.net' + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: envoy-gateway + spec: + hostnames: + - example.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /v1/example + queryParams: + - name: debug + value: "yes" + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-3 + namespace: envoy-gateway + spec: + hostnames: + - example.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /v1/example + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-4 + namespace: envoy-gateway + spec: + hostnames: + - example.net + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: version + value: one + path: + value: /v1/status + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-5 + namespace: envoy-gateway + spec: + hostnames: + - example.net + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /v1/status + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + isHTTP2: false + name: httproute/envoy-gateway/httproute-2/rule/0/match/0/example_com + pathMatch: + distinct: false + name: "" + prefix: /v1/example + queryParamMatches: + - distinct: false + exact: "yes" + name: debug + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + isHTTP2: false + name: httproute/envoy-gateway/httproute-3/rule/0/match/0/example_com + pathMatch: + distinct: false + name: "" + prefix: /v1/example + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-4/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + headerMatches: + - distinct: false + exact: one + name: version + hostname: example.net + isHTTP2: false + name: httproute/envoy-gateway/httproute-4/rule/0/match/0/example_net + pathMatch: + distinct: false + name: "" + prefix: /v1/status + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-5/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.net + isHTTP2: false + name: httproute/envoy-gateway/httproute-5/rule/0/match/0/example_net + pathMatch: + distinct: false + name: "" + prefix: /v1/status + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.com' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/*_com + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*.net' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/*_net + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.in.yaml b/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.in.yaml new file mode 100644 index 0000000000..daf66d6777 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.in.yaml @@ -0,0 +1,34 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - name: udp + port: 80 + protocol: UDP diff --git a/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.out.yaml b/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.out.yaml new file mode 100644 index 0000000000..2481d89181 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-invalid-multiple-gateways.out.yaml @@ -0,0 +1,147 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + - name: udp + port: 80 + protocol: UDP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Port, protocol and hostname tuple must be unique for every listener + reason: HostnameConflict + status: "True" + type: Conflicted + - lastTransitionTime: null + message: Listener is invalid, see other Conditions for details. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: udp + supportedKinds: + - group: gateway.networking.k8s.io + kind: UDPRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-2/udp + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-2/udp + protocol: UDP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.in.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.in.yaml new file mode 100644 index 0000000000..662a2381ae --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.in.yaml @@ -0,0 +1,45 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: http-2 + hostname: company.com + port: 8888 + protocol: HTTP + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-3 + port: 8888 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: http-4 + hostname: example.com + port: 8888 + protocol: HTTP diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.out.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.out.yaml new file mode 100755 index 0000000000..4d162ad5d8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-listeners-same-ports.out.yaml @@ -0,0 +1,210 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - hostname: company.com + name: http-2 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-3 + port: 8888 + protocol: HTTP + - hostname: example.com + name: http-4 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-4 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-1/http-2 + ports: + - containerPort: 8888 + name: envoy-gateway/gateway-1/http-2 + protocol: HTTP + servicePort: 8888 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + - address: 0.0.0.0 + hostnames: + - company.com + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http-3 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 + - address: 0.0.0.0 + hostnames: + - example.com + isHTTP2: false + name: envoy-gateway/gateway-2/http-4 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml new file mode 100644 index 0000000000..07203553a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.in.yaml @@ -0,0 +1,81 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + hostname: "*.envoyproxy.io" + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + - name: http-3 + hostname: example.com + port: 8888 + protocol: HTTP + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - example.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http-3 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-2 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml new file mode 100644 index 0000000000..3ee2965a47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways-multiple-routes.out.yaml @@ -0,0 +1,292 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: '*.envoyproxy.io' + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + - allowedRoutes: + namespaces: + from: All + hostname: example.com + name: http-3 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - example.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http-3 + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http-3 +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-2/http-2 + ports: + - containerPort: 8888 + name: envoy-gateway/gateway-2/http-2 + protocol: HTTP + servicePort: 8888 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*.envoyproxy.io' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 + - address: 0.0.0.0 + hostnames: + - example.com + isHTTP2: false + name: envoy-gateway/gateway-2/http-3 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: example.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/example_com + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.in.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.in.yaml new file mode 100644 index 0000000000..821dd97b6c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.in.yaml @@ -0,0 +1,41 @@ +envoyproxy: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: apk + name: test + spec: + mergeGateways: true +gateways: + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-2 + port: 8888 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: http-3 + hostname: example.com + port: 8888 + protocol: HTTP diff --git a/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.out.yaml b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.out.yaml new file mode 100644 index 0000000000..974e1f0f6c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/merge-valid-multiple-gateways.out.yaml @@ -0,0 +1,174 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-2 + port: 8888 + protocol: HTTP + - hostname: example.com + name: http-3 + port: 8888 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http-3 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway-class: + proxy: + config: + apiVersion: apk.wso2.com/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: apk + spec: + logging: {} + mergeGateways: true + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: envoy-gateway/gateway-1/http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-2/http-2 + ports: + - containerPort: 8888 + name: envoy-gateway/gateway-2/http-2 + protocol: HTTP + servicePort: 8888 + metadata: + labels: + apk.wso2.com/owning-gatewayclass: envoy-gateway-class + name: envoy-gateway-class +xdsIR: + envoy-gateway-class: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http-2 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 + - address: 0.0.0.0 + hostnames: + - example.com + isHTTP2: false + name: envoy-gateway/gateway-2/http-3 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 8888 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.in.yaml new file mode 100644 index 0000000000..0b4c43f857 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.in.yaml @@ -0,0 +1,42 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + cors: + allowOrigins: + - "http://*.example.com" + - "http://foo.bar.com" + - "https://*" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + maxAge: 1000s diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.out.yaml new file mode 100755 index 0000000000..d0fd54b1bc --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-invalid-cross-ns-ref.out.yaml @@ -0,0 +1,115 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: default + spec: + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - http://*.example.com + - http://foo.bar.com + - https://* + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Namespace:default TargetRef.Namespace:envoy-gateway, SecurityPolicy + can only target a resource in the same namespace. + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.in.yaml new file mode 100644 index 0000000000..fff03a86c8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.in.yaml @@ -0,0 +1,23 @@ +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-unknown-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: unknown-gateway + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-unknown-httproute + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: unknown-httproute + namespace: envoy-gateway diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.out.yaml new file mode 100644 index 0000000000..c052671098 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-no-status-for-unknown-gateway-or-route.out.yaml @@ -0,0 +1,31 @@ +infraIR: {} +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-unknown-httproute + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: unknown-httproute + namespace: envoy-gateway + status: + ancestors: null +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-unknown-gateway + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: unknown-gateway + namespace: envoy-gateway + status: + ancestors: null +xdsIR: {} diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.in.yaml new file mode 100644 index 0000000000..c5afd9fe8b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.in.yaml @@ -0,0 +1,117 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + cors: + allowOrigins: + - "http://*.example.com" + - "http://foo.bar.com" + - "https://*" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + maxAge: 1000s + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + cors: + allowOrigins: + - "https://*.test.com:8080" + - "https://www.test.org:8080" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-5" + - "x-header-6" + exposeHeaders: + - "x-header-7" + - "x-header-8" + maxAge: 2000s diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.out.yaml new file mode 100755 index 0000000000..3008e7c8a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-override-replace.out.yaml @@ -0,0 +1,338 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route-1 + namespace: default + spec: + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - https://*.test.com:8080 + - https://www.test.org:8080 + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: envoy-gateway + spec: + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - http://*.example.com + - http://foo.bar.com + - https://* + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: https://.*\.test\.com:8080 + - distinct: false + exact: https://www.test.org:8080 + name: "" + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: http://.*\.example\.com + - distinct: false + exact: http://foo.bar.com + name: "" + - distinct: false + name: "" + safeRegex: https://.* + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.in.yaml new file mode 100644 index 0000000000..6600b040c4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.in.yaml @@ -0,0 +1,133 @@ +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-as-well + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: also-target-httproute-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: target-grpcroute-in-gateway-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: envoy-gateway + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + rules: + - matches: + - headers: + - type: Exact + name: magic + value: foo + backendRefs: + - name: service-1 + port: 8080 +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: https + protocol: HTTPS + port: 443 + allowedRoutes: + namespaces: + from: Same + - name: tcp + protocol: TCP + port: 53 + allowedRoutes: + namespaces: + from: Same diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.out.yaml new file mode 100755 index 0000000000..2ddeae68c4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-status-conditions.out.yaml @@ -0,0 +1,436 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: https + port: 443 + protocol: HTTPS + - allowedRoutes: + namespaces: + from: Same + name: tcp + port: 53 + protocol: TCP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is HTTPS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: https + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tcp + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - headers: + - name: magic + type: Exact + value: foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + - address: null + name: envoy-gateway/gateway-2/tcp + ports: + - containerPort: 10053 + name: tcp + protocol: TCP + servicePort: 53 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: also-target-httproute-in-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Unable to target HTTPRoute, another SecurityPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-grpcroute-in-gateway-2 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: GRPCRoute + name: grpcroute-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [envoy-gateway/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-as-well + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Unable to target Gateway, another SecurityPolicy has already attached + to it + reason: Conflicted + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 1 + valid: 0 + directResponse: + statusCode: 500 + headerMatches: + - distinct: false + exact: foo + name: magic + hostname: '*' + isHTTP2: true + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/* diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.in.yaml new file mode 100644 index 0000000000..6755033693 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.in.yaml @@ -0,0 +1,58 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: users-secret + data: + .htpasswd: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + basicAuth: + users: + name: "users-secret" diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.out.yaml new file mode 100644 index 0000000000..493d550bde --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-basic-auth.out.yaml @@ -0,0 +1,166 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + basicAuth: + users: + group: null + kind: null + name: users-secret + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + basicAuth: + users: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.in.yaml new file mode 100644 index 0000000000..a39e1ec9c7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.in.yaml @@ -0,0 +1,173 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-3 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-3 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-2 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + cors: + allowOrigins: + - "http://*.example.com" + - "http://foo.bar.com" + - "https://*" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + maxAge: 1000s +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + cors: + allowOrigins: + - "https://*.test.com:8080" + - "https://www.test.org:8080" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-5" + - "x-header-6" + exposeHeaders: + - "x-header-7" + - "x-header-8" + maxAge: 2000s +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + cors: + allowOrigins: + - "*" + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-5" + - "x-header-6" + exposeHeaders: + - "x-header-7" + - "x-header-8" + maxAge: 2000s diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.out.yaml new file mode 100644 index 0000000000..1414208291 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-cors.out.yaml @@ -0,0 +1,554 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-3 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-3 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-3 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 + envoy-gateway/gateway-3: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-3/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-3 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-3 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route-1 + namespace: default + spec: + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - https://*.test.com:8080 + - https://www.test.org:8080 + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route-2 + namespace: default + spec: + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - '*' + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: envoy-gateway + spec: + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - http://*.example.com + - http://foo.bar.com + - https://* + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-1 + - x-header-2 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: http://.*\.example\.com + - distinct: false + exact: http://foo.bar.com + name: "" + - distinct: false + name: "" + safeRegex: https://.* + exposeHeaders: + - x-header-3 + - x-header-4 + maxAge: 16m40s + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: https://.*\.test\.com:8080 + - distinct: false + exact: https://www.test.org:8080 + name: "" + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + envoy-gateway/gateway-3: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-3/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + cors: + allowHeaders: + - x-header-5 + - x-header-6 + allowMethods: + - GET + - POST + allowOrigins: + - distinct: false + name: "" + safeRegex: .* + exposeHeaders: + - x-header-7 + - x-header-8 + maxAge: 33m20s + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml new file mode 100644 index 0000000000..246d996352 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml @@ -0,0 +1,65 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: default + name: http-backend + spec: + ports: + - port: 8080 +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + name: http-backend + namespace: default + port: 80 + headersToBackend: + - header1 + - header2 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml new file mode 100644 index 0000000000..986994f0e6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml @@ -0,0 +1,167 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: default + port: 80 + headersToBackend: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: TCP Port 80 not found on service default/http-backend + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml new file mode 100644 index 0000000000..1ad4755a35 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml @@ -0,0 +1,64 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: default + name: http-backend + spec: + ports: + - port: 8080 +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + name: http-backend + namespace: default + headersToBackend: + - header1 + - header2 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml new file mode 100755 index 0000000000..9a12785e41 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml @@ -0,0 +1,167 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: default + headersToBackend: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: A valid port number corresponding to a port on the Service must be + specified + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml new file mode 100644 index 0000000000..8701ab0753 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml @@ -0,0 +1,65 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: http-backend + spec: + ports: + - port: 80 +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + name: http-backend + namespace: envoy-gateway + port: 80 + headersToBackend: + - header1 + - header2 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml new file mode 100644 index 0000000000..fab5b99308 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml @@ -0,0 +1,168 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: envoy-gateway + port: 80 + headersToBackend: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Backend ref to Service envoy-gateway/http-backend not permitted by + any ReferenceGrant + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml new file mode 100644 index 0000000000..8834c86060 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml @@ -0,0 +1,56 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + name: http-backend + namespace: default + port: 80 + headersToBackend: + - header1 + - header2 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml new file mode 100644 index 0000000000..34585ba90f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml @@ -0,0 +1,167 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: default + port: 80 + headersToBackend: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Service default/http-backend not found + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml new file mode 100644 index 0000000000..0102d9c6b9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml @@ -0,0 +1,231 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - www.bar.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /bar + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: http-backend + spec: + ports: + - port: 80 + name: http + protocol: TCP + - apiVersion: v1 + kind: Service + metadata: + namespace: default + name: grpc-backend + spec: + ports: + - port: 9000 + name: grpc + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: envoy-gateway + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 80 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-grpc-backend + namespace: default + labels: + kubernetes.io/service-name: grpc-backend + addressType: IPv4 + ports: + - name: grpc + protocol: TCP + port: 9000 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: envoy-gateway + name: referencegrant-1 + spec: + from: + - group: apk.wso2.com + kind: SecurityPolicy + namespace: default + - group: gateway.networking.k8s.io + kind: BackendTLSPolicy + namespace: default + to: + - group: '' + kind: Service +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: ca-cmap + namespace: default + data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIDJzCCAg+gAwIBAgIUAl6UKIuKmzte81cllz5PfdN2IlIwDQYJKoZIhvcNAQEL + BQAwIzEQMA4GA1UEAwwHbXljaWVudDEPMA0GA1UECgwGa3ViZWRiMB4XDTIzMTAw + MjA1NDE1N1oXDTI0MTAwMTA1NDE1N1owIzEQMA4GA1UEAwwHbXljaWVudDEPMA0G + A1UECgwGa3ViZWRiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSTc + 1yj8HW62nynkFbXo4VXKv2jC0PM7dPVky87FweZcTKLoWQVPQE2p2kLDK6OEszmM + yyr+xxWtyiveremrWqnKkNTYhLfYPhgQkczib7eUalmFjUbhWdLvHakbEgCodn3b + kz57mInX2VpiDOKg4kyHfiuXWpiBqrCx0KNLpxo3DEQcFcsQTeTHzh4752GV04RU + Ti/GEWyzIsl4Rg7tGtAwmcIPgUNUfY2Q390FGqdH4ahn+mw/6aFbW31W63d9YJVq + ioyOVcaMIpM5B/c7Qc8SuhCI1YGhUyg4cRHLEw5VtikioyE3X04kna3jQAj54YbR + bpEhc35apKLB21HOUQIDAQABo1MwUTAdBgNVHQ4EFgQUyvl0VI5vJVSuYFXu7B48 + 6PbMEAowHwYDVR0jBBgwFoAUyvl0VI5vJVSuYFXu7B486PbMEAowDwYDVR0TAQH/ + BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAMLxrgFVMuNRq2wAwcBt7SnNR5Cfz + 2MvXq5EUmuawIUi9kaYjwdViDREGSjk7JW17vl576HjDkdfRwi4E28SydRInZf6J + i8HZcZ7caH6DxR335fgHVzLi5NiTce/OjNBQzQ2MJXVDd8DBmG5fyatJiOJQ4bWE + A7FlP0RdP3CO3GWE0M5iXOB2m1qWkE2eyO4UHvwTqNQLdrdAXgDQlbam9e4BG3Gg + d/6thAkWDbt/QNT+EJHDCvhDRKh1RuGHyg+Y+/nebTWWrFWsktRrbOoHCZiCpXI1 + 3eXE6nt0YkgtDxG22KqnhpAg9gUSs2hlhoxyvkzyF0mu6NhPlwAgnq7+/Q== + -----END CERTIFICATE----- +backendTLSPolicies: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls-http + namespace: default + spec: + targetRef: + group: '' + kind: Service + name: http-backend + namespace: envoy-gateway + sectionName: "80" + tls: + caCertRefs: + - name: ca-cmap + group: '' + kind: ConfigMap + hostname: http-backend + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + name: policy-btls-grpc + namespace: default + spec: + targetRef: + group: '' + kind: Service + name: grpc-backend + sectionName: "9000" + tls: + caCertRefs: + - name: ca-cmap + group: '' + kind: ConfigMap + hostname: grpc-backend +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + Name: http-backend + Namespace: envoy-gateway + Port: 80 + Path: /auth + headersToBackend: + - header1 + - header2 + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + extAuth: + headersToExtAuth: + - header1 + - header2 + grpc: + backendRef: + name: grpc-backend + port: 9000 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml new file mode 100755 index 0000000000..ad044aac32 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml @@ -0,0 +1,373 @@ +backendTLSPolicies: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls-http + namespace: default + spec: + targetRef: + group: "" + kind: Service + name: http-backend + namespace: envoy-gateway + sectionName: "80" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: http-backend + status: + ancestors: + - ancestorRef: + group: apk.wso2.com + kind: SecurityPolicy + name: policy-for-gateway + namespace: default + conditions: + - lastTransitionTime: null + message: BackendTLSPolicy is Accepted + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: BackendTLSPolicy + metadata: + creationTimestamp: null + name: policy-btls-grpc + namespace: default + spec: + targetRef: + group: "" + kind: Service + name: grpc-backend + sectionName: "9000" + tls: + caCertRefs: + - group: "" + kind: ConfigMap + name: ca-cmap + hostname: grpc-backend + status: + ancestors: + - ancestorRef: + group: apk.wso2.com + kind: SecurityPolicy + name: policy-for-http-route + namespace: default + conditions: + - lastTransitionTime: null + message: BackendTLSPolicy is Accepted + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - www.bar.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + extAuth: + grpc: + backendRef: + name: grpc-backend + port: 9000 + headersToExtAuth: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: envoy-gateway + port: 80 + headersToBackend: + - header1 + - header2 + path: /auth + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + extAuth: + grpc: + authority: grpc-backend.default:9000 + destination: + name: securitypolicy/default/policy-for-http-route/grpc-backend + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 9000 + protocol: GRPC + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls-grpc/default-ca + sni: grpc-backend + weight: 1 + headersToExtAuth: + - header1 + - header2 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + extAuth: + http: + authority: http-backend.envoy-gateway:80 + destination: + name: securitypolicy/default/policy-for-gateway/http-backend + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 80 + protocol: HTTP + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls-http/default-ca + sni: http-backend + weight: 1 + headersToBackend: + - header1 + - header2 + path: /auth + hostname: www.bar.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/www_bar_com + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.in.yaml new file mode 100644 index 0000000000..2521c1298c --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.in.yaml @@ -0,0 +1,165 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo + backendRefs: + - name: service-1 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - www.bar.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /bar + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: http-backend + spec: + ports: + - port: 80 + name: http + protocol: TCP + - apiVersion: v1 + kind: Service + metadata: + namespace: default + name: grpc-backend + spec: + ports: + - port: 9000 + name: grpc + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: envoy-gateway + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 80 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-grpc-backend + namespace: default + labels: + kubernetes.io/service-name: grpc-backend + addressType: IPv4 + ports: + - name: grpc + protocol: TCP + port: 9000 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: envoy-gateway + name: referencegrant-1 + spec: + from: + - group: apk.wso2.com + kind: SecurityPolicy + namespace: default + to: + - group: '' + kind: Service +securityPolicies: + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + extAuth: + http: + backendRef: + Name: http-backend + Namespace: envoy-gateway + Port: 80 + Path: /auth + headersToBackend: + - header1 + - header2 + - apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + extAuth: + headersToExtAuth: + - header1 + - header2 + grpc: + backendRef: + name: grpc-backend + port: 9000 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.out.yaml new file mode 100644 index 0000000000..06400a32a7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-extauth.out.yaml @@ -0,0 +1,297 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - www.bar.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + extAuth: + grpc: + backendRef: + name: grpc-backend + port: 9000 + headersToExtAuth: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + extAuth: + http: + backendRef: + name: http-backend + namespace: envoy-gateway + port: 80 + headersToBackend: + - header1 + - header2 + path: /auth + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + extAuth: + grpc: + authority: grpc-backend.default:9000 + destination: + name: securitypolicy/default/policy-for-http-route/grpc-backend + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 9000 + protocol: GRPC + weight: 1 + headersToExtAuth: + - header1 + - header2 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + extAuth: + http: + authority: http-backend.envoy-gateway:80 + destination: + name: securitypolicy/default/policy-for-gateway/http-backend + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 80 + protocol: HTTP + weight: 1 + headersToBackend: + - header1 + - header2 + path: /auth + hostname: www.bar.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/www_bar_com + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml new file mode 100644 index 0000000000..8e3d04deaa --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.in.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway # This policy should attach httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route # This policy should attach httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + jwt: + providers: + - name: example2 + issuer: https://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim2 + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client2-secret" diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml new file mode 100644 index 0000000000..d3c939f3b9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-and-invalid-oidc.out.yaml @@ -0,0 +1,308 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + spec: + jwt: + providers: + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: one-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client2-secret + provider: + issuer: https://accounts.google.com + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Secret default/client2-secret does not exist + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + issuer: https://accounts.google.com + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Secret envoy-gateway/client1-secret does not exist + reason: Invalid + status: "False" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-2]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + jwt: + providers: + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: one-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml new file mode 100644 index 0000000000..7c3a41a1f7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.in.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + - name: example2 + issuer: https://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: two-route-example-key + claim: claim2 +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + jwt: + providers: + - name: example3 + issuer: https://three.example.com + audiences: + - three.foo.com + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: three-route-example-key + claim: claim3 + extractFrom: + headers: + - name: Authorization + valuePrefix: 'Bearer ' + cookies: + - session_access_token + params: + - token diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml new file mode 100644 index 0000000000..22f29d9c96 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt-with-custom-extractor.out.yaml @@ -0,0 +1,382 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + extractFrom: + cookies: + - session_access_token + headers: + - name: Authorization + valuePrefix: 'Bearer ' + params: + - token + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: https://two.example.com + name: example2 + remoteJWKS: + uri: https://two.example.com/jwt/public-key/jwks.json + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + extractFrom: + cookies: + - session_access_token + headers: + - name: Authorization + valuePrefix: 'Bearer ' + params: + - token + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.in.yaml new file mode 100644 index 0000000000..0f57a7f983 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.in.yaml @@ -0,0 +1,118 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - apk.wso2.com + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jwt: + providers: + - name: example1 + issuer: https://one.example.com + audiences: + - one.foo.com + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key + claim: claim1 + - name: example2 + issuer: http://two.example.com + audiences: + - two.foo.com + remoteJWKS: + uri: http://two.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: two-route-example-key + claim: claim2 +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + jwt: + providers: + - name: example3 + issuer: https://three.example.com + audiences: + - three.foo.com + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + claimToHeaders: + - header: three-route-example-key + claim: claim3 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.out.yaml new file mode 100644 index 0000000000..52ac328af5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-jwt.out.yaml @@ -0,0 +1,366 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - apk.wso2.com + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: http://two.example.com + name: example2 + remoteJWKS: + uri: http://two.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + jwt: + providers: + - audiences: + - one.foo.com + claimToHeaders: + - claim: claim1 + header: one-route-example-key + issuer: https://one.example.com + name: example1 + remoteJWKS: + uri: https://one.example.com/jwt/public-key/jwks.json + - audiences: + - two.foo.com + claimToHeaders: + - claim: claim2 + header: two-route-example-key + issuer: http://two.example.com + name: example2 + remoteJWKS: + uri: http://two.example.com/jwt/public-key/jwks.json + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: apk.wso2.com + isHTTP2: false + jwt: + providers: + - audiences: + - three.foo.com + claimToHeaders: + - claim: claim3 + header: three-route-example-key + issuer: https://three.example.com + name: example3 + remoteJWKS: + uri: https://three.example.com/jwt/public-key/jwks.json + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml new file mode 100644 index 0000000000..5c3795d803 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.in.yaml @@ -0,0 +1,41 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client1-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-non-exist-secretRef + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + oidc: + provider: + issuer: "https://httpbin.org/" + clientID: "client1.apps.foo.bar.com" + clientSecret: + name: "client1-secret" diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml new file mode 100644 index 0000000000..34cc81c21b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-issuer.out.yaml @@ -0,0 +1,108 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-non-exist-secretRef + namespace: default + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + oidc: + clientID: client1.apps.foo.bar.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + issuer: https://httpbin.org/ + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: 'Error fetching endpoints from issuer: invalid character ''<'' looking + for beginning of value' + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml new file mode 100644 index 0000000000..0f291acfd5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.in.yaml @@ -0,0 +1,118 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: client2-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client3-secret + data: + invalid_client_secret_key: Y2xpZW50MTpzZWNyZXQK + +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-3 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-non-exist-secretRef + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-no-referenceGrant + uid: 08335a80-83ba-4592-888f-6ac0bba44ce4 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + namespace: envoy-gateway + name: "client2-secret" +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-no-client-secret-key + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + oidc: + provider: + issuer: "https://accounts.google.com" + authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth" + tokenEndpoint: "https://oauth2.googleapis.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + namespace: default + name: "client3-secret" diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml new file mode 100644 index 0000000000..75516da5bb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc-invalid-secretref.out.yaml @@ -0,0 +1,320 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-3 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-1 + default/gateway-2: + proxy: + listeners: + - address: null + name: default/gateway-2/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-2 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-2 + default/gateway-3: + proxy: + listeners: + - address: null + name: default/gateway-3/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-3 + apk.wso2.com/owning-gateway-namespace: default + name: default/gateway-3 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-non-exist-secretRef + namespace: default + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Secret default/client1-secret does not exist + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-no-referenceGrant + namespace: default + uid: 08335a80-83ba-4592-888f-6ac0bba44ce4 + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client2-secret + namespace: envoy-gateway + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: default + conditions: + - lastTransitionTime: null + message: Secret ref namespace must be unspecified/empty or default + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-no-client-secret-key + namespace: default + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client3-secret + namespace: default + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + issuer: https://accounts.google.com + tokenEndpoint: https://oauth2.googleapis.com/token + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-3 + namespace: default + conditions: + - lastTransitionTime: null + message: Client secret not found in secret default/client3-secret + reason: Invalid + status: "False" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + default/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + default/gateway-3: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-3/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.in.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.in.yaml new file mode 100644 index 0000000000..8be22f0239 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.in.yaml @@ -0,0 +1,127 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: client1-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client2-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: default + name: client3-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: apk + name: envoy-oidc-hmac + data: + hmac-secret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.example.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - www.example.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/bar" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-discover-endpoints # This policy should attach httproute-2 + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + oidc: + provider: + issuer: "https://accounts.google.com" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" + redirectURL: "https://www.example.com/bar/oauth2/callback" + logoutPath: "/bar/logout" +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route # This policy should attach httproute-1 + uid: 08335a80-83ba-4592-888f-6ac0bba44ce4 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + oidc: + provider: + issuer: "https://oauth.foo.com" + authorizationEndpoint: "https://oauth.foo.com/oauth2/v2/auth" + tokenEndpoint: "https://oauth.foo.com/token" + clientID: "client2.oauth.foo.com" + clientSecret: + name: "client2-secret" + scopes: ["openid", "email", "profile"] + redirectURL: "https://www.example.com/foo/oauth2/callback" + logoutPath: "/foo/logout" diff --git a/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.out.yaml b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.out.yaml new file mode 100644 index 0000000000..a6014fbff6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/securitypolicy-with-oidc.out.yaml @@ -0,0 +1,304 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.example.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - www.example.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route + namespace: default + uid: 08335a80-83ba-4592-888f-6ac0bba44ce4 + spec: + oidc: + clientID: client2.oauth.foo.com + clientSecret: + group: null + kind: null + name: client2-secret + logoutPath: /foo/logout + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + issuer: https://oauth.foo.com + tokenEndpoint: https://oauth.foo.com/token + redirectURL: https://www.example.com/foo/oauth2/callback + scopes: + - openid + - email + - profile + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + namespace: default + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: apk.wso2.com/gatewayclass-controller +- apiVersion: apk.wso2.com/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-discover-endpoints + namespace: envoy-gateway + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + logoutPath: /bar/logout + provider: + issuer: https://accounts.google.com + redirectURL: https://www.example.com/bar/oauth2/callback + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: apk.wso2.com/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.example.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_example_com + oidc: + clientID: client2.oauth.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + cookieSuffix: 5f93c2e4 + hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + logoutPath: /foo/logout + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + tokenEndpoint: https://oauth.foo.com/token + redirectPath: /foo/oauth2/callback + redirectURL: https://www.example.com/foo/oauth2/callback + scopes: + - openid + - email + - profile + pathMatch: + distinct: false + name: "" + prefix: /foo + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.example.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/www_example_com + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + cookieSuffix: b0a1b740 + hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + logoutPath: /bar/logout + provider: + authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth + tokenEndpoint: https://oauth2.googleapis.com/token + redirectPath: /bar/oauth2/callback + redirectURL: https://www.example.com/bar/oauth2/callback + scopes: + - openid + pathMatch: + distinct: false + name: "" + prefix: /bar diff --git a/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml b/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml new file mode 100644 index 0000000000..b4beac9da1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.in.yaml @@ -0,0 +1,46 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 90 + tls: + certificateRefs: + - group: "" + kind: Secret + name: tls-secret-1 + mode: Terminate + allowedRoutes: + namespaces: + from: All +tcpRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + namespace: default + name: tcproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml b/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml new file mode 100644 index 0000000000..b2246be98a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tcproute-attaching-to-gateway-with-listener-tls-terminate.out.yaml @@ -0,0 +1,117 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 90 + protocol: TLS + tls: + certificateRefs: + - group: "" + kind: Secret + name: tls-secret-1 + mode: Terminate + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10090 + name: tls + protocol: TLS + servicePort: 90 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tcpRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TCPRoute + metadata: + creationTimestamp: null + name: tcproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tcproute/default/tcproute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: TCP + weight: 1 + name: envoy-gateway/gateway-1/tls/tcproute-1 + port: 10090 + tls: + terminate: + certificates: + - name: envoy-gateway/tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lVRUZNaFA5ZUo5WEFCV3NRNVptNmJSazJjTE5Rd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xabTl2TG1KaGNpNWpiMjB3SGhjTk1qUXdNakk1TURrek1ERXdXaGNOTXpRdwpNakkyTURrek1ERXdXakFXTVJRd0VnWURWUVFEREF0bWIyOHVZbUZ5TG1OdmJUQ0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFKbEk2WXhFOVprQ1BzNnBDUXhickNtZWl4OVA1RGZ4OVJ1NUxENFQKSm1kVzdJS2R0UVYvd2ZMbXRzdTc2QithVGRDaldlMEJUZmVPT1JCYlIzY1BBRzZFbFFMaWNsUVVydW4zcStncwpKcEsrSTdjSStqNXc4STY4WEg1V1E3clZVdGJ3SHBxYncrY1ZuQnFJVU9MaUlhdGpJZjdLWDUxTTF1RjljZkVICkU0RG5jSDZyYnI1OS9SRlpCc2toeHM1T3p3Sklmb2hreXZGd2V1VHd4Sy9WcGpJKzdPYzQ4QUJDWHBOTzlEL3EKRWgrck9hdWpBTWNYZ0hRSVRrQ2lpVVRjVW82TFNIOXZMWlB0YXFmem9acTZuaE1xcFc2NUUxcEF3RjNqeVRUeAphNUk4SmNmU0Zqa2llWjIwTFVRTW43TThVNHhIamFvL2d2SDBDQWZkQjdSTFUyc0NBd0VBQWFOVE1GRXdIUVlEClZSME9CQllFRk9SQ0U4dS8xRERXN2loWnA3Y3g5dFNtUG02T01COEdBMVVkSXdRWU1CYUFGT1JDRTh1LzFERFcKN2loWnA3Y3g5dFNtUG02T01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBRnQ1M3pqc3FUYUg1YThFMmNodm1XQWdDcnhSSzhiVkxNeGl3TkdqYm1FUFJ6K3c2TngrazBBOEtFY0lEc0tjClNYY2k1OHU0b1didFZKQmx6YS9adWpIUjZQMUJuT3BsK2FveTc4NGJiZDRQMzl3VExvWGZNZmJCQ20xdmV2aDkKQUpLbncyWnRxcjRta2JMY3hFcWxxM3NCTEZBUzlzUUxuS05DZTJjR0xkVHAyYm9HK3FjZ3lRZ0NJTTZmOEVNdgpXUGlmQ01NR3V6Sy9HUkY0YlBPL1lGNDhld0R1M1VlaWgwWFhkVUFPRTlDdFVhOE5JaGMxVVBhT3pQcnRZVnFyClpPR2t2L0t1K0I3OGg4U0VzTzlYclFjdXdiT25KeDZLdFIrYWV5a3ZBcFhDUTNmWkMvYllLQUFSK1A4QUpvUVoKYndJVW1YaTRnajVtK2JLUGhlK2lyK0U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= diff --git a/adapter/internal/operator/gateway-api/testdata/tls/.gitignore b/adapter/internal/operator/gateway-api/testdata/tls/.gitignore new file mode 100644 index 0000000000..a813e7b2fd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/.gitignore @@ -0,0 +1,2 @@ +# allow test keys to be checked-in to test folder only +!*.key diff --git a/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256-cert.pem b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256-cert.pem new file mode 100644 index 0000000000..2ca528b493 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBgTCCASegAwIBAgIUFml8LBG0o/QK4Q+Z7k824sC2iFwwCgYIKoZIzj0EAwIw +FjEUMBIGA1UEAwwLZm9vLmJhci5jb20wHhcNMjQwMjI5MDkzMDEwWhcNMzQwMjI2 +MDkzMDEwWjAWMRQwEgYDVQQDDAtmb28uYmFyLmNvbTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABLaYvqKuVVoxDo52eWzvYB5jw7EMF86rmyoi5ZuayzdMvpg4zBr8 +RKBjNs+T1jB8OKucS/7RUDx0pz+976zM3iOjUzBRMB0GA1UdDgQWBBTO6+cg0R0d +wtrzJQPG6g76hBBUzTAfBgNVHSMEGDAWgBTO6+cg0R0dwtrzJQPG6g76hBBUzTAP +BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQC2XpPQgQzWae3b5pZt +GcuMfDJ0c0OPKcnegkXZ2C4B3gIgU/sRkwIpLQNRV+EaYw4P5T5gPE6Y+VpmC98h +oVjZ/zQ= +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256.key b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256.key new file mode 100644 index 0000000000..b5678c1fb5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p256.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAyfKQvPBudXRh0LLmtVRJPYl6e+GgzvgctFexKh9a1GoAoGCCqGSM49 +AwEHoUQDQgAEtpi+oq5VWjEOjnZ5bO9gHmPDsQwXzqubKiLlm5rLN0y+mDjMGvxE +oGM2z5PWMHw4q5xL/tFQPHSnP73vrMzeIw== +-----END EC PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384-cert.pem b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384-cert.pem new file mode 100644 index 0000000000..3d55a9124e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384-cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBvTCCAUSgAwIBAgIUSivJGTDwokS7heY/2csRsz4vJB0wCgYIKoZIzj0EAwIw +FjEUMBIGA1UEAwwLZm9vLmJhci5jb20wHhcNMjQwMjI5MDkzMDEwWhcNMzQwMjI2 +MDkzMDEwWjAWMRQwEgYDVQQDDAtmb28uYmFyLmNvbTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABFvrvRebaaWuS6MAEId6wZfOKvuCTyUMOlZJqFCF9TkzMYl8CiofynOt +Crs0vva9kd/P2JMGBJqgYevbwotrRi12qLnH04/joGIjjTOKo3Iofr+Fk8wLvAe3 +O1ZKtR9szqNTMFEwHQYDVR0OBBYEFIKs1QFnoDt9+qokR4ODWbMz1f+PMB8GA1Ud +IwQYMBaAFIKs1QFnoDt9+qokR4ODWbMz1f+PMA8GA1UdEwEB/wQFMAMBAf8wCgYI +KoZIzj0EAwIDZwAwZAIwH31tHyfUP04XHpbWGe1Z1IaBZBwIth55DU4hjfPu8m+J +Wv7bW8zTSgwLiqorfcunAjA0ghNJBA1X2XtIKDml3s7/Vx8FJcS04vpChh+lAbLS +veaa28R3TLEY3U+QTXI/wIk= +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384.key b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384.key new file mode 100644 index 0000000000..4626890e66 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/ecdsa-p384.key @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCeNj3chbP4g2cPOizPipLiJ6tCQAXf4rjSHHksQ1b6KV7xyDP6iVAK +BVoyR/aipR+gBwYFK4EEACKhZANiAARb670Xm2mlrkujABCHesGXzir7gk8lDDpW +SahQhfU5MzGJfAoqH8pzrQq7NL72vZHfz9iTBgSaoGHr28KLa0Ytdqi5x9OP46Bi +I40ziqNyKH6/hZPMC7wHtztWSrUfbM4= +-----END EC PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/gen-certs.sh b/adapter/internal/operator/gateway-api/testdata/tls/gen-certs.sh new file mode 100755 index 0000000000..8d428ed4a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/gen-certs.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# The following commands were used to generate test key/cert pairs +# using openssl (LibreSSL 3.3.6) + +CERT_VALIDITY_DAYS=3650 + +# RSA + +openssl req -x509 -nodes -days $CERT_VALIDITY_DAYS -newkey rsa:2048 -keyout rsa-pkcs8.key -out rsa-cert.pem -subj "/CN=foo.bar.com" +openssl rsa -in rsa-pkcs8.key -out rsa-pkcs1.key + +# RSA with SAN extension + +openssl req -x509 -nodes -days $CERT_VALIDITY_DAYS -newkey rsa:2048 -keyout rsa-pkcs8-san.key -out rsa-cert-san.pem -subj "/CN=Test Inc" -addext "subjectAltName = DNS:foo.bar.com" +openssl rsa -in rsa-pkcs8-san.key -out rsa-pkcs1-san.key + +# RSA with wildcard SAN domain + +openssl req -x509 -nodes -days $CERT_VALIDITY_DAYS -newkey rsa:2048 -keyout rsa-pkcs8-wildcard.key -out rsa-cert-wildcard.pem -subj "/CN=Test Inc" -addext "subjectAltName = DNS:*, DNS:*.example.com" +openssl rsa -in rsa-pkcs8-wildcard.key -out rsa-pkcs1-wildcard.key + +# ECDSA-p256 + +openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-p256.key +openssl req -new -x509 -days $CERT_VALIDITY_DAYS -key ecdsa-p256.key -out ecdsa-p256-cert.pem -subj "/CN=foo.bar.com" + +# ECDSA-p384 + +openssl ecparam -name secp384r1 -genkey -noout -out ecdsa-p384.key +openssl req -new -x509 -days $CERT_VALIDITY_DAYS -key ecdsa-p384.key -out ecdsa-p384-cert.pem -subj "/CN=foo.bar.com" diff --git a/adapter/internal/operator/gateway-api/testdata/tls/invalid-key-type.key b/adapter/internal/operator/gateway-api/testdata/tls/invalid-key-type.key new file mode 100644 index 0000000000..5d5aa4d36e --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/invalid-key-type.key @@ -0,0 +1,3 @@ +-----BEGIN FOO----- +SGVsbG8gd29ybGQK +-----END FOO----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/malformed-cert.pem b/adapter/internal/operator/gateway-api/testdata/tls/malformed-cert.pem new file mode 100644 index 0000000000..0c19932b1a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/malformed-cert.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +SGVsbG8gd29ybGQK +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/malformed-ecdsa.key b/adapter/internal/operator/gateway-api/testdata/tls/malformed-ecdsa.key new file mode 100644 index 0000000000..e6cc48732f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/malformed-ecdsa.key @@ -0,0 +1,3 @@ +-----BEGIN EC PRIVATE KEY----- +SGVsbG8gd29ybGQK +-----END EC PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/malformed-encoding.pem b/adapter/internal/operator/gateway-api/testdata/tls/malformed-encoding.pem new file mode 100644 index 0000000000..9a02198b43 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/malformed-encoding.pem @@ -0,0 +1 @@ +SGVsbG8gd29ybGQK diff --git a/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs1.key b/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs1.key new file mode 100644 index 0000000000..02ef0ad96f --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs1.key @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +SGVsbG8gd29ybGQK +-----END RSA PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs8.key b/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs8.key new file mode 100644 index 0000000000..18d998a6e9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/malformed-pkcs8.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +SGVsbG8gd29ybGQK +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-san.pem b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-san.pem new file mode 100644 index 0000000000..3dfef07bcb --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-san.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHzCCAgegAwIBAgIUWL1B/R492wfVLSDYPTpACUMQTiMwDQYJKoZIhvcNAQEL +BQAwEzERMA8GA1UEAwwIVGVzdCBJbmMwHhcNMjQwMjI5MDkzMDEwWhcNMzQwMjI2 +MDkzMDEwWjATMREwDwYDVQQDDAhUZXN0IEluYzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMpoxyIyxqyqk/s1BCrUhbH0k+PyOGdb1sI5EazA/p9BpmSI +zX99ViqFuha+gf1Q0Ght/Wh5Fy4UKzcgo4Jv3qd3r82q+M0VojAj7RBajiwsPSDV +u6hNiy4CE9Ic2abqmh1HMlbQnaYafXkZoRhvCH3enZ1h2tfRprmwa+vYO3HlLk9k +x2Z3xcXIaePNoBOe71U9t/gsKakjnIk7rV0S//BW6YYEYlHRxTbSeoyqsSC0ImL/ +gT+0dm/rmtU0e1LVUMGzQI/BFMqD/hUgSrweH0t7BjFOsIK2S62loQ87HQOx5ntV +aPO4C8TAdxheUcCscoUQ233tsMo9qPy6paQTfgMCAwEAAaNrMGkwHQYDVR0OBBYE +FLxFSbDoGbAEZAHoayV/xzIHMTu5MB8GA1UdIwQYMBaAFLxFSbDoGbAEZAHoayV/ +xzIHMTu5MA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILZm9vLmJhci5jb20w +DQYJKoZIhvcNAQELBQADggEBAJ6zTuAm5LPUv1A6Y6ASzRqepEfzHJb2AdBKQKG4 +oPqQlqbs2HyILGjGv//h8YU17LKV6W0fQ7bvUlHTiIdlh3mBL838r1cLGO2LaUf1 +JNhtKY+kT9I+KxV1ZPkrASLb2/6673bOyOdTU4D6OSPsywtvZ7qWM564hm2qw62G +2SgiC4+F61/BkcvG3XRhhAcLpCxyyrzdAuXsnhOHHLziJZ9byLTceWoGnIPk8qA5 +yKpnsH2LPYPUeY1HxNTNTUc0+BQpjikqPmdSyYGi4EUwBEhMVYZBhJLjmaEL3Dee +7zt7DFeERauy/uHL+LteokP0E7eKgAk+amoJxY+PYv2jMZk= +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-wildcard.pem b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-wildcard.pem new file mode 100644 index 0000000000..a4f1c00ce6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert-wildcard.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDJDCCAgygAwIBAgIUSrSbKLf0bLEGogWx/gCw2GCtvxEwDQYJKoZIhvcNAQEL +BQAwEzERMA8GA1UEAwwIVGVzdCBJbmMwHhcNMjQwMjI5MDkzMDEwWhcNMzQwMjI2 +MDkzMDEwWjATMREwDwYDVQQDDAhUZXN0IEluYzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAK1JzjPIiWg3qoHSrAddkeJjaMP9iyMTi/BPo9cJPoRE8Zi7 +WapUrXL/9A9r+jHMyGIZNZNdcZ5cY2Xv0LP8ZxVy2lk0+3wtYzHnpGYGVtyq2tet +DhFsiPl82YRjC0mzWa4QMz4SXzFHNgIDpRdhfrovmseuWGQE8pV4Ud9UK/SKilMO +qtB5JirLP2Vs5T1oWicWLQvfbGwv7sFDdr9bG0XtSQp17HSgo50SDE5+BjSmptFw +1VcKLlqahMXBDDiorgwhIhGGKpESeM0P7bFhVmkM3ash2xSTBuFUBDlE4JOahzws +DXrupUhDdS1hdc2fPrjhFAlJfWEYZ6BlZjysiVUCAwEAAaNwMG4wHQYDVR0OBBYE +FBQufK1LibuVm9Ts/VjBx8L3uiNeMB8GA1UdIwQYMBaAFBQufK1LibuVm9Ts/VjB +x8L3uiNeMA8GA1UdEwEB/wQFMAMBAf8wGwYDVR0RBBQwEoIBKoINKi5leGFtcGxl +LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAfPS41agevWMV3ZXt0COFG3uYfPFXnVw6 +P41p9O6GkdYsuqFvPeTyyH2/dAIKKwSzM/pthg8Kn8LZlmJyFNnA1stJxn5XdbV1 +pPqj8UvYCBzyjMIqmRyoiyLiQlbohMa0UdEBKcH/PdLE9K8TGJrZgoGXqq1WmitD +3vcPjSePKEiUJVS9lChySs0KY5B+hUQD0Jj6npFD4Zk0xqdxh0rWueCqq7vjqEYz +pj4Pwrufn1PBTmfxMuV/UJV5ebikeuZP35kWzLR7ZWAL7wuDdWp/6o4yk3QLanDQ +CwgCFcX+ss+0VyuM3YerTOUU8QVJJx4UZANZx6+43pdJZOcntPZ4CA== +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert.pem b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert.pem new file mode 100644 index 0000000000..84a712c489 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDTCCAfWgAwIBAgIUEFMhP9eJ9XABWsQ5Zm6bRk2cLNQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZm9vLmJhci5jb20wHhcNMjQwMjI5MDkzMDEwWhcNMzQw +MjI2MDkzMDEwWjAWMRQwEgYDVQQDDAtmb28uYmFyLmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAJlI6YxE9ZkCPs6pCQxbrCmeix9P5Dfx9Ru5LD4T +JmdW7IKdtQV/wfLmtsu76B+aTdCjWe0BTfeOORBbR3cPAG6ElQLiclQUrun3q+gs +JpK+I7cI+j5w8I68XH5WQ7rVUtbwHpqbw+cVnBqIUOLiIatjIf7KX51M1uF9cfEH +E4DncH6rbr59/RFZBskhxs5OzwJIfohkyvFweuTwxK/VpjI+7Oc48ABCXpNO9D/q +Eh+rOaujAMcXgHQITkCiiUTcUo6LSH9vLZPtaqfzoZq6nhMqpW65E1pAwF3jyTTx +a5I8JcfSFjkieZ20LUQMn7M8U4xHjao/gvH0CAfdB7RLU2sCAwEAAaNTMFEwHQYD +VR0OBBYEFORCE8u/1DDW7ihZp7cx9tSmPm6OMB8GA1UdIwQYMBaAFORCE8u/1DDW +7ihZp7cx9tSmPm6OMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AFt53zjsqTaH5a8E2chvmWAgCrxRK8bVLMxiwNGjbmEPRz+w6Nx+k0A8KEcIDsKc +SXci58u4oWbtVJBlza/ZujHR6P1BnOpl+aoy784bbd4P39wTLoXfMfbBCm1vevh9 +AJKnw2Ztqr4mkbLcxEqlq3sBLFAS9sQLnKNCe2cGLdTp2boG+qcgyQgCIM6f8EMv +WPifCMMGuzK/GRF4bPO/YF48ewDu3Ueih0XXdUAOE9CtUa8NIhc1UPaOzPrtYVqr +ZOGkv/Ku+B78h8SEsO9XrQcuwbOnJx6KtR+aeykvApXCQ3fZC/bYKAAR+P8AJoQZ +bwIUmXi4gj5m+bKPhe+ir+E= +-----END CERTIFICATE----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-san.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-san.key new file mode 100644 index 0000000000..4a103794b7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-san.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKaMciMsasqpP7 +NQQq1IWx9JPj8jhnW9bCORGswP6fQaZkiM1/fVYqhboWvoH9UNBobf1oeRcuFCs3 +IKOCb96nd6/NqvjNFaIwI+0QWo4sLD0g1buoTYsuAhPSHNmm6podRzJW0J2mGn15 +GaEYbwh93p2dYdrX0aa5sGvr2Dtx5S5PZMdmd8XFyGnjzaATnu9VPbf4LCmpI5yJ +O61dEv/wVumGBGJR0cU20nqMqrEgtCJi/4E/tHZv65rVNHtS1VDBs0CPwRTKg/4V +IEq8Hh9LewYxTrCCtkutpaEPOx0DseZ7VWjzuAvEwHcYXlHArHKFENt97bDKPaj8 +uqWkE34DAgMBAAECggEAOh5v32/5ACdSqnUEp0+yu/BgbrlkXLlQJyC/5HzeH9h3 +8LIfX5YbocYYMuOs65HC5dl/Q/pRH5gG+KNUj+WTZz7EqieHnUuy8GPAdEusOx7Q +xpbYdXEen61VB6XlETqX2gRRfgAU5FcC5Dzpttxa8dl0JHauof2/A96WfnfuSbtk +GlCLQdMg+pstPWq3rXeUQ09LFQdOCsZsUz76QzlxXo2YlCDcIQVprT0YlkZ5iIFM +2HkFtaJGBW6bTNjiTyqSQTkz35AIk8mIZbuqjLZC65WKpwgbscVMxl5eRRuSQ06o +BFnZYJDLeeDmD7yV2vFS14WNDnohPyw9dcAzRO614QKBgQDoSJ7InpRExuud2Cij +36xMfwcXpmfh63EBDhmgw6L4EhJ25zKm1ZzKL+ULAzLos/Y3w69Rf1X6miHZ93Gk +EjHFGKorY8TRegJny/uhUNqEc0KzB2BhSucXFGs6XTWM2qGXGRK2ecepJjTX7uy3 +Z2Pq/wD396BDb7gt+L71PSWD2QKBgQDfE064ojNLQ2yszXmL283TzI+Cadcs28GV +f+5cV97QSEimSsgK9hrg7PzdcEk7jkAye1GgRldqEoH+9lrIH8ltTIkiw/F8lvkw +3UM9XvR9MKPEGKuxxFo0I/ksxtaHcSALpkFQMgakC5386XgOiIcOaM7hib3hrWBx +HKpZuq0TOwKBgAg3OVaDNV9RYdgNYzKYoLF79LZDbn0xJKgS5ZkoEkWP7hNCjioU +eB02oHVJQa21X8oNI9BQHqxkczoQZHaXJieAfdRXDQkTta4SKu5Du9bfdMZ8Rk5q +pc3NLRW9Yj1/JiQAAQdvOz6iWVAh5UF/aKtRJamfmo2IEA2gNLDb3s3RAoGAUcXq +HZSX6QcWV2IbJEG2+eUK1mumDTOhiwQShJdRfliBCTRwkFQXFkzJhQMcVVCYB+lc +yrv+X0vZi3UTYjkExaRAwRXC+ED/qAH0HBMq3Jlv7vp8NfYcevCU7u0UxyGY9axE +VPmBpbD8gTG0aN9zYrzY2aR8jrXXnJ89cxcTSvECgYEAvtFZMBh+noQxMttnh0/L +LwrAjedj8pWOAPzCdDFxvgzVp1mdi7GxbH4noEhcPhTlZ53Wfi3oOB4uhcGdXWpk +HbjIWVUWczRKh15dICHNcbykU55T6p1jpPyYXsJfmonCWVYt9yppsLJ5wCu648J3 +QT4FZnPrtsI8mIcPI24A4xU= +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-wildcard.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-wildcard.key new file mode 100644 index 0000000000..4b212d5fa8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1-wildcard.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtSc4zyIloN6qB +0qwHXZHiY2jD/YsjE4vwT6PXCT6ERPGYu1mqVK1y//QPa/oxzMhiGTWTXXGeXGNl +79Cz/GcVctpZNPt8LWMx56RmBlbcqtrXrQ4RbIj5fNmEYwtJs1muEDM+El8xRzYC +A6UXYX66L5rHrlhkBPKVeFHfVCv0iopTDqrQeSYqyz9lbOU9aFonFi0L32xsL+7B +Q3a/WxtF7UkKdex0oKOdEgxOfgY0pqbRcNVXCi5amoTFwQw4qK4MISIRhiqREnjN +D+2xYVZpDN2rIdsUkwbhVAQ5ROCTmoc8LA167qVIQ3UtYXXNnz644RQJSX1hGGeg +ZWY8rIlVAgMBAAECggEAJjN4jSreu8ZVxNPcD62dfPJcRKSOuTdUaLAt1rhXe1I1 +jm1yyV1sdUcelw2/NJ5H6HRVPrzhUL9ePDkfYaY5VV2h6/uFmyzoSrado84u9OuH +Xfds/ANYuONAknJBKUir8vboTP1A1fy0V8JU8TARf1sCPJXfL4F1tuS0685C+F/6 +djrRq8ijw7wBgfptXeecVB1uqrKflKU4i86gCX6fC9KIa4YOHUUrR/d1epe1KujW +5JZtbgIrsWT8Yk/m47R6Azk1UotZVxKbZyCYsQB9iTfLSXU9mIC7uPuYw9qwBiRo +YR00BVI3BtUrcvoQtL3P7jWA/f0EHmOKCRLGOaNFkQKBgQDyB+MSSqbWhuwXVc2g +RViOUqQtthqku+9kZce1O8NZTDjAoCP7K5TIs8RoZLnIS12r4V3X9B4NkkxDwBvj +ngM1MBppNlKrRvGTLIqtsu9dsSzzNueZZLAtovIg4aEP2cBLmoc4XcDQiDdU/EjB +TypTbDm40z9pBl3EBRXdFCxlaQKBgQC3SjejSRC3vAuA8QNeX2RyOB7qU5M/5EMJ +FeNB3KNzper/b17bgumhG8XE+fw9OhyWg8kipHbicddO3UsNYF6wOpTf5XUmPhJJ +rxX3ibB3imjuMnrBBc9oEQYrHYe7e8hqb8e8tsrl23t1zNTYfEAV/mgGGvSKLkb+ +x4zkfDTrDQKBgQDhw1Ggoln2Cmz3Z2Yjdgwg1h2nNhKsT1ICyJ39A/44sczOgZKO +nBxxCp7O8gYKO6LozZQH+QJ/jlPtbqmVLLO5TWGVesKqWcbFjereE+cL8lUHmdN3 +VmCwcSqCWXUvn+Kroz026t0Mp8NVVu9Wwk2s+2rsaMDharrUOhom2sZ+4QKBgQCp +QYDlaShl8uFQnb91m3NfQlHSI4E7o+b/cdXdUQkjV1kNrmOTI23pF4NmpU53n70c +hO/s7KDX9TZVHmcbIB3wcPhfVT38JZ+vqV4Iq35otUi2hajzrBDeUbI3iFp6GBF3 +sGdq7gWpgyecHZ2DmCF57edTk/qzy45F4jJKICSh1QKBgDoHRNBc84OghC3mnpkq +h/dERHHhseCqFa79Z/47hGM02YOhm8PdkIFBubwGZYO8slx+OLFmg9hdf1vahJBg +G36uNic5VarCL4x3RsMiZCwmokP9AR52D/vvnxR47jbP/x+4B2qc6fwJVAQNhFg0 +KtOqp0kWe61o2COcNV/PVxj3 +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1.key new file mode 100644 index 0000000000..d07adaf014 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCZSOmMRPWZAj7O +qQkMW6wpnosfT+Q38fUbuSw+EyZnVuyCnbUFf8Hy5rbLu+gfmk3Qo1ntAU33jjkQ +W0d3DwBuhJUC4nJUFK7p96voLCaSviO3CPo+cPCOvFx+VkO61VLW8B6am8PnFZwa +iFDi4iGrYyH+yl+dTNbhfXHxBxOA53B+q26+ff0RWQbJIcbOTs8CSH6IZMrxcHrk +8MSv1aYyPuznOPAAQl6TTvQ/6hIfqzmrowDHF4B0CE5AoolE3FKOi0h/by2T7Wqn +86Gaup4TKqVuuRNaQMBd48k08WuSPCXH0hY5InmdtC1EDJ+zPFOMR42qP4Lx9AgH +3Qe0S1NrAgMBAAECgf96g/PYxvaZy4Bn1NrJBd8LZOgcbZc2gnx6Ikv/UXZ0Nhlx +EYiKjeFjV6HW5oEXrZ+/mPf4dusVaL74U9VojuPJceAeRrjL3eOhbHtcx+0gcGLg +XxF9TRap65TuUd1ai04hGwYf75sbP7RKbPivwZgUAe0CpVufchnXqrsYr8pJY51O +WVkSqz4RY9Wm0k5G1JFyIyEC9umDlujcHNtRVmafkNfAtCliPrFKc/n9nJfO6RFP +7g7V/IvqnuIr7PE3Gn6PaT+Bgg448td5JzPpTA5ZBcBo2orz/kxYPFpr/gPUBqQd +o6nWqw76ZxwPldwLhJ+XYNX7ouctUSCL9u77fyECgYEAyv7Dlxf+KQlfDwmo/r1T +0L1ZnH472hiIedSheyVBHbEVTSmr42BRljW4DbRcQM4VcxxDkGrR76RGe9og6mzc +Rv8+VlCX2+qy9p5m6ZXrbAw30zC/5mPkRWubhUhi+9eCMZa/hAI/RFv269DrBD2/ +6kg0F8X0O/6wI+WpatK3W0cCgYEAwU9A6bJpfaXKKXPGmOG/nUxTyzyqYjKNZJd/ +9GhEnuGjK5CAEVPJa8kZvfQzlWmwZafq0DhqI8vLaFCDf8Y8E99Man3GWhUb3V/J +qNQQS356NCfZw7lxoKKBIvT6cwihTnsE/R4HCsal2wwN4Zl9HWNBgamS7TLkz1Lh +fwRDkL0CgYBZ49j+5nwA9gpnIVL5gyNDcyXke63LVUPSF0tuub+NA2a4ZbSdGoDm +cGFRiEW12Mx8zc5JfFP8t5USsTQUOyKMOekD1ep5UwPu25Qc6et3TC3IUnUX87IY +357dtYFHnlYj2WpzbX9QqRy9riT0GtgKSfDvehQ+IPkk3EVabXcObwKBgGGxC70O +zQESp/gK6nKYo5161V4Aapr1sT8E0UVS7Fre7Pc3L4GSNliiaL/2iZsYrmyxT5mq +6PjuJP2yssIADJx+XL/0kCk2Qb6+icsoYJPGdzukaAjhzuq/NUPVSjyYP+zJftvs +MOLhQTBSBzHbv776SkCc0gPNlJSx7gOixAKBAoGAJBGUn3e5AfComA1E1DxRyLZj +T0PkACePjD+xkFJhwDhCgswhm4UJg1fAo1hBQRFttpVC/uBLck18MEAI1vdfSyPv +kSg5kVqPju3sg39TMgOVewjP3E3AMQGug1P71Yk2zYJPlh95dLU5HJVno6ovB+Pm +Lqy+Mzx3wkF0d8ePXQ4= +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-san.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-san.key new file mode 100644 index 0000000000..4a103794b7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-san.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKaMciMsasqpP7 +NQQq1IWx9JPj8jhnW9bCORGswP6fQaZkiM1/fVYqhboWvoH9UNBobf1oeRcuFCs3 +IKOCb96nd6/NqvjNFaIwI+0QWo4sLD0g1buoTYsuAhPSHNmm6podRzJW0J2mGn15 +GaEYbwh93p2dYdrX0aa5sGvr2Dtx5S5PZMdmd8XFyGnjzaATnu9VPbf4LCmpI5yJ +O61dEv/wVumGBGJR0cU20nqMqrEgtCJi/4E/tHZv65rVNHtS1VDBs0CPwRTKg/4V +IEq8Hh9LewYxTrCCtkutpaEPOx0DseZ7VWjzuAvEwHcYXlHArHKFENt97bDKPaj8 +uqWkE34DAgMBAAECggEAOh5v32/5ACdSqnUEp0+yu/BgbrlkXLlQJyC/5HzeH9h3 +8LIfX5YbocYYMuOs65HC5dl/Q/pRH5gG+KNUj+WTZz7EqieHnUuy8GPAdEusOx7Q +xpbYdXEen61VB6XlETqX2gRRfgAU5FcC5Dzpttxa8dl0JHauof2/A96WfnfuSbtk +GlCLQdMg+pstPWq3rXeUQ09LFQdOCsZsUz76QzlxXo2YlCDcIQVprT0YlkZ5iIFM +2HkFtaJGBW6bTNjiTyqSQTkz35AIk8mIZbuqjLZC65WKpwgbscVMxl5eRRuSQ06o +BFnZYJDLeeDmD7yV2vFS14WNDnohPyw9dcAzRO614QKBgQDoSJ7InpRExuud2Cij +36xMfwcXpmfh63EBDhmgw6L4EhJ25zKm1ZzKL+ULAzLos/Y3w69Rf1X6miHZ93Gk +EjHFGKorY8TRegJny/uhUNqEc0KzB2BhSucXFGs6XTWM2qGXGRK2ecepJjTX7uy3 +Z2Pq/wD396BDb7gt+L71PSWD2QKBgQDfE064ojNLQ2yszXmL283TzI+Cadcs28GV +f+5cV97QSEimSsgK9hrg7PzdcEk7jkAye1GgRldqEoH+9lrIH8ltTIkiw/F8lvkw +3UM9XvR9MKPEGKuxxFo0I/ksxtaHcSALpkFQMgakC5386XgOiIcOaM7hib3hrWBx +HKpZuq0TOwKBgAg3OVaDNV9RYdgNYzKYoLF79LZDbn0xJKgS5ZkoEkWP7hNCjioU +eB02oHVJQa21X8oNI9BQHqxkczoQZHaXJieAfdRXDQkTta4SKu5Du9bfdMZ8Rk5q +pc3NLRW9Yj1/JiQAAQdvOz6iWVAh5UF/aKtRJamfmo2IEA2gNLDb3s3RAoGAUcXq +HZSX6QcWV2IbJEG2+eUK1mumDTOhiwQShJdRfliBCTRwkFQXFkzJhQMcVVCYB+lc +yrv+X0vZi3UTYjkExaRAwRXC+ED/qAH0HBMq3Jlv7vp8NfYcevCU7u0UxyGY9axE +VPmBpbD8gTG0aN9zYrzY2aR8jrXXnJ89cxcTSvECgYEAvtFZMBh+noQxMttnh0/L +LwrAjedj8pWOAPzCdDFxvgzVp1mdi7GxbH4noEhcPhTlZ53Wfi3oOB4uhcGdXWpk +HbjIWVUWczRKh15dICHNcbykU55T6p1jpPyYXsJfmonCWVYt9yppsLJ5wCu648J3 +QT4FZnPrtsI8mIcPI24A4xU= +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-wildcard.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-wildcard.key new file mode 100644 index 0000000000..4b212d5fa8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8-wildcard.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtSc4zyIloN6qB +0qwHXZHiY2jD/YsjE4vwT6PXCT6ERPGYu1mqVK1y//QPa/oxzMhiGTWTXXGeXGNl +79Cz/GcVctpZNPt8LWMx56RmBlbcqtrXrQ4RbIj5fNmEYwtJs1muEDM+El8xRzYC +A6UXYX66L5rHrlhkBPKVeFHfVCv0iopTDqrQeSYqyz9lbOU9aFonFi0L32xsL+7B +Q3a/WxtF7UkKdex0oKOdEgxOfgY0pqbRcNVXCi5amoTFwQw4qK4MISIRhiqREnjN +D+2xYVZpDN2rIdsUkwbhVAQ5ROCTmoc8LA167qVIQ3UtYXXNnz644RQJSX1hGGeg +ZWY8rIlVAgMBAAECggEAJjN4jSreu8ZVxNPcD62dfPJcRKSOuTdUaLAt1rhXe1I1 +jm1yyV1sdUcelw2/NJ5H6HRVPrzhUL9ePDkfYaY5VV2h6/uFmyzoSrado84u9OuH +Xfds/ANYuONAknJBKUir8vboTP1A1fy0V8JU8TARf1sCPJXfL4F1tuS0685C+F/6 +djrRq8ijw7wBgfptXeecVB1uqrKflKU4i86gCX6fC9KIa4YOHUUrR/d1epe1KujW +5JZtbgIrsWT8Yk/m47R6Azk1UotZVxKbZyCYsQB9iTfLSXU9mIC7uPuYw9qwBiRo +YR00BVI3BtUrcvoQtL3P7jWA/f0EHmOKCRLGOaNFkQKBgQDyB+MSSqbWhuwXVc2g +RViOUqQtthqku+9kZce1O8NZTDjAoCP7K5TIs8RoZLnIS12r4V3X9B4NkkxDwBvj +ngM1MBppNlKrRvGTLIqtsu9dsSzzNueZZLAtovIg4aEP2cBLmoc4XcDQiDdU/EjB +TypTbDm40z9pBl3EBRXdFCxlaQKBgQC3SjejSRC3vAuA8QNeX2RyOB7qU5M/5EMJ +FeNB3KNzper/b17bgumhG8XE+fw9OhyWg8kipHbicddO3UsNYF6wOpTf5XUmPhJJ +rxX3ibB3imjuMnrBBc9oEQYrHYe7e8hqb8e8tsrl23t1zNTYfEAV/mgGGvSKLkb+ +x4zkfDTrDQKBgQDhw1Ggoln2Cmz3Z2Yjdgwg1h2nNhKsT1ICyJ39A/44sczOgZKO +nBxxCp7O8gYKO6LozZQH+QJ/jlPtbqmVLLO5TWGVesKqWcbFjereE+cL8lUHmdN3 +VmCwcSqCWXUvn+Kroz026t0Mp8NVVu9Wwk2s+2rsaMDharrUOhom2sZ+4QKBgQCp +QYDlaShl8uFQnb91m3NfQlHSI4E7o+b/cdXdUQkjV1kNrmOTI23pF4NmpU53n70c +hO/s7KDX9TZVHmcbIB3wcPhfVT38JZ+vqV4Iq35otUi2hajzrBDeUbI3iFp6GBF3 +sGdq7gWpgyecHZ2DmCF57edTk/qzy45F4jJKICSh1QKBgDoHRNBc84OghC3mnpkq +h/dERHHhseCqFa79Z/47hGM02YOhm8PdkIFBubwGZYO8slx+OLFmg9hdf1vahJBg +G36uNic5VarCL4x3RsMiZCwmokP9AR52D/vvnxR47jbP/x+4B2qc6fwJVAQNhFg0 +KtOqp0kWe61o2COcNV/PVxj3 +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8.key b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8.key new file mode 100644 index 0000000000..d07adaf014 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tls/rsa-pkcs8.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCZSOmMRPWZAj7O +qQkMW6wpnosfT+Q38fUbuSw+EyZnVuyCnbUFf8Hy5rbLu+gfmk3Qo1ntAU33jjkQ +W0d3DwBuhJUC4nJUFK7p96voLCaSviO3CPo+cPCOvFx+VkO61VLW8B6am8PnFZwa +iFDi4iGrYyH+yl+dTNbhfXHxBxOA53B+q26+ff0RWQbJIcbOTs8CSH6IZMrxcHrk +8MSv1aYyPuznOPAAQl6TTvQ/6hIfqzmrowDHF4B0CE5AoolE3FKOi0h/by2T7Wqn +86Gaup4TKqVuuRNaQMBd48k08WuSPCXH0hY5InmdtC1EDJ+zPFOMR42qP4Lx9AgH +3Qe0S1NrAgMBAAECgf96g/PYxvaZy4Bn1NrJBd8LZOgcbZc2gnx6Ikv/UXZ0Nhlx +EYiKjeFjV6HW5oEXrZ+/mPf4dusVaL74U9VojuPJceAeRrjL3eOhbHtcx+0gcGLg +XxF9TRap65TuUd1ai04hGwYf75sbP7RKbPivwZgUAe0CpVufchnXqrsYr8pJY51O +WVkSqz4RY9Wm0k5G1JFyIyEC9umDlujcHNtRVmafkNfAtCliPrFKc/n9nJfO6RFP +7g7V/IvqnuIr7PE3Gn6PaT+Bgg448td5JzPpTA5ZBcBo2orz/kxYPFpr/gPUBqQd +o6nWqw76ZxwPldwLhJ+XYNX7ouctUSCL9u77fyECgYEAyv7Dlxf+KQlfDwmo/r1T +0L1ZnH472hiIedSheyVBHbEVTSmr42BRljW4DbRcQM4VcxxDkGrR76RGe9og6mzc +Rv8+VlCX2+qy9p5m6ZXrbAw30zC/5mPkRWubhUhi+9eCMZa/hAI/RFv269DrBD2/ +6kg0F8X0O/6wI+WpatK3W0cCgYEAwU9A6bJpfaXKKXPGmOG/nUxTyzyqYjKNZJd/ +9GhEnuGjK5CAEVPJa8kZvfQzlWmwZafq0DhqI8vLaFCDf8Y8E99Man3GWhUb3V/J +qNQQS356NCfZw7lxoKKBIvT6cwihTnsE/R4HCsal2wwN4Zl9HWNBgamS7TLkz1Lh +fwRDkL0CgYBZ49j+5nwA9gpnIVL5gyNDcyXke63LVUPSF0tuub+NA2a4ZbSdGoDm +cGFRiEW12Mx8zc5JfFP8t5USsTQUOyKMOekD1ep5UwPu25Qc6et3TC3IUnUX87IY +357dtYFHnlYj2WpzbX9QqRy9riT0GtgKSfDvehQ+IPkk3EVabXcObwKBgGGxC70O +zQESp/gK6nKYo5161V4Aapr1sT8E0UVS7Fre7Pc3L4GSNliiaL/2iZsYrmyxT5mq +6PjuJP2yssIADJx+XL/0kCk2Qb6+icsoYJPGdzukaAjhzuq/NUPVSjyYP+zJftvs +MOLhQTBSBzHbv776SkCc0gPNlJSx7gOixAKBAoGAJBGUn3e5AfComA1E1DxRyLZj +T0PkACePjD+xkFJhwDhCgswhm4UJg1fAo1hBQRFttpVC/uBLck18MEAI1vdfSyPv +kSg5kVqPju3sg39TMgOVewjP3E3AMQGug1P71Yk2zYJPlh95dLU5HJVno6ovB+Pm +Lqy+Mzx3wkF0d8ePXQ4= +-----END PRIVATE KEY----- diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.in.yaml new file mode 100644 index 0000000000..a10119662d --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.in.yaml @@ -0,0 +1,32 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + hostname: foo.com + port: 90 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.out.yaml new file mode 100644 index 0000000000..95aec26787 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-attaching-to-gateway.out.yaml @@ -0,0 +1,112 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tls + port: 90 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10090 + name: tls + protocol: TLS + servicePort: 90 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-1 + port: 10090 + tls: + passthrough: + snis: + - foo.com diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.in.yaml new file mode 100644 index 0000000000..e7d44bece2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.in.yaml @@ -0,0 +1,48 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 91 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "foo.com" + rules: + - backendRefs: + - name: service-1 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "bar.com" + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.out.yaml new file mode 100644 index 0000000000..6cd2d930bd --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-multiple.out.yaml @@ -0,0 +1,162 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 91 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10091 + name: tls + protocol: TLS + servicePort: 91 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + hostnames: + - foo.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-2 + namespace: default + spec: + hostnames: + - bar.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-1 + port: 10091 + tls: + passthrough: + snis: + - foo.com + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-2/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-2 + port: 10091 + tls: + passthrough: + snis: + - bar.com diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml new file mode 100644 index 0000000000..f37773dbd8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.in.yaml @@ -0,0 +1,29 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 90 + allowedRoutes: + namespaces: + from: All +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml new file mode 100644 index 0000000000..a75fdbe5ef --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-not-attaching-to-gateway-with-no-mode.out.yaml @@ -0,0 +1,81 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 90 + protocol: TLS + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must have TLS set when protocol is TLS. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TCPRoute + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml new file mode 100644 index 0000000000..00e85a8737 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.in.yaml @@ -0,0 +1,78 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + hostname: foo.com + port: 90 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + namespace: test-service-namespace + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: test-service-namespace + name: service-1 + spec: + clusterIP: 1.1.1.1 + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-service-1 + namespace: test-service-namespace + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - "7.7.7.7" + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: test-service-namespace + name: referencegrant-1 + spec: + from: + - group: gateway.networking.k8s.io + kind: TLSRoute + namespace: default + to: + - group: "" + kind: Service diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml new file mode 100644 index 0000000000..e62a5aab00 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-backendref-in-other-namespace-allowed-by-refgrant.out.yaml @@ -0,0 +1,113 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tls + port: 90 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10090 + name: tls + protocol: TLS + servicePort: 90 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + namespace: test-service-namespace + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-1 + port: 10090 + tls: + passthrough: + snis: + - foo.com diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.in.yaml new file mode 100644 index 0000000000..76f60b0752 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.in.yaml @@ -0,0 +1,31 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 91 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.out.yaml new file mode 100644 index 0000000000..919955fee1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-hostname.out.yaml @@ -0,0 +1,111 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 91 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10091 + name: tls + protocol: TLS + servicePort: 91 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-1 + port: 10091 + tls: + passthrough: + snis: + - '*' diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.in.yaml new file mode 100644 index 0000000000..621f046a9b --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.in.yaml @@ -0,0 +1,33 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + port: 91 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: All +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + hostnames: + - "foo.com" + rules: + - backendRefs: + - name: service-1 + port: 8080 diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.out.yaml new file mode 100644 index 0000000000..f3ef27913a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-empty-listener-hostname.out.yaml @@ -0,0 +1,113 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: tls + port: 91 + protocol: TLS + tls: + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/tls + ports: + - containerPort: 10091 + name: tls + protocol: TLS + servicePort: 91 + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + hostnames: + - foo.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + tcp: + - address: 0.0.0.0 + destination: + name: tlsroute/default/tlsroute-1/rule/-1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTPS + weight: 1 + name: envoy-gateway/gateway-1/tls/tlsroute-1 + port: 10091 + tls: + passthrough: + snis: + - foo.com diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml new file mode 100644 index 0000000000..26a91e256a --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.in.yaml @@ -0,0 +1,44 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: tls + protocol: TLS + hostname: foo.com + tls: + mode: Passthrough + certificateRefs: + - name: tls-secret-1 + port: 90 + allowedRoutes: + namespaces: + from: All +tlsRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + namespace: default + name: tlsroute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 +secrets: + - apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: tls-secret-1 + type: kubernetes.io/tls + data: + tls.crt: Zm9vCg== + tls.key: YmFyCg== diff --git a/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml new file mode 100644 index 0000000000..92b48bfcac --- /dev/null +++ b/adapter/internal/operator/gateway-api/testdata/tlsroute-with-listener-both-passthrough-and-cert-data.out.yaml @@ -0,0 +1,86 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: foo.com + name: tls + port: 90 + protocol: TLS + tls: + certificateRefs: + - group: null + kind: null + name: tls-secret-1 + mode: Passthrough + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Listener must not have TLS certificate refs set for TLS mode Passthrough. + reason: Invalid + status: "False" + type: Programmed + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: tls + supportedKinds: + - group: gateway.networking.k8s.io + kind: TLSRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + metadata: + labels: + apk.wso2.com/owning-gateway-name: gateway-1 + apk.wso2.com/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +tlsRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: TLSRoute + metadata: + creationTimestamp: null + name: tlsroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: There are no ready listeners for this parent ref + reason: NoReadyListeners + status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: apk.wso2.com/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout diff --git a/adapter/internal/operator/gateway-api/tls.go b/adapter/internal/operator/gateway-api/tls.go new file mode 100644 index 0000000000..5cf277c978 --- /dev/null +++ b/adapter/internal/operator/gateway-api/tls.go @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "time" + + corev1 "k8s.io/api/core/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// validateTLSSecretData ensures the cert and key provided in a secret +// is not malformed and can be properly parsed +func validateTLSSecretsData(secrets []*corev1.Secret, host *v1.Hostname) error { + var publicKeyAlgorithm string + var parseErr error + + pkaSecretSet := make(map[string][]string) + for _, secret := range secrets { + certData := secret.Data[corev1.TLSCertKey] + + if err := validateCertificate(certData); err != nil { + return fmt.Errorf("%s/%s must contain valid %s and %s, unable to validate certificate in %s: %w", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSCertKey, err) + } + + certBlock, _ := pem.Decode(certData) + if certBlock == nil { + return fmt.Errorf("%s/%s must contain valid %s and %s, unable to decode pem data in %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSCertKey) + } + + cert, err := x509.ParseCertificate(certBlock.Bytes) + if err != nil { + return fmt.Errorf("%s/%s must contain valid %s and %s, unable to parse certificate in %s: %w", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSCertKey, err) + } + publicKeyAlgorithm = cert.PublicKeyAlgorithm.String() + + keyData := secret.Data[corev1.TLSPrivateKeyKey] + + keyBlock, _ := pem.Decode(keyData) + if keyBlock == nil { + return fmt.Errorf("%s/%s must contain valid %s and %s, unable to decode pem data in %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey) + } + + matchedFQDN, err := verifyHostname(cert, host) + if err != nil { + return fmt.Errorf("%s/%s must contain valid %s and %s, hostname %s does not match Common Name or DNS Names in the certificate %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, string(*host), corev1.TLSCertKey) + } + pkaSecretKey := fmt.Sprintf("%s/%s", publicKeyAlgorithm, matchedFQDN) + + // Check whether the public key algorithm and matched certificate FQDN in the referenced secrets are unique. + if matchedFQDN, ok := pkaSecretSet[pkaSecretKey]; ok { + return fmt.Errorf("%s/%s public key algorithm must be unique, matched certificate FQDN %s has a conflicting algorithm [%s]", + secret.Namespace, secret.Name, matchedFQDN, publicKeyAlgorithm) + + } + pkaSecretSet[pkaSecretKey] = matchedFQDN + + switch keyBlock.Type { + case "PRIVATE KEY": + _, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes) + if err != nil { + parseErr = fmt.Errorf("%s/%s must contain valid %s and %s, unable to parse PKCS8 formatted private key in %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey) + } + case "RSA PRIVATE KEY": + _, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + if err != nil { + parseErr = fmt.Errorf("%s/%s must contain valid %s and %s, unable to parse PKCS1 formatted private key in %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey) + } + case "EC PRIVATE KEY": + _, err := x509.ParseECPrivateKey(keyBlock.Bytes) + if err != nil { + parseErr = fmt.Errorf("%s/%s must contain valid %s and %s, unable to parse EC formatted private key in %s", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, corev1.TLSPrivateKeyKey) + } + default: + return fmt.Errorf("%s/%s must contain valid %s and %s, %s key format found in %s, supported formats are PKCS1, PKCS8 or EC", secret.Namespace, secret.Name, corev1.TLSCertKey, corev1.TLSPrivateKeyKey, keyBlock.Type, corev1.TLSPrivateKeyKey) + } + } + + return parseErr +} + +// verifyHostname checks if the listener Hostname matches any domain in the certificate, returns a list of matched hosts. +func verifyHostname(cert *x509.Certificate, host *v1.Hostname) ([]string, error) { + var matchedHosts []string + + if len(cert.DNSNames) > 0 { + matchedHosts = computeHosts(cert.DNSNames, host) + } else { + matchedHosts = computeHosts([]string{cert.Subject.CommonName}, host) + } + + if len(matchedHosts) > 0 { + return matchedHosts, nil + } + + return nil, x509.HostnameError{Certificate: cert, Host: string(*host)} +} + +func validateCertificate(data []byte) error { + block, _ := pem.Decode(data) + if block == nil { + return fmt.Errorf("pem decode failed") + } + certs, err := x509.ParseCertificates(block.Bytes) + if err != nil { + return err + } + now := time.Now() + roundedTime := now.Truncate(time.Minute) + + // TODO(amali) remove this round logic if any issue happens. + // it's added due to issue in gateway conformance test certificate + // Only add a minute if the current time is not exactly on the minute + if !now.Equal(roundedTime) { + roundedTime = roundedTime.Add(time.Minute) + } + for _, cert := range certs { + if roundedTime.After(cert.NotAfter) { + return fmt.Errorf("certificate is expired %v, now: %v", cert.NotAfter, roundedTime) + } + if roundedTime.Before(cert.NotBefore) { + return fmt.Errorf("certificate is not yet valid %v, now %v", cert.NotBefore, roundedTime) + } + } + return nil +} diff --git a/adapter/internal/operator/gateway-api/tls_test.go b/adapter/internal/operator/gateway-api/tls_test.go new file mode 100644 index 0000000000..8faaef876a --- /dev/null +++ b/adapter/internal/operator/gateway-api/tls_test.go @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "errors" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +const ( + secretName = "secret" + secretNamespace = "test" +) + +// createTestSecret creates a K8s tls secret using testdata +// see for more info +func createTestSecrets(t *testing.T, certFile, keyFile string) []*corev1.Secret { + t.Helper() + + certData, err := os.ReadFile(filepath.Join("testdata", "tls", certFile)) + require.NoError(t, err) + + keyData, err := os.ReadFile(filepath.Join("testdata", "tls", keyFile)) + require.NoError(t, err) + + return []*corev1.Secret{{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: secretNamespace, + }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSCertKey: certData, + corev1.TLSPrivateKeyKey: keyData, + }, + }} +} + +// TestValidateTLSSecretData ensures that we can properly validate the contents of a K8s tls secret. +// The test assumes the secret is valid and was able to be applied to a cluster. +func TestValidateTLSSecretsData(t *testing.T) { + type testCase struct { + Name string + CertFile string + KeyFile string + Domain v1.Hostname + ExpectedErr error + } + + testCases := []testCase{ + { + Name: "valid-rsa-pkcs1", + CertFile: "rsa-cert.pem", + KeyFile: "rsa-pkcs1.key", + Domain: "*", + ExpectedErr: nil, + }, + { + Name: "valid-rsa-pkcs8", + CertFile: "rsa-cert.pem", + KeyFile: "rsa-pkcs8.key", + Domain: "*", + ExpectedErr: nil, + }, + { + Name: "valid-rsa-san-domain", + CertFile: "rsa-cert-san.pem", + KeyFile: "rsa-pkcs8-san.key", + Domain: "foo.bar.com", + ExpectedErr: nil, + }, + { + Name: "valid-rsa-wildcard-domain", + CertFile: "rsa-cert-wildcard.pem", + KeyFile: "rsa-pkcs1-wildcard.key", + Domain: "foo.bar.com", + ExpectedErr: nil, + }, + { + Name: "valid-ecdsa-p256", + CertFile: "ecdsa-p256-cert.pem", + KeyFile: "ecdsa-p256.key", + Domain: "*", + ExpectedErr: nil, + }, + { + Name: "valid-ecdsa-p384", + CertFile: "ecdsa-p384-cert.pem", + KeyFile: "ecdsa-p384.key", + Domain: "*", + ExpectedErr: nil, + }, + { + Name: "malformed-cert-pem-encoding", + CertFile: "malformed-encoding.pem", + KeyFile: "rsa-pkcs8.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to validate certificate in tls.crt: pem decode failed"), + }, + { + Name: "malformed-key-pem-encoding", + CertFile: "rsa-cert.pem", + KeyFile: "malformed-encoding.pem", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to decode pem data in tls.key"), + }, + { + Name: "malformed-cert", + CertFile: "malformed-cert.pem", + KeyFile: "rsa-pkcs8.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to validate certificate in tls.crt: x509: malformed certificate"), + }, + { + Name: "malformed-pkcs8-key", + CertFile: "rsa-cert.pem", + KeyFile: "malformed-pkcs8.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to parse PKCS8 formatted private key in tls.key"), + }, + { + Name: "malformed-pkcs1-key", + CertFile: "rsa-cert.pem", + KeyFile: "malformed-pkcs1.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to parse PKCS1 formatted private key in tls.key"), + }, + { + Name: "malformed-ecdsa-key", + CertFile: "rsa-cert.pem", + KeyFile: "malformed-ecdsa.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, unable to parse EC formatted private key in tls.key"), + }, + { + Name: "invalid-key-type", + CertFile: "rsa-cert.pem", + KeyFile: "invalid-key-type.key", + Domain: "*", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, FOO key format found in tls.key, supported formats are PKCS1, PKCS8 or EC"), + }, + { + Name: "invalid-domain-cert", + CertFile: "rsa-cert-san.pem", + KeyFile: "rsa-pkcs8-san.key", + Domain: "*.example.com", + ExpectedErr: errors.New("test/secret must contain valid tls.crt and tls.key, hostname *.example.com does not match Common Name or DNS Names in the certificate tls.crt"), + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + secrets := createTestSecrets(t, tc.CertFile, tc.KeyFile) + require.NotNil(t, secrets) + err := validateTLSSecretsData(secrets, &tc.Domain) + if tc.ExpectedErr == nil { + require.NoError(t, err) + } else { + require.EqualError(t, err, tc.ExpectedErr.Error()) + } + }) + } +} + +func TestValidateCertificate(t *testing.T) { + type testCase struct { + Name string + CertFile string + ExpectedErr error + } + + testCases := []testCase{ + { + Name: "valid-rsa-cert", + CertFile: "rsa-cert.pem", + ExpectedErr: nil, + }, + { + Name: "valid-ecdsa-p256-cert", + CertFile: "ecdsa-p256-cert.pem", + ExpectedErr: nil, + }, + { + Name: "valid-ecdsa-p384-cert", + CertFile: "ecdsa-p384-cert.pem", + ExpectedErr: nil, + }, + { + Name: "malformed-cert", + CertFile: "malformed-cert.pem", + ExpectedErr: errors.New("x509: malformed certificate"), + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + certData, err := os.ReadFile(filepath.Join("testdata", "tls", tc.CertFile)) + require.NoError(t, err) + err = validateCertificate(certData) + if tc.ExpectedErr == nil { + require.NoError(t, err) + } else { + require.EqualError(t, err, tc.ExpectedErr.Error()) + } + }) + } +} diff --git a/adapter/internal/operator/gateway-api/translator.go b/adapter/internal/operator/gateway-api/translator.go new file mode 100644 index 0000000000..90b54a4213 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator.go @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "golang.org/x/exp/maps" + "k8s.io/apimachinery/pkg/runtime/schema" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +const ( + KindConfigMap = "ConfigMap" + KindBackendTLSPolicy = "BackendTLSPolicy" + KindGateway = "Gateway" + KindGatewayClass = "GatewayClass" + KindGRPCRoute = "GRPCRoute" + KindHTTPRoute = "HTTPRoute" + KindNamespace = "Namespace" + KindTLSRoute = "TLSRoute" + KindTCPRoute = "TCPRoute" + KindUDPRoute = "UDPRoute" + KindService = "Service" + KindServiceImport = "ServiceImport" + KindSecret = "Secret" + KindBackend = "Backend" + + GroupMultiClusterService = "multicluster.x-k8s.io" + // OwningGatewayNamespaceLabel is the owner reference label used for managed infra. + // The value should be the namespace of the accepted Envoy Gateway. + OwningGatewayNamespaceLabel = "apk.wso2.com/owning-gateway-namespace" + + OwningGatewayClassLabel = "apk.wso2.com/owning-gatewayclass" + // OwningGatewayNameLabel is the owner reference label used for managed infra. + // The value should be the name of the accepted Envoy Gateway. + OwningGatewayNameLabel = "apk.wso2.com/owning-gateway-name" + + // minEphemeralPort is the first port in the ephemeral port range. + minEphemeralPort = 1024 + // wellKnownPortShift is the constant added to the well known port (1-1023) + // to convert it into an ephemeral port. + wellKnownPortShift = 10000 +) + +var _ TranslatorManager = (*Translator)(nil) + +type TranslatorManager interface { + Translate(resources *Resources) *TranslateResult + GetRelevantGateways(gateways []*gwapiv1.Gateway) []*GatewayContext + + RoutesTranslator + ListenersTranslator + AddressesTranslator + FiltersTranslator +} + +// Translator translates Gateway API resources to IRs and computes status +// for Gateway API resources. +type Translator struct { + // GatewayControllerName is the name of the Gateway API controller + GatewayControllerName string + + // GatewayClassName is the name of the GatewayClass + // to process Gateways for. + GatewayClassName gwapiv1.ObjectName + + // GlobalRateLimitEnabled is true when global + // ratelimiting has been configured by the admin. + GlobalRateLimitEnabled bool + + // EndpointRoutingDisabled can be set to true to use + // the Service Cluster IP for routing to the backend + // instead. + EndpointRoutingDisabled bool + + // MergeGateways is true when all Gateway Listeners + // should be merged under the parent GatewayClass. + MergeGateways bool + + // EnvoyPatchPolicyEnabled when the EnvoyPatchPolicy + // feature is enabled. + EnvoyPatchPolicyEnabled bool + + // ExtensionGroupKinds stores the group/kind for all resources + // introduced by an Extension so that the translator can + // store referenced resources in the IR for later use. + ExtensionGroupKinds []schema.GroupKind + + // Namespace is the namespace that Envoy Gateway runs in. + Namespace string +} + +type TranslateResult struct { + Resources + XdsIR XdsIRMap `json:"xdsIR" yaml:"xdsIR"` + InfraIR InfraIRMap `json:"infraIR" yaml:"infraIR"` +} + +func newTranslateResult(gateways []*GatewayContext, + httpRoutes []*HTTPRouteContext, + grpcRoutes []*GRPCRouteContext, + tlsRoutes []*TLSRouteContext, + tcpRoutes []*TCPRouteContext, + udpRoutes []*UDPRouteContext, + xdsIR XdsIRMap, infraIR InfraIRMap) *TranslateResult { + translateResult := &TranslateResult{ + XdsIR: xdsIR, + InfraIR: infraIR, + } + + for _, gateway := range gateways { + translateResult.Gateways = append(translateResult.Gateways, gateway.Gateway) + } + for _, httpRoute := range httpRoutes { + translateResult.HTTPRoutes = append(translateResult.HTTPRoutes, httpRoute.HTTPRoute) + } + for _, grpcRoute := range grpcRoutes { + translateResult.GRPCRoutes = append(translateResult.GRPCRoutes, grpcRoute.GRPCRoute) + } + for _, tlsRoute := range tlsRoutes { + translateResult.TLSRoutes = append(translateResult.TLSRoutes, tlsRoute.TLSRoute) + } + for _, tcpRoute := range tcpRoutes { + translateResult.TCPRoutes = append(translateResult.TCPRoutes, tcpRoute.TCPRoute) + } + for _, udpRoute := range udpRoutes { + translateResult.UDPRoutes = append(translateResult.UDPRoutes, udpRoute.UDPRoute) + } + return translateResult +} + +func (t *Translator) Translate(resources *Resources) *TranslateResult { + // Get Gateways belonging to our GatewayClass. + gateways := t.GetRelevantGateways(resources.Gateways) + + // Build IR maps. + xdsIR, infraIR := t.InitIRs(gateways, resources) + + // Process all Listeners for all relevant Gateways. + t.ProcessListeners(gateways, xdsIR, infraIR, resources) + + // Process all Addresses for all relevant Gateways. + t.ProcessAddresses(gateways, infraIR) + + // Process all relevant HTTPRoutes. + httpRoutes := t.ProcessHTTPRoutes(resources.HTTPRoutes, gateways, resources, xdsIR) + + // Process all relevant GRPCRoutes. + // grpcRoutes := t.ProcessGRPCRoutes(resources.GRPCRoutes, gateways, resources, xdsIR) + + // // Process all relevant TLSRoutes. + // tlsRoutes := t.ProcessTLSRoutes(resources.TLSRoutes, gateways, resources, xdsIR) + + // // Process all relevant TCPRoutes. + // tcpRoutes := t.ProcessTCPRoutes(resources.TCPRoutes, gateways, resources, xdsIR) + + // // Process all relevant UDPRoutes. + // udpRoutes := t.ProcessUDPRoutes(resources.UDPRoutes, gateways, resources, xdsIR) + + // Process BackendTrafficPolicies + // routes := []RouteContext{} + // for _, h := range httpRoutes { + // routes = append(routes, h) + // } + // for _, g := range grpcRoutes { + // routes = append(routes, g) + // } + // for _, t := range tlsRoutes { + // routes = append(routes, t) + // } + // for _, t := range tcpRoutes { + // routes = append(routes, t) + // } + // for _, u := range udpRoutes { + // routes = append(routes, u) + // } + + // Process BackendTrafficPolicies + // backendTrafficPolicies := t.ProcessBackendTrafficPolicies( + // resources.BackendTrafficPolicies, gateways, routes, xdsIR) + + // // Process SecurityPolicies + // securityPolicies := t.ProcessSecurityPolicies( + // resources.SecurityPolicies, gateways, routes, resources, xdsIR) + + // Sort xdsIR based on the Gateway API spec + sortXdsIRMap(xdsIR) + + return newTranslateResult(gateways, httpRoutes, nil, nil, nil, nil, xdsIR, infraIR) + +} + +// GetRelevantGateways returns GatewayContexts, containing a copy of the original +// Gateway with the Listener statuses reset. +func (t *Translator) GetRelevantGateways(gateways []*gwapiv1.Gateway) []*GatewayContext { + var relevant []*GatewayContext + + for _, gateway := range gateways { + if gateway == nil { + panic("received nil gateway") + } + + if gateway.Spec.GatewayClassName == t.GatewayClassName { + gc := &GatewayContext{ + Gateway: gateway.DeepCopy(), + } + gc.ResetListeners() + + relevant = append(relevant, gc) + } + } + + return relevant +} + +// InitIRs checks if mergeGateways is enabled in EnvoyProxy config and initializes XdsIR and InfraIR maps with adequate keys. +func (t *Translator) InitIRs(gateways []*GatewayContext, resources *Resources) (map[string]*ir.Xds, map[string]*ir.Infra) { + xdsIR := make(XdsIRMap) + infraIR := make(InfraIRMap) + + var irKey string + for _, gateway := range gateways { + gwXdsIR := &ir.Xds{} + gwInfraIR := ir.NewInfra() + labels := infrastructureLabels(gateway.Gateway) + annotations := infrastructureAnnotations(gateway.Gateway) + gwInfraIR.Proxy.GetProxyMetadata().Annotations = annotations + + irKey = irStringKey(gateway.Gateway.Namespace, gateway.Gateway.Name) + + maps.Copy(labels, GatewayOwnerLabels(gateway.Namespace, gateway.Name)) + gwInfraIR.Proxy.GetProxyMetadata().Labels = labels + + gwInfraIR.Proxy.Name = irKey + // save the IR references in the map before the translation starts + xdsIR[irKey] = gwXdsIR + infraIR[irKey] = gwInfraIR + } + + return xdsIR, infraIR +} + +func infrastructureAnnotations(gtw *gwapiv1.Gateway) map[string]string { + if gtw.Spec.Infrastructure != nil && len(gtw.Spec.Infrastructure.Annotations) > 0 { + res := make(map[string]string) + for k, v := range gtw.Spec.Infrastructure.Annotations { + res[string(k)] = string(v) + } + return res + } + return nil +} + +func infrastructureLabels(gtw *gwapiv1.Gateway) map[string]string { + res := make(map[string]string) + if gtw.Spec.Infrastructure != nil { + for k, v := range gtw.Spec.Infrastructure.Labels { + res[string(k)] = string(v) + } + } + return res +} + +// XdsIR and InfraIR map keys by default are {GatewayNamespace}/{GatewayName}, but if mergeGateways is set, they are merged under {GatewayClassName} key. +func (t *Translator) getIRKey(gateway *gwapiv1.Gateway) string { + irKey := irStringKey(gateway.Namespace, gateway.Name) + if t.MergeGateways { + return string(t.GatewayClassName) + } + + return irKey +} diff --git a/adapter/internal/operator/gateway-api/translator/accesslog.go b/adapter/internal/operator/gateway-api/translator/accesslog.go new file mode 100644 index 0000000000..eb53fe8b36 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/accesslog.go @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "sort" + + accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" + cfgcore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + fileaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" + grpcaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" + otelaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + otlpcommonv1 "go.opentelemetry.io/proto/otlp/common/v1" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/structpb" + "k8s.io/utils/ptr" +) + +const ( + // EnvoyTextLogFormat is the default log format for Envoy. + // See https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#default-format-string + EnvoyTextLogFormat = "{\"start_time\":\"%START_TIME%\",\"method\":\"%REQ(:METHOD)%\"," + + "\"x-envoy-origin-path\":\"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%\",\"protocol\":\"%PROTOCOL%\"," + + "\"response_code\":\"%RESPONSE_CODE%\",\"response_flags\":\"%RESPONSE_FLAGS%\"," + + "\"response_code_details\":\"%RESPONSE_CODE_DETAILS%\"," + + "\"connection_termination_details\":\"%CONNECTION_TERMINATION_DETAILS%\"," + + "\"upstream_transport_failure_reason\":\"%UPSTREAM_TRANSPORT_FAILURE_REASON%\"," + + "\"bytes_received\":\"%BYTES_RECEIVED%\",\"bytes_sent\":\"%BYTES_SENT%\"," + + "\"duration\":\"%DURATION%\",\"x-envoy-upstream-service-time\":\"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%\"," + + "\"x-forwarded-for\":\"%REQ(X-FORWARDED-FOR)%\",\"user-agent\":\"%REQ(USER-AGENT)%\"," + + "\"x-request-id\":\"%REQ(X-REQUEST-ID)%\",\":authority\":\"%REQ(:AUTHORITY)%\"," + + "\"upstream_host\":\"%UPSTREAM_HOST%\",\"upstream_cluster\":\"%UPSTREAM_CLUSTER%\"," + + "\"upstream_local_address\":\"%UPSTREAM_LOCAL_ADDRESS%\"," + + "\"downstream_local_address\":\"%DOWNSTREAM_LOCAL_ADDRESS%\"," + + "\"downstream_remote_address\":\"%DOWNSTREAM_REMOTE_ADDRESS%\"," + + "\"requested_server_name\":\"%REQUESTED_SERVER_NAME%\",\"route_name\":\"%ROUTE_NAME%\"}\n" + + otelLogName = "otel_envoy_accesslog" + otelAccessLog = "envoy.access_loggers.open_telemetry" +) + +var ( + // for the case when a route does not exist to upstream, hcm logs will not be present + listenerAccessLogFilter = &accesslog.AccessLogFilter{ + FilterSpecifier: &accesslog.AccessLogFilter_ResponseFlagFilter{ + ResponseFlagFilter: &accesslog.ResponseFlagFilter{Flags: []string{"NR"}}, + }, + } +) + +func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLog { + if al == nil { + return nil + } + + totalLen := len(al.Text) + len(al.JSON) + len(al.OpenTelemetry) + accessLogs := make([]*accesslog.AccessLog, 0, totalLen) + // handle text file access logs + for _, text := range al.Text { + filelog := &fileaccesslog.FileAccessLog{ + Path: text.Path, + } + format := EnvoyTextLogFormat + if text.Format != nil { + format = *text.Format + } + + filelog.AccessLogFormat = &fileaccesslog.FileAccessLog_LogFormat{ + LogFormat: &cfgcore.SubstitutionFormatString{ + Format: &cfgcore.SubstitutionFormatString_TextFormatSource{ + TextFormatSource: &cfgcore.DataSource{ + Specifier: &cfgcore.DataSource_InlineString{ + InlineString: format, + }, + }, + }, + }, + } + + // TODO: find a better way to handle this + accesslogAny, _ := anypb.New(filelog) + accessLogs = append(accessLogs, &accesslog.AccessLog{ + Name: wellknown.FileAccessLog, + ConfigType: &accesslog.AccessLog_TypedConfig{ + TypedConfig: accesslogAny, + }, + }) + } + // handle json file access logs + for _, json := range al.JSON { + jsonFormat := &structpb.Struct{ + Fields: make(map[string]*structpb.Value, len(json.JSON)), + } + + // sort keys to ensure consistent ordering + keys := maps.Keys(json.JSON) + sort.Strings(keys) + + for _, key := range keys { + jsonFormat.Fields[key] = &structpb.Value{ + Kind: &structpb.Value_StringValue{ + StringValue: json.JSON[key], + }, + } + } + + filelog := &fileaccesslog.FileAccessLog{ + Path: json.Path, + AccessLogFormat: &fileaccesslog.FileAccessLog_LogFormat{ + LogFormat: &cfgcore.SubstitutionFormatString{ + Format: &cfgcore.SubstitutionFormatString_JsonFormat{ + JsonFormat: jsonFormat, + }, + }, + }, + } + + accesslogAny, _ := anypb.New(filelog) + accessLogs = append(accessLogs, &accesslog.AccessLog{ + Name: wellknown.FileAccessLog, + ConfigType: &accesslog.AccessLog_TypedConfig{ + TypedConfig: accesslogAny, + }, + }) + } + // handle open telemetry access logs + for _, otel := range al.OpenTelemetry { + al := &otelaccesslog.OpenTelemetryAccessLogConfig{ + CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ + LogName: otelLogName, + GrpcService: &cfgcore.GrpcService{ + TargetSpecifier: &cfgcore.GrpcService_EnvoyGrpc_{ + EnvoyGrpc: &cfgcore.GrpcService_EnvoyGrpc{ + ClusterName: buildClusterName("accesslog", otel.Host, otel.Port), + Authority: otel.Host, + }, + }, + }, + TransportApiVersion: cfgcore.ApiVersion_V3, + }, + ResourceAttributes: convertToKeyValueList(otel.Resources, false), + } + + format := EnvoyTextLogFormat + if otel.Text != nil { + format = *otel.Text + } + + if format != "" { + al.Body = &otlpcommonv1.AnyValue{ + Value: &otlpcommonv1.AnyValue_StringValue{ + StringValue: format, + }, + } + } + + al.Attributes = convertToKeyValueList(otel.Attributes, true) + + accesslogAny, _ := anypb.New(al) + accessLogs = append(accessLogs, &accesslog.AccessLog{ + Name: otelAccessLog, + ConfigType: &accesslog.AccessLog_TypedConfig{ + TypedConfig: accesslogAny, + }, + }) + } + + // add filter for listener access logs + if forListener { + for _, al := range accessLogs { + al.Filter = listenerAccessLogFilter + } + } + + return accessLogs +} + +// read more here: https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/k8s/ +const ( + k8sNamespaceNameKey = "k8s.namespace.name" + k8sPodNameKey = "k8s.pod.name" +) + +func convertToKeyValueList(attributes map[string]string, additionalLabels bool) *otlpcommonv1.KeyValueList { + maxLen := len(attributes) + if additionalLabels { + maxLen += 2 + } + keyValueList := &otlpcommonv1.KeyValueList{ + Values: make([]*otlpcommonv1.KeyValue, 0, maxLen), + } + + // always set the k8s namespace and pod name for better UX + // EG cannot know the client namespace and pod name, + // so we set these on attributes that read from the environment. + if additionalLabels { + // TODO: check the provider type and set the appropriate attributes + keyValueList.Values = append(keyValueList.Values, &otlpcommonv1.KeyValue{ + Key: k8sNamespaceNameKey, + Value: &otlpcommonv1.AnyValue{Value: &otlpcommonv1.AnyValue_StringValue{StringValue: "%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%"}}, + }) + + keyValueList.Values = append(keyValueList.Values, &otlpcommonv1.KeyValue{ + Key: k8sPodNameKey, + Value: &otlpcommonv1.AnyValue{Value: &otlpcommonv1.AnyValue_StringValue{StringValue: "%ENVIRONMENT(ENVOY_POD_NAME)%"}}, + }) + } + + if len(attributes) == 0 { + return keyValueList + } + + // sort keys to ensure consistent ordering + keys := maps.Keys(attributes) + sort.Strings(keys) + + for _, key := range keys { + keyValueList.Values = append(keyValueList.Values, &otlpcommonv1.KeyValue{ + Key: key, + Value: &otlpcommonv1.AnyValue{Value: &otlpcommonv1.AnyValue_StringValue{StringValue: attributes[key]}}, + }) + } + + return keyValueList +} + +func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessLog) error { + if al == nil { + return nil + } + + for _, otel := range al.OpenTelemetry { + clusterName := buildClusterName("accesslog", otel.Host, otel.Port) + + ds := &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(otel.Host, otel.Port)}, + } + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: clusterName, + settings: []*ir.DestinationSetting{ds}, + tSocket: nil, + endpointType: EndpointTypeDNS, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + + } + + return nil +} diff --git a/adapter/internal/operator/gateway-api/translator/basicauth.go b/adapter/internal/operator/gateway-api/translator/basicauth.go new file mode 100644 index 0000000000..5fd4932f92 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/basicauth.go @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + basicauthv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/basic_auth/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + basicAuthFilter = "envoy.filters.http.basic_auth" +) + +func init() { + registerHTTPFilter(&basicAuth{}) +} + +type basicAuth struct { +} + +var _ httpFilter = &basicAuth{} + +// patchHCM builds and appends the basic_auth Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an basic_auth filter for each route that contains an BasicAuth config. +// The filter is disabled by default. It is enabled on the route level. +func (*basicAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !routeContainsBasicAuth(route) { + continue + } + + filter, err := buildHCMBasicAuthFilter(route) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return errs +} + +// buildHCMBasicAuthFilter returns a basic_auth HTTP filter from the provided IR HTTPRoute. +func buildHCMBasicAuthFilter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) { + basicAuthProto := basicAuthConfig(route) + + if err := basicAuthProto.ValidateAll(); err != nil { + return nil, err + } + + basicAuthAny, err := anypb.New(basicAuthProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: basicAuthFilterName(route), + Disabled: true, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: basicAuthAny, + }, + }, nil +} + +func basicAuthFilterName(route *ir.HTTPRoute) string { + return perRouteFilterName(basicAuthFilter, route.Name) +} + +func basicAuthConfig(route *ir.HTTPRoute) *basicauthv3.BasicAuth { + return &basicauthv3.BasicAuth{ + Users: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: route.BasicAuth.Users, + }, + }, + } +} + +// routeContainsBasicAuth returns true if BasicAuth exists for the provided route. +func routeContainsBasicAuth(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.BasicAuth != nil { + return true + } + + return false +} + +func (*basicAuth) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} + +// patchRoute patches the provided route with the basicAuth config if applicable. +// Note: this method enables the corresponding basicAuth filter for the provided route. +func (*basicAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.BasicAuth == nil { + return nil + } + return enableFilterOnRoute(basicAuthFilter, route, irRoute) +} diff --git a/adapter/internal/operator/gateway-api/translator/cluster.go b/adapter/internal/operator/gateway-api/translator/cluster.go new file mode 100644 index 0000000000..fe29398cf2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/cluster.go @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "encoding/hex" + "fmt" + "sort" + "time" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + preservecasev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3" + proxyprotocolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3" + rawbufferv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3" + httpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" + xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" + "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/wrapperspb" + "k8s.io/utils/ptr" +) + +const ( + extensionOptionsKey = "envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#envoy-v3-api-field-config-cluster-v3-cluster-per-connection-buffer-limit-bytes + tcpClusterPerConnectionBufferLimitBytes = 32768 + tcpClusterPerConnectTimeout = 10 * time.Second +) + +type xdsClusterArgs struct { + name string + settings []*ir.DestinationSetting + tSocket *corev3.TransportSocket + endpointType EndpointType + loadBalancer *ir.LoadBalancer + proxyProtocol *ir.ProxyProtocol + circuitBreaker *ir.CircuitBreaker + healthCheck *ir.HealthCheck + http1Settings *ir.HTTP1Settings + timeout *ir.Timeout + tcpkeepalive *ir.TCPKeepalive +} + +type EndpointType int + +const ( + EndpointTypeDNS EndpointType = iota + EndpointTypeStatic +) + +func buildEndpointType(settings []*ir.DestinationSetting) EndpointType { + // Get endpoint address type for xds cluster by returning the first DestinationSetting's AddressType, + // since there's no Mixed AddressType among all the DestinationSettings. + if settings == nil { + return EndpointTypeStatic + } + + addrType := settings[0].AddressType + + if addrType != nil && *addrType == ir.FQDN { + return EndpointTypeDNS + } + + return EndpointTypeStatic +} + +func buildXdsCluster(args *xdsClusterArgs) *clusterv3.Cluster { + cluster := &clusterv3.Cluster{ + Name: args.name, + DnsLookupFamily: clusterv3.Cluster_V4_ONLY, + CommonLbConfig: &clusterv3.Cluster_CommonLbConfig{ + LocalityConfigSpecifier: &clusterv3.Cluster_CommonLbConfig_LocalityWeightedLbConfig_{ + LocalityWeightedLbConfig: &clusterv3.Cluster_CommonLbConfig_LocalityWeightedLbConfig{}}}, + OutlierDetection: &clusterv3.OutlierDetection{}, + PerConnectionBufferLimitBytes: wrapperspb.UInt32(tcpClusterPerConnectionBufferLimitBytes), + } + + cluster.ConnectTimeout = buildConnectTimeout(args.timeout) + + // Set Proxy Protocol + if args.proxyProtocol != nil { + cluster.TransportSocket = buildProxyProtocolSocket(args.proxyProtocol, args.tSocket) + } else if args.tSocket != nil { + cluster.TransportSocket = args.tSocket + } + + for i, ds := range args.settings { + if ds.TLS != nil { + socket, err := buildXdsUpstreamTLSSocketWthCert(ds.TLS) + if err != nil { + // TODO: Log something here + return nil + } + if args.proxyProtocol != nil { + socket = buildProxyProtocolSocket(args.proxyProtocol, socket) + } + matchName := fmt.Sprintf("%s/tls/%d", args.name, i) + cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, &clusterv3.Cluster_TransportSocketMatch{ + Name: matchName, + Match: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "name": structpb.NewStringValue(matchName), + }, + }, + TransportSocket: socket, + }) + } + } + + if args.endpointType == EndpointTypeStatic { + cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_EDS} + cluster.EdsClusterConfig = &clusterv3.Cluster_EdsClusterConfig{ + ServiceName: args.name, + EdsConfig: &corev3.ConfigSource{ + ResourceApiVersion: resource.DefaultAPIVersion, + ConfigSourceSpecifier: &corev3.ConfigSource_Ads{ + Ads: &corev3.AggregatedConfigSource{}, + }, + }, + } + } else { + cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_STRICT_DNS} + cluster.DnsRefreshRate = durationpb.New(30 * time.Second) + cluster.RespectDnsTtl = true + } + + // build common, HTTP/1 and HTTP/2 protocol options for cluster + epo := buildTypedExtensionProtocolOptions(args) + if epo != nil { + cluster.TypedExtensionProtocolOptions = epo + } + + // Set Load Balancer policy + //nolint:gocritic + if args.loadBalancer == nil { + cluster.LbPolicy = clusterv3.Cluster_LEAST_REQUEST + } else if args.loadBalancer.LeastRequest != nil { + cluster.LbPolicy = clusterv3.Cluster_LEAST_REQUEST + if args.loadBalancer.LeastRequest.SlowStart != nil { + if args.loadBalancer.LeastRequest.SlowStart.Window != nil { + cluster.LbConfig = &clusterv3.Cluster_LeastRequestLbConfig_{ + LeastRequestLbConfig: &clusterv3.Cluster_LeastRequestLbConfig{ + SlowStartConfig: &clusterv3.Cluster_SlowStartConfig{ + SlowStartWindow: durationpb.New(args.loadBalancer.LeastRequest.SlowStart.Window.Duration), + }, + }, + } + } + } + } else if args.loadBalancer.RoundRobin != nil { + cluster.LbPolicy = clusterv3.Cluster_ROUND_ROBIN + if args.loadBalancer.RoundRobin.SlowStart != nil { + if args.loadBalancer.RoundRobin.SlowStart.Window != nil { + cluster.LbConfig = &clusterv3.Cluster_RoundRobinLbConfig_{ + RoundRobinLbConfig: &clusterv3.Cluster_RoundRobinLbConfig{ + SlowStartConfig: &clusterv3.Cluster_SlowStartConfig{ + SlowStartWindow: durationpb.New(args.loadBalancer.RoundRobin.SlowStart.Window.Duration), + }, + }, + } + } + } + } else if args.loadBalancer.Random != nil { + cluster.LbPolicy = clusterv3.Cluster_RANDOM + } else if args.loadBalancer.ConsistentHash != nil { + cluster.LbPolicy = clusterv3.Cluster_MAGLEV + } + + if args.healthCheck != nil && args.healthCheck.Active != nil { + cluster.HealthChecks = buildXdsHealthCheck(args.healthCheck.Active) + } + + if args.healthCheck != nil && args.healthCheck.Passive != nil { + cluster.OutlierDetection = buildXdsOutlierDetection(args.healthCheck.Passive) + + } + + cluster.CircuitBreakers = buildXdsClusterCircuitBreaker(args.circuitBreaker) + + if args.tcpkeepalive != nil { + cluster.UpstreamConnectionOptions = buildXdsClusterUpstreamOptions(args.tcpkeepalive) + } + return cluster +} + +func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck) []*corev3.HealthCheck { + hc := &corev3.HealthCheck{ + Timeout: durationpb.New(healthcheck.Timeout.Duration), + Interval: durationpb.New(healthcheck.Interval.Duration), + } + if healthcheck.UnhealthyThreshold != nil { + hc.UnhealthyThreshold = wrapperspb.UInt32(*healthcheck.UnhealthyThreshold) + } + if healthcheck.HealthyThreshold != nil { + hc.HealthyThreshold = wrapperspb.UInt32(*healthcheck.HealthyThreshold) + } + if healthcheck.HTTP != nil { + httpChecker := &corev3.HealthCheck_HttpHealthCheck{ + Host: healthcheck.HTTP.Host, + Path: healthcheck.HTTP.Path, + } + if healthcheck.HTTP.Method != nil { + httpChecker.Method = corev3.RequestMethod(corev3.RequestMethod_value[*healthcheck.HTTP.Method]) + } + httpChecker.ExpectedStatuses = buildHTTPStatusRange(healthcheck.HTTP.ExpectedStatuses) + if receive := buildHealthCheckPayload(healthcheck.HTTP.ExpectedResponse); receive != nil { + httpChecker.Receive = append(httpChecker.Receive, receive) + } + hc.HealthChecker = &corev3.HealthCheck_HttpHealthCheck_{ + HttpHealthCheck: httpChecker, + } + } + if healthcheck.TCP != nil { + tcpChecker := &corev3.HealthCheck_TcpHealthCheck{ + Send: buildHealthCheckPayload(healthcheck.TCP.Send), + } + if receive := buildHealthCheckPayload(healthcheck.TCP.Receive); receive != nil { + tcpChecker.Receive = append(tcpChecker.Receive, receive) + } + hc.HealthChecker = &corev3.HealthCheck_TcpHealthCheck_{ + TcpHealthCheck: tcpChecker, + } + } + return []*corev3.HealthCheck{hc} +} + +func buildXdsOutlierDetection(outlierDetection *ir.OutlierDetection) *clusterv3.OutlierDetection { + od := &clusterv3.OutlierDetection{ + BaseEjectionTime: durationpb.New(outlierDetection.BaseEjectionTime.Duration), + Interval: durationpb.New(outlierDetection.Interval.Duration), + } + if outlierDetection.SplitExternalLocalOriginErrors != nil { + od.SplitExternalLocalOriginErrors = *outlierDetection.SplitExternalLocalOriginErrors + } + + if outlierDetection.MaxEjectionPercent != nil && *outlierDetection.MaxEjectionPercent > 0 { + od.MaxEjectionPercent = wrapperspb.UInt32(uint32(*outlierDetection.MaxEjectionPercent)) + } + + if outlierDetection.ConsecutiveLocalOriginFailures != nil { + od.ConsecutiveLocalOriginFailure = wrapperspb.UInt32(*outlierDetection.ConsecutiveLocalOriginFailures) + } + + if outlierDetection.Consecutive5xxErrors != nil { + od.Consecutive_5Xx = wrapperspb.UInt32(*outlierDetection.Consecutive5xxErrors) + } + + if outlierDetection.ConsecutiveGatewayErrors != nil { + od.ConsecutiveGatewayFailure = wrapperspb.UInt32(*outlierDetection.ConsecutiveGatewayErrors) + } + + return od +} + +// buildHTTPStatusRange converts an array of http status to an array of the range of http status. +func buildHTTPStatusRange(irStatuses []ir.HTTPStatus) []*xdstype.Int64Range { + if len(irStatuses) == 0 { + return nil + } + ranges := []*xdstype.Int64Range{} + sort.Slice(irStatuses, func(i int, j int) bool { + return irStatuses[i] < irStatuses[j] + }) + var start, end int64 + for i := 0; i < len(irStatuses); i++ { + switch { + case start == 0: + start = int64(irStatuses[i]) + end = int64(irStatuses[i] + 1) + case int64(irStatuses[i]) == end: + end++ + default: + ranges = append(ranges, &xdstype.Int64Range{Start: start, End: end}) + start = int64(irStatuses[i]) + end = int64(irStatuses[i] + 1) + } + } + if start != 0 { + ranges = append(ranges, &xdstype.Int64Range{Start: start, End: end}) + } + return ranges +} + +func buildHealthCheckPayload(irLoad *ir.HealthCheckPayload) *corev3.HealthCheck_Payload { + if irLoad == nil { + return nil + } + + var hcp corev3.HealthCheck_Payload + if irLoad.Text != nil && *irLoad.Text != "" { + hcp.Payload = &corev3.HealthCheck_Payload_Text{ + Text: hex.EncodeToString([]byte(*irLoad.Text)), + } + } + if len(irLoad.Binary) > 0 { + binPayload := &corev3.HealthCheck_Payload_Binary{ + Binary: make([]byte, len(irLoad.Binary)), + } + copy(binPayload.Binary, irLoad.Binary) + hcp.Payload = binPayload + } + return &hcp +} + +func buildXdsClusterCircuitBreaker(circuitBreaker *ir.CircuitBreaker) *clusterv3.CircuitBreakers { + // Always allow the same amount of retries as regular requests to handle surges in retries + // related to pod restarts + cbt := &clusterv3.CircuitBreakers_Thresholds{ + Priority: corev3.RoutingPriority_DEFAULT, + MaxRetries: &wrapperspb.UInt32Value{ + Value: uint32(1024), + }, + } + + if circuitBreaker != nil { + if circuitBreaker.MaxConnections != nil { + cbt.MaxConnections = &wrapperspb.UInt32Value{ + Value: *circuitBreaker.MaxConnections, + } + } + + if circuitBreaker.MaxPendingRequests != nil { + cbt.MaxPendingRequests = &wrapperspb.UInt32Value{ + Value: *circuitBreaker.MaxPendingRequests, + } + } + + if circuitBreaker.MaxParallelRequests != nil { + cbt.MaxRequests = &wrapperspb.UInt32Value{ + Value: *circuitBreaker.MaxParallelRequests, + } + } + + if circuitBreaker.MaxParallelRetries != nil { + cbt.MaxRetries = &wrapperspb.UInt32Value{ + Value: *circuitBreaker.MaxParallelRetries, + } + } + } + + ecb := &clusterv3.CircuitBreakers{ + Thresholds: []*clusterv3.CircuitBreakers_Thresholds{cbt}, + } + + return ecb +} + +func buildXdsClusterLoadAssignment(clusterName string, destSettings []*ir.DestinationSetting) *endpointv3.ClusterLoadAssignment { + localities := make([]*endpointv3.LocalityLbEndpoints, 0, len(destSettings)) + for i, ds := range destSettings { + + endpoints := make([]*endpointv3.LbEndpoint, 0, len(ds.Endpoints)) + + var metadata *corev3.Metadata + if ds.TLS != nil { + metadata = &corev3.Metadata{ + FilterMetadata: map[string]*structpb.Struct{ + "envoy.transport_socket_match": { + Fields: map[string]*structpb.Value{ + "name": structpb.NewStringValue(fmt.Sprintf("%s/tls/%d", clusterName, i)), + }, + }, + }, + } + } + + for _, irEp := range ds.Endpoints { + lbEndpoint := &endpointv3.LbEndpoint{ + Metadata: metadata, + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_TCP, + Address: irEp.Host, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: irEp.Port, + }, + }, + }, + }, + }, + }, + } + // Set default weight of 1 for all endpoints. + lbEndpoint.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: 1} + endpoints = append(endpoints, lbEndpoint) + } + + // Envoy requires a distinct region to be set for each LocalityLbEndpoints. + // If we don't do this, Envoy will merge all LocalityLbEndpoints into one. + // We use the name of the backendRef as a pseudo region name. + locality := &endpointv3.LocalityLbEndpoints{ + Locality: &corev3.Locality{ + Region: fmt.Sprintf("%s/backend/%d", clusterName, i), + }, + LbEndpoints: endpoints, + Priority: 0, + } + + // Set locality weight + var weight uint32 + if ds.Weight != nil { + weight = *ds.Weight + } else { + weight = 1 + } + locality.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: weight} + + localities = append(localities, locality) + } + return &endpointv3.ClusterLoadAssignment{ClusterName: clusterName, Endpoints: localities} +} + +func buildTypedExtensionProtocolOptions(args *xdsClusterArgs) map[string]*anypb.Any { + requiresHTTP2Options := false + for _, ds := range args.settings { + if ds.Protocol == ir.GRPC || + ds.Protocol == ir.HTTP2 { + requiresHTTP2Options = true + break + } + } + + requiresCommonHTTPOptions := (args.timeout != nil && args.timeout.HTTP != nil && + (args.timeout.HTTP.MaxConnectionDuration != nil || args.timeout.HTTP.ConnectionIdleTimeout != nil)) || + (args.circuitBreaker != nil && args.circuitBreaker.MaxRequestsPerConnection != nil) + + requiresHTTP1Options := args.http1Settings != nil && (args.http1Settings.EnableTrailers || args.http1Settings.PreserveHeaderCase || args.http1Settings.HTTP10 != nil) + + if !(requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options) { + return nil + } + + protocolOptions := httpv3.HttpProtocolOptions{} + + if requiresCommonHTTPOptions { + protocolOptions.CommonHttpProtocolOptions = &corev3.HttpProtocolOptions{} + + if args.timeout != nil && args.timeout.HTTP != nil { + if args.timeout.HTTP.ConnectionIdleTimeout != nil { + protocolOptions.CommonHttpProtocolOptions.IdleTimeout = + durationpb.New(args.timeout.HTTP.ConnectionIdleTimeout.Duration) + } + + if args.timeout.HTTP.MaxConnectionDuration != nil { + protocolOptions.CommonHttpProtocolOptions.MaxConnectionDuration = + durationpb.New(args.timeout.HTTP.MaxConnectionDuration.Duration) + } + } + + if args.circuitBreaker != nil && args.circuitBreaker.MaxRequestsPerConnection != nil { + protocolOptions.CommonHttpProtocolOptions.MaxRequestsPerConnection = &wrapperspb.UInt32Value{ + Value: *args.circuitBreaker.MaxRequestsPerConnection, + } + } + + } + + // When setting any Typed Extension Protocol Options, UpstreamProtocolOptions are mandatory + // If translation requires HTTP2 enablement or HTTP1 trailers, set appropriate setting + // Default to http1 otherwise + // TODO: If the cluster is TLS enabled, use AutoHTTPConfig instead of ExplicitHttpConfig + // so that when ALPN is supported then enabling http1 options doesn't force HTTP/1.1 + switch { + case requiresHTTP2Options: + protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{}, + }, + } + case requiresHTTP1Options: + http1opts := &corev3.Http1ProtocolOptions{ + EnableTrailers: args.http1Settings.EnableTrailers, + } + if args.http1Settings.PreserveHeaderCase { + preservecaseAny, _ := anypb.New(&preservecasev3.PreserveCaseFormatterConfig{}) + http1opts.HeaderKeyFormat = &corev3.Http1ProtocolOptions_HeaderKeyFormat{ + HeaderFormat: &corev3.Http1ProtocolOptions_HeaderKeyFormat_StatefulFormatter{ + StatefulFormatter: &corev3.TypedExtensionConfig{ + Name: "preserve_case", + TypedConfig: preservecaseAny, + }, + }, + } + } + if args.http1Settings.HTTP10 != nil { + http1opts.AcceptHttp_10 = true + http1opts.DefaultHostForHttp_10 = ptr.Deref(args.http1Settings.HTTP10.DefaultHost, "") + } + protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{ + HttpProtocolOptions: http1opts, + }, + }, + } + default: + protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{}, + }, + } + } + + anyProtocolOptions, _ := anypb.New(&protocolOptions) + + extensionOptions := map[string]*anypb.Any{ + extensionOptionsKey: anyProtocolOptions, + } + + return extensionOptions +} + +// buildClusterName returns a cluster name for the given `host` and `port`. +// The format is: ||, where type is "accesslog" for access logs. +// It's easy to distinguish when debugging. +func buildClusterName(prefix string, host string, port uint32) string { + return fmt.Sprintf("%s|%s|%d", prefix, host, port) +} + +// buildProxyProtocolSocket builds the ProxyProtocol transport socket. +func buildProxyProtocolSocket(proxyProtocol *ir.ProxyProtocol, tSocket *corev3.TransportSocket) *corev3.TransportSocket { + if proxyProtocol == nil { + return nil + } + + ppCtx := &proxyprotocolv3.ProxyProtocolUpstreamTransport{} + + switch proxyProtocol.Version { + case ir.ProxyProtocolVersionV1: + ppCtx.Config = &corev3.ProxyProtocolConfig{ + Version: corev3.ProxyProtocolConfig_V1, + } + case ir.ProxyProtocolVersionV2: + ppCtx.Config = &corev3.ProxyProtocolConfig{ + Version: corev3.ProxyProtocolConfig_V2, + } + } + + // If existing transport socket does not exist wrap around raw buffer + if tSocket == nil { + rawCtx := &rawbufferv3.RawBuffer{} + rawCtxAny, err := anypb.New(rawCtx) + if err != nil { + return nil + } + rawSocket := &corev3.TransportSocket{ + Name: wellknown.TransportSocketRawBuffer, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: rawCtxAny, + }, + } + ppCtx.TransportSocket = rawSocket + } else { + ppCtx.TransportSocket = tSocket + } + + ppCtxAny, err := anypb.New(ppCtx) + if err != nil { + return nil + } + + return &corev3.TransportSocket{ + Name: "envoy.transport_sockets.upstream_proxy_protocol", + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: ppCtxAny, + }, + } +} + +func buildConnectTimeout(to *ir.Timeout) *durationpb.Duration { + if to != nil && to.TCP != nil && to.TCP.ConnectTimeout != nil { + return durationpb.New(to.TCP.ConnectTimeout.Duration) + } + return durationpb.New(tcpClusterPerConnectTimeout) +} + +func buildXdsClusterUpstreamOptions(tcpkeepalive *ir.TCPKeepalive) *clusterv3.UpstreamConnectionOptions { + if tcpkeepalive == nil { + return nil + } + + ka := &clusterv3.UpstreamConnectionOptions{ + TcpKeepalive: &corev3.TcpKeepalive{}, + } + + if tcpkeepalive.Probes != nil { + ka.TcpKeepalive.KeepaliveProbes = wrapperspb.UInt32(*tcpkeepalive.Probes) + } + + if tcpkeepalive.Probes != nil { + ka.TcpKeepalive.KeepaliveTime = wrapperspb.UInt32(*tcpkeepalive.IdleTime) + } + + if tcpkeepalive.Interval != nil { + ka.TcpKeepalive.KeepaliveInterval = wrapperspb.UInt32(*tcpkeepalive.Interval) + } + + return ka + +} diff --git a/adapter/internal/operator/gateway-api/translator/cors.go b/adapter/internal/operator/gateway-api/translator/cors.go new file mode 100644 index 0000000000..ba27240166 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/cors.go @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + "strconv" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + corsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/golang/protobuf/ptypes/wrappers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "google.golang.org/protobuf/types/known/anypb" +) + +func init() { + registerHTTPFilter(&cors{}) +} + +type cors struct { +} + +var _ httpFilter = &cors{} + +// patchHCM builds and appends the CORS Filter to the HTTP Connection Manager if +// applicable. +func (*cors) patchHCM( + mgr *hcmv3.HttpConnectionManager, + irListener *ir.HTTPListener) error { + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + if !listenerContainsCORS(irListener) { + return nil + } + + // Return early if filter already exists. + for _, httpFilter := range mgr.HttpFilters { + if httpFilter.Name == wellknown.CORS { + return nil + } + } + + corsFilter, err := buildHCMCORSFilter() + if err != nil { + return err + } + + // Ensure the CORS filter is the first one in the filter chain. + mgr.HttpFilters = append([]*hcmv3.HttpFilter{corsFilter}, mgr.HttpFilters...) + + return nil +} + +// buildHCMCORSFilter returns a CORS filter from the provided IR listener. +func buildHCMCORSFilter() (*hcmv3.HttpFilter, error) { + corsProto := &corsv3.Cors{} + + corsAny, err := anypb.New(corsProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: wellknown.CORS, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: corsAny, + }, + }, nil +} + +// listenerContainsCORS returns true if the provided listener has CORS +// policies attached to its routes. +func listenerContainsCORS(irListener *ir.HTTPListener) bool { + if irListener == nil { + return false + } + + for _, route := range irListener.Routes { + if route.CORS != nil { + return true + } + } + + return false +} + +// patchRoute patches the provided route with the CORS config if applicable. +func (*cors) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.CORS == nil { + return nil + } + + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[wellknown.CORS]; ok { + // This should not happen since this is the only place where the CORS + // filter is added in a route. + return fmt.Errorf("route already contains cors config: %+v", route) + } + + var ( + allowOrigins []*matcherv3.StringMatcher + allowMethods string + allowHeaders string + exposeHeaders string + maxAge string + allowCredentials *wrappers.BoolValue + ) + + //nolint:gocritic + + for _, origin := range irRoute.CORS.AllowOrigins { + allowOrigins = append(allowOrigins, buildXdsStringMatcher(origin)) + } + + allowMethods = strings.Join(irRoute.CORS.AllowMethods, ", ") + allowHeaders = strings.Join(irRoute.CORS.AllowHeaders, ", ") + exposeHeaders = strings.Join(irRoute.CORS.ExposeHeaders, ", ") + if irRoute.CORS.MaxAge != nil { + maxAge = strconv.Itoa(int(irRoute.CORS.MaxAge.Seconds())) + } + allowCredentials = &wrappers.BoolValue{Value: irRoute.CORS.AllowCredentials} + + routeCfgProto := &corsv3.CorsPolicy{ + AllowOriginStringMatch: allowOrigins, + AllowMethods: allowMethods, + AllowHeaders: allowHeaders, + ExposeHeaders: exposeHeaders, + MaxAge: maxAge, + AllowCredentials: allowCredentials, + } + + routeCfgAny, err := anypb.New(routeCfgProto) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[wellknown.CORS] = routeCfgAny + + return nil +} + +func (c *cors) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} diff --git a/adapter/internal/operator/gateway-api/translator/extauth.go b/adapter/internal/operator/gateway-api/translator/extauth.go new file mode 100644 index 0000000000..c50c584cf2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/extauth.go @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "net/url" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + extauthv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/golang/protobuf/ptypes/duration" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + extAuthFilter = "envoy.filters.http.ext_authz" +) + +func init() { + registerHTTPFilter(&extAuth{}) +} + +type extAuth struct { +} + +var _ httpFilter = &extAuth{} + +// patchHCM builds and appends the ext_authz Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an ext_authz filter for each route that contains an ExtAuthz config. +// The filter is disabled by default. It is enabled on the route level. +// TODO: zhaohuabing avoid duplicated HTTP filters +func (*extAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !routeContainsExtAuth(route) { + continue + } + + filter, err := buildHCMExtAuthFilter(route) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return errs +} + +// buildHCMExtAuthFilter returns an ext_authz HTTP filter from the provided IR HTTPRoute. +func buildHCMExtAuthFilter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) { + extAuthProto := extAuthConfig(route.ExtAuth) + if err := extAuthProto.ValidateAll(); err != nil { + return nil, err + } + + extAuthAny, err := anypb.New(extAuthProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: extAuthFilterName(route), + Disabled: true, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: extAuthAny, + }, + }, nil +} + +func extAuthFilterName(route *ir.HTTPRoute) string { + return perRouteFilterName(extAuthFilter, route.Name) +} + +func extAuthConfig(extAuth *ir.ExtAuth) *extauthv3.ExtAuthz { + config := &extauthv3.ExtAuthz{ + TransportApiVersion: corev3.ApiVersion_V3, + FailureModeAllow: false, + } + + var headersToExtAuth []*matcherv3.StringMatcher + for _, header := range extAuth.HeadersToExtAuth { + headersToExtAuth = append(headersToExtAuth, &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: header, + }, + }) + } + + if len(headersToExtAuth) > 0 { + config.AllowedHeaders = &matcherv3.ListStringMatcher{ + Patterns: headersToExtAuth, + } + } + + if extAuth.HTTP != nil { + config.Services = &extauthv3.ExtAuthz_HttpService{ + HttpService: httpService(extAuth.HTTP), + } + } else if extAuth.GRPC != nil { + config.Services = &extauthv3.ExtAuthz_GrpcService{ + GrpcService: &corev3.GrpcService{ + TargetSpecifier: &corev3.GrpcService_EnvoyGrpc_{ + EnvoyGrpc: grpcService(extAuth.GRPC), + }, + Timeout: &duration.Duration{ + Seconds: defaultExtServiceRequestTimeout, + }, + }, + } + } + + return config +} + +func httpService(http *ir.HTTPExtAuthService) *extauthv3.HttpService { + var ( + uri string + headersToBackend []*matcherv3.StringMatcher + service *extauthv3.HttpService + ) + + service = &extauthv3.HttpService{ + PathPrefix: http.Path, + } + + u := url.URL{ + // scheme should be decided by the TLS setting, but we don't have that info now. + // It's safe to set it to http because the ext auth filter doesn't use the + // uri to make the request. It only uses the cluster. + Scheme: "http", + Host: http.Authority, + Path: http.Path, + } + uri = u.String() + + service.ServerUri = &corev3.HttpUri{ + Uri: uri, + HttpUpstreamType: &corev3.HttpUri_Cluster{ + Cluster: http.Destination.Name, + }, + Timeout: &duration.Duration{ + Seconds: defaultExtServiceRequestTimeout, + }, + } + + for _, header := range http.HeadersToBackend { + headersToBackend = append(headersToBackend, &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: header, + }, + }) + } + + if len(headersToBackend) > 0 { + service.AuthorizationResponse = &extauthv3.AuthorizationResponse{ + AllowedUpstreamHeaders: &matcherv3.ListStringMatcher{ + Patterns: headersToBackend, + }, + } + } + + return service +} + +func grpcService(grpc *ir.GRPCExtAuthService) *corev3.GrpcService_EnvoyGrpc { + return &corev3.GrpcService_EnvoyGrpc{ + ClusterName: grpc.Destination.Name, + Authority: grpc.Authority, + } +} + +// routeContainsExtAuth returns true if ExtAuth exists for the provided route. +func routeContainsExtAuth(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.ExtAuth != nil { + return true + } + + return false +} + +// patchResources patches the cluster resources for the external auth services. +func (*extAuth) patchResources(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if tCtx == nil || tCtx.XdsResources == nil { + return errors.New("xds resource table is nil") + } + + var errs error + for _, route := range routes { + if !routeContainsExtAuth(route) { + continue + } + if route.ExtAuth.HTTP != nil { + if err := createExtServiceXDSCluster( + &route.ExtAuth.HTTP.Destination, tCtx); err != nil && !errors.Is( + err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } else { + if err := createExtServiceXDSCluster( + &route.ExtAuth.GRPC.Destination, tCtx); err != nil && !errors.Is( + err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } + } + + return errs +} + +func createExtServiceXDSCluster(rd *ir.RouteDestination, tCtx *types.ResourceVersionTable) error { + var ( + endpointType EndpointType + tSocket *corev3.TransportSocket + err error + ) + + // Get the address type from the first setting. + // This is safe because no mixed address types in the settings. + addrTypeState := rd.Settings[0].AddressType + if addrTypeState != nil && *addrTypeState == ir.FQDN { + endpointType = EndpointTypeDNS + } else { + endpointType = EndpointTypeStatic + } + + if rd.Settings[0].TLS != nil { + tSocket, err = processTLSSocket(rd.Settings[0].TLS, tCtx) + if err != nil { + return err + } + } + + if err = addXdsCluster(tCtx, &xdsClusterArgs{ + name: rd.Name, + settings: rd.Settings, + tSocket: tSocket, + endpointType: endpointType, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + return nil +} + +// patchRoute patches the provided route with the extAuth config if applicable. +// Note: this method enables the corresponding extAuth filter for the provided route. +func (*extAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.ExtAuth == nil { + return nil + } + return enableFilterOnRoute(extAuthFilter, route, irRoute) +} diff --git a/adapter/internal/operator/gateway-api/translator/fault.go b/adapter/internal/operator/gateway-api/translator/fault.go new file mode 100644 index 0000000000..3e8858033d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/fault.go @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + xdsfault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3" + xdshttpfaultv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" +) + +const ( + faultFilter = "envoy.filters.http.fault" +) + +func init() { + registerHTTPFilter(&fault{}) +} + +type fault struct { +} + +var _ httpFilter = &fault{} + +// patchHCM builds and appends the fault Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an fault filter for each route that contains an Fault config. +func (*fault) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + if !listenerContainsFault(irListener) { + return nil + } + + // Return early if the fault filter already exists. + for _, existingFilter := range mgr.HttpFilters { + if existingFilter.Name == faultFilter { + return nil + } + } + + faultFilter, err := buildHCMFaultFilter(irListener) + if err != nil { + return err + } + mgr.HttpFilters = append(mgr.HttpFilters, faultFilter) + + return nil +} + +// buildHCMFaultFilter returns a basic_auth HTTP filter from the provided IR HTTPRoute. +func buildHCMFaultFilter(irListener *ir.HTTPListener) (*hcmv3.HttpFilter, error) { + faultProto := faultConfig(irListener) + + if err := faultProto.ValidateAll(); err != nil { + return nil, err + } + + faultAny, err := anypb.New(faultProto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: faultFilter, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: faultAny, + }, + }, nil +} + +func faultConfig(irListener *ir.HTTPListener) *xdshttpfaultv3.HTTPFault { + return &xdshttpfaultv3.HTTPFault{} +} + +// listenerContainsFault returns true if Fault exists for the provided listener. +func listenerContainsFault(irListener *ir.HTTPListener) bool { + for _, route := range irListener.Routes { + if routeContainsFault(route) { + return true + } + } + return false +} + +// routeContainsFault returns true if Fault exists for the provided route. +func routeContainsFault(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.FaultInjection != nil { + return true + } + + return false +} + +func (*fault) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error { + return nil +} + +// patchRoute patches the provided route with the fault config if applicable. +// Note: this method enables the corresponding fault filter for the provided route. +func (*fault) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.FaultInjection == nil { + return nil + } + + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[wellknown.Fault]; ok { + // This should not happen since this is the only place where the fault + // filter is added in a route. + return fmt.Errorf("route already contains fault config: %+v", route) + } + + routeCfgProto := &xdshttpfaultv3.HTTPFault{} + + if irRoute.FaultInjection.Delay != nil { + routeCfgProto.Delay = &xdsfault.FaultDelay{} + if irRoute.FaultInjection.Delay.Percentage != nil { + routeCfgProto.Delay.Percentage = translatePercentToFractionalPercent(irRoute.FaultInjection.Delay.Percentage) + } + if irRoute.FaultInjection.Delay.FixedDelay != nil { + routeCfgProto.Delay.FaultDelaySecifier = &xdsfault.FaultDelay_FixedDelay{ + FixedDelay: durationpb.New(irRoute.FaultInjection.Delay.FixedDelay.Duration), + } + } + } + + if irRoute.FaultInjection.Abort != nil { + routeCfgProto.Abort = &xdshttpfaultv3.FaultAbort{} + if irRoute.FaultInjection.Abort.Percentage != nil { + routeCfgProto.Abort.Percentage = translatePercentToFractionalPercent(irRoute.FaultInjection.Abort.Percentage) + } + if irRoute.FaultInjection.Abort.HTTPStatus != nil { + routeCfgProto.Abort.ErrorType = &xdshttpfaultv3.FaultAbort_HttpStatus{ + HttpStatus: uint32(*irRoute.FaultInjection.Abort.HTTPStatus), + } + } + if irRoute.FaultInjection.Abort.GrpcStatus != nil { + routeCfgProto.Abort.ErrorType = &xdshttpfaultv3.FaultAbort_GrpcStatus{ + GrpcStatus: uint32(*irRoute.FaultInjection.Abort.GrpcStatus), + } + } + + } + + if routeCfgProto.Delay == nil && routeCfgProto.Abort == nil { + return nil + } + + routeCfgAny, err := anypb.New(routeCfgProto) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[wellknown.Fault] = routeCfgAny + + return nil + +} + +// translatePercentToFractionalPercent translates an v1alpha3 Percent instance +// to an envoy.type.FractionalPercent instance. +func translatePercentToFractionalPercent(p *float32) *xdstype.FractionalPercent { + return &xdstype.FractionalPercent{ + Numerator: uint32(*p * 10000), + Denominator: xdstype.FractionalPercent_MILLION, + } +} diff --git a/adapter/internal/operator/gateway-api/translator/httpfilters.go b/adapter/internal/operator/gateway-api/translator/httpfilters.go new file mode 100644 index 0000000000..5086dc8919 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/httpfilters.go @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "sort" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/xds/filters" + "github.com/wso2/apk/adapter/internal/types" + "k8s.io/utils/ptr" +) + +var httpFilters []httpFilter + +// registerHTTPFilter registers the provided HTTP filter. +func registerHTTPFilter(filter httpFilter) { + httpFilters = append(httpFilters, filter) +} + +// httpFilter is the interface for all the HTTP filters. +// +// There are two ways to support per-route configuration for an HTTP filter: +// - For the filters with native per-route configuration support: +// - patchHCM: EG adds the filter to the HCM filter chain only once. +// - patchRoute: EG adds the filter's native per-route configuration to each +// route's typedFilterConfig. +// +// - For the filters without native per-route configuration support: +// - patchHCM: EG adds a filter for each route in the HCM filter chain, the +// filter name is prefixed with the filter's type name, for example, +// "envoy.filters.http.oauth2", and suffixed with the route name. Each filter +// is configured with the route's per-route configuration. The filter is +// disabled by default and is enabled on the route level. +// - PatchRouteWithPerRouteConfig: EG enables the corresponding filter for each +// route in the typedFilterConfig of that route. +// +// The filter types that haven't native per-route support: oauth2, basic authn, ext_authz. +// Note: The filter types that have native per-route configuration support should +// always se their own native per-route configuration. +type httpFilter interface { + // patchHCM patches the HttpConnectionManager with the filter. + // Note: this method may be called multiple times for the same filter, please + // make sure to avoid duplicate additions of the same filter. + patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error + + // patchRoute patches the provide Route with a filter's Route level configuration. + patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error + + // patchResources adds all the other needed resources referenced by this + // filter to the resource version table. + // for example: + // - a jwt filter needs to add the cluster for the jwks. + // - an oidc filter needs to add the cluster for token endpoint and the secret + // for the oauth2 client secret and the hmac secret. + patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error +} + +type OrderedHTTPFilter struct { + filter *hcmv3.HttpFilter + order int +} + +type OrderedHTTPFilters []*OrderedHTTPFilter + +// newOrderedHTTPFilter gives each HTTP filter a rational order. +// This is needed because the order of the filters is important. +// For example, the cors filter should be put at the first to avoid unnecessary +// processing of other filters for unauthorized cross-region access. +// The router filter must be the last one since it's a terminal filter. +// +// Important: please modify this method and set the order for the new filter +// when adding a new filter in the HCM filter chain. +// If the order is not explicitly specified in this method, a filter will be set +// a default order 50. +func newOrderedHTTPFilter(filter *hcmv3.HttpFilter) *OrderedHTTPFilter { + order := 50 + + // Set a rational order for all the filters. + switch { + case filter.Name == wellknown.CORS: + order = 1 + case isFilterType(filter, extAuthFilter): + order = 2 + case isFilterType(filter, basicAuthFilter): + order = 3 + case isFilterType(filter, oauth2Filter): + order = 4 + case filter.Name == wellknown.Fault: + order = 6 + // case filter.Name == localRateLimitFilter: + // order = 7 + case filter.Name == wellknown.HTTPRateLimit: + order = 8 + case filter.Name == wellknown.Router: + order = 100 + } + + return &OrderedHTTPFilter{ + filter: filter, + order: order, + } +} + +// sort.Interface implementation. + +func (o OrderedHTTPFilters) Len() int { + return len(o) +} + +func (o OrderedHTTPFilters) Less(i, j int) bool { + return o[i].order < o[j].order +} + +func (o OrderedHTTPFilters) Swap(i, j int) { + o[i], o[j] = o[j], o[i] +} + +// sortHTTPFilters sorts the HTTP filters in the correct order. +// This is needed because the order of the filters is important. +// For example, the cors filter should be put at the first to avoid unnecessary +// processing of other filters for unauthorized cross-region access. +// The router filter must be the last one since it's a terminal filter. +func sortHTTPFilters(filters []*hcmv3.HttpFilter) []*hcmv3.HttpFilter { + orderedFilters := make(OrderedHTTPFilters, len(filters)) + for i := 0; i < len(filters); i++ { + orderedFilters[i] = newOrderedHTTPFilter(filters[i]) + } + sort.Sort(orderedFilters) + + for i := 0; i < len(filters); i++ { + filters[i] = orderedFilters[i].filter + } + return filters +} + +// patchHCMWithFilters builds and appends HTTP Filters to the HTTP connection +// manager. +// Important: don't forget to set the order for newly added filters in the +// newOrderedHTTPFilter method. +func (t *Translator) patchHCMWithFilters( + mgr *hcmv3.HttpConnectionManager, + irListener *ir.HTTPListener) error { + // The order of filter patching is not relevant here. + // All the filters will be sorted in correct order after the patching is done. + // + // Important: don't forget to set the order for new filters in the + // newOrderedHTTPFilter method. + for _, filter := range httpFilters { + if err := filter.patchHCM(mgr, irListener); err != nil { + return err + } + } + + // // RateLimit filter is handled separately because it relies on the global + // // rate limit server configuration. + // t.patchHCMWithRateLimit(mgr, irListener) + + // Add the router filter if it doesn't exist. + hasRouter := false + for _, filter := range mgr.HttpFilters { + if filter.Name == wellknown.Router { + hasRouter = true + break + } + } + if !hasRouter { + headerSettings := ptr.Deref(irListener.Headers, ir.HeaderSettings{}) + mgr.HttpFilters = append(mgr.HttpFilters, filters.GenerateRouterFilter(headerSettings.EnableEnvoyHeaders)) + } + + // Sort the filters in the correct order. + mgr.HttpFilters = sortHTTPFilters(mgr.HttpFilters) + return nil +} + +// patchRouteWithPerRouteConfig appends per-route filter configuration to the +// provided route. +func patchRouteWithPerRouteConfig( + route *routev3.Route, + irRoute *ir.HTTPRoute) error { + + for _, filter := range httpFilters { + if err := filter.patchRoute(route, irRoute); err != nil { + return err + } + } + + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + // if err := + // patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil { + // return nil + // } + + return nil +} + +// isFilterType returns true if the filter is the provided filter type. +func isFilterType(filter *hcmv3.HttpFilter, filterType string) bool { + // Multiple filters of the same types are added to the HCM filter chain, one for each + // route. The filter name is prefixed with the filter type, for example: + // "envoy.filters.http.oauth2_first-route". + return strings.HasPrefix(filter.Name, filterType) +} + +// patchResources adds all the other needed resources referenced by this +// filter to the resource version table. +// for example: +// - a jwt filter needs to add the cluster for the jwks. +// - an oidc filter needs to add the secret for the oauth2 client secret. +func patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + for _, filter := range httpFilters { + if err := filter.patchResources(tCtx, routes); err != nil { + return err + } + } + return nil +} diff --git a/adapter/internal/operator/gateway-api/translator/listener.go b/adapter/internal/operator/gateway-api/translator/listener.go new file mode 100644 index 0000000000..49bd76dcce --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/listener.go @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + + xdscore "github.com/cncf/xds/go/xds/core/v3" + matcher "github.com/cncf/xds/go/xds/type/matcher/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + tls_inspectorv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tcpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" + udpv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3" + preservecasev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3" + quicv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/golang/protobuf/ptypes/wrappers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + xdsfilters "github.com/wso2/apk/adapter/internal/operator/gateway-api/xds/filters" + "github.com/wso2/apk/adapter/pkg/utils/protocov" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/wrapperspb" + "k8s.io/utils/ptr" +) + +const ( + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener.proto#envoy-v3-api-field-config-listener-v3-listener-per-connection-buffer-limit-bytes + tcpListenerPerConnectionBufferLimitBytes = 32768 + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-max-concurrent-streams + http2MaxConcurrentStreamsLimit = 100 + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-stream-window-size + http2InitialStreamWindowSize = 65536 // 64 KiB + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-connection-window-size + http2InitialConnectionWindowSize = 1048576 // 1 MiB +) + +func http1ProtocolOptions(opts *ir.HTTP1Settings) *corev3.Http1ProtocolOptions { + if opts == nil { + return nil + } + if !opts.EnableTrailers && !opts.PreserveHeaderCase && opts.HTTP10 == nil { + return nil + } + // If PreserveHeaderCase is true and EnableTrailers is false then setting the EnableTrailers field to false + // is simply keeping it at its default value of "disabled". + r := &corev3.Http1ProtocolOptions{ + EnableTrailers: opts.EnableTrailers, + } + if opts.PreserveHeaderCase { + preservecaseAny, _ := anypb.New(&preservecasev3.PreserveCaseFormatterConfig{}) + r.HeaderKeyFormat = &corev3.Http1ProtocolOptions_HeaderKeyFormat{ + HeaderFormat: &corev3.Http1ProtocolOptions_HeaderKeyFormat_StatefulFormatter{ + StatefulFormatter: &corev3.TypedExtensionConfig{ + Name: "preserve_case", + TypedConfig: preservecaseAny, + }, + }, + } + } + if opts.HTTP10 != nil { + r.AcceptHttp_10 = true + r.DefaultHostForHttp_10 = ptr.Deref(opts.HTTP10.DefaultHost, "") + } + return r +} + +func http2ProtocolOptions() *corev3.Http2ProtocolOptions { + return &corev3.Http2ProtocolOptions{ + MaxConcurrentStreams: &wrappers.UInt32Value{ + Value: http2MaxConcurrentStreamsLimit, + }, + InitialStreamWindowSize: &wrappers.UInt32Value{ + Value: http2InitialStreamWindowSize, + }, + InitialConnectionWindowSize: &wrappers.UInt32Value{ + Value: http2InitialConnectionWindowSize, + }, + } +} + +// func xffNumTrustedHops(clientIPDetection *ir.ClientIPDetectionSettings) uint32 { +// if clientIPDetection != nil && clientIPDetection.XForwardedFor != nil && clientIPDetection.XForwardedFor.NumTrustedHops != nil { +// return *clientIPDetection.XForwardedFor.NumTrustedHops +// } +// return 0 +// } + +// func originalIPDetectionExtensions(clientIPDetection *ir.ClientIPDetectionSettings) []*corev3.TypedExtensionConfig { +// // Return early if settings are nil +// if clientIPDetection == nil { +// return nil +// } + +// var extensionConfig []*corev3.TypedExtensionConfig + +// // Custom header extension +// if clientIPDetection.CustomHeader != nil { +// var rejectWithStatus *typev3.HttpStatus +// if ptr.Deref(clientIPDetection.CustomHeader.FailClosed, false) { +// rejectWithStatus = &typev3.HttpStatus{Code: typev3.StatusCode_Forbidden} +// } + +// customHeaderConfigAny, _ := anypb.New(&customheaderv3.CustomHeaderConfig{ +// HeaderName: clientIPDetection.CustomHeader.Name, +// RejectWithStatus: rejectWithStatus, + +// AllowExtensionToSetAddressAsTrusted: true, +// }) + +// extensionConfig = append(extensionConfig, &corev3.TypedExtensionConfig{ +// Name: "envoy.extensions.http.original_ip_detection.custom_header", +// TypedConfig: customHeaderConfigAny, +// }) +// } + +// return extensionConfig +// } + +// buildXdsTCPListener creates a xds Listener resource +// TODO: Improve function parameters +func buildXdsTCPListener(name, address string, port uint32, keepalive *ir.TCPKeepalive, accesslog *ir.AccessLog) *listenerv3.Listener { + socketOptions := buildTCPSocketOptions(keepalive) + al := buildXdsAccessLog(accesslog, true) + return &listenerv3.Listener{ + Name: name, + AccessLog: al, + SocketOptions: socketOptions, + PerConnectionBufferLimitBytes: wrapperspb.UInt32(tcpListenerPerConnectionBufferLimitBytes), + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_TCP, + Address: address, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: port, + }, + }, + }, + }, + // Remove /healthcheck/fail from endpoints that trigger a drain of listeners for better control + // over the drain process while still allowing the healthcheck to be failed during pod shutdown. + DrainType: listenerv3.Listener_MODIFY_ONLY, + } +} + +// buildXdsQuicListener creates a xds Listener resource for quic +func buildXdsQuicListener(name, address string, port uint32, accesslog *ir.AccessLog) *listenerv3.Listener { + xdsListener := &listenerv3.Listener{ + Name: name + "-quic", + AccessLog: buildXdsAccessLog(accesslog, true), + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_UDP, + Address: address, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: port, + }, + }, + }, + }, + UdpListenerConfig: &listenerv3.UdpListenerConfig{ + DownstreamSocketConfig: &corev3.UdpSocketConfig{}, + QuicOptions: &listenerv3.QuicProtocolOptions{}, + }, + // Remove /healthcheck/fail from endpoints that trigger a drain of listeners for better control + // over the drain process while still allowing the healthcheck to be failed during pod shutdown. + DrainType: listenerv3.Listener_MODIFY_ONLY, + } + + return xdsListener +} + +func (t *Translator) addXdsHTTPFilterChain(xdsListener *listenerv3.Listener, irListener *ir.HTTPListener, + accesslog *ir.AccessLog, tracing *ir.Tracing, http3Listener bool) error { + al := buildXdsAccessLog(accesslog, false) + + hcmTracing, err := buildHCMTracing(tracing) + if err != nil { + return err + } + + // HTTP filter configuration + var statPrefix string + if irListener.TLS != nil { + statPrefix = "https" + } else { + statPrefix = "http" + } + + // Client IP detection + var useRemoteAddress = true + // var originalIPDetectionExtensions = originalIPDetectionExtensions(irListener.ClientIPDetection) + // if originalIPDetectionExtensions != nil { + // useRemoteAddress = false + // } + + mgr := &hcmv3.HttpConnectionManager{ + AccessLog: al, + CodecType: hcmv3.HttpConnectionManager_AUTO, + StatPrefix: statPrefix, + RouteSpecifier: &hcmv3.HttpConnectionManager_Rds{ + Rds: &hcmv3.Rds{ + ConfigSource: makeConfigSource(), + // Configure route name to be found via RDS. + RouteConfigName: irListener.Name, + }, + }, + HttpProtocolOptions: http1ProtocolOptions(irListener.HTTP1), + // Hide the Envoy proxy in the Server header by default + ServerHeaderTransformation: hcmv3.HttpConnectionManager_PASS_THROUGH, + // Add HTTP2 protocol options + // Set it by default to also support HTTP1.1 to HTTP2 Upgrades + Http2ProtocolOptions: http2ProtocolOptions(), + // https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for + UseRemoteAddress: &wrappers.BoolValue{Value: useRemoteAddress}, + // XffNumTrustedHops: xffNumTrustedHops(irListener.ClientIPDetection), + // OriginalIpDetectionExtensions: originalIPDetectionExtensions, + // normalize paths according to RFC 3986 + NormalizePath: &wrapperspb.BoolValue{Value: true}, + MergeSlashes: irListener.Path.MergeSlashes, + // PathWithEscapedSlashesAction: translateEscapePath(irListener.Path.EscapedSlashesAction), + CommonHttpProtocolOptions: &corev3.HttpProtocolOptions{ + HeadersWithUnderscoresAction: corev3.HttpProtocolOptions_REJECT_REQUEST, + }, + Tracing: hcmTracing, + } + + if irListener.Timeout != nil && irListener.Timeout.HTTP != nil && irListener.Timeout.HTTP.RequestReceivedTimeout != nil { + mgr.RequestTimeout = durationpb.New(irListener.Timeout.HTTP.RequestReceivedTimeout.Duration) + } + + // Add the proxy protocol filter if needed + patchProxyProtocolFilter(xdsListener, irListener) + + if irListener.IsHTTP2 { + mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.GRPCWeb) + // always enable grpc stats filter + mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.GRPCStats) + } + + if http3Listener { + mgr.CodecType = hcmv3.HttpConnectionManager_HTTP3 + mgr.Http3ProtocolOptions = &corev3.Http3ProtocolOptions{} + } + // Add HTTP filters to the HCM, the filters have already been sorted in the + // correct order in the patchHCMWithFilters function. + if err := t.patchHCMWithFilters(mgr, irListener); err != nil { + return err + } + + mgrAny, err := protocov.ToAnyWithError(mgr) + if err != nil { + return err + } + + filterChain := &listenerv3.FilterChain{ + Filters: []*listenerv3.Filter{{ + Name: wellknown.HTTPConnectionManager, + ConfigType: &listenerv3.Filter_TypedConfig{ + TypedConfig: mgrAny, + }, + }}, + } + + if irListener.TLS != nil { + var tSocket *corev3.TransportSocket + if http3Listener { + tSocket, err = buildDownstreamQUICTransportSocket(irListener.TLS, http3Listener) + if err != nil { + return err + } + } else { + tSocket, err = buildXdsDownstreamTLSSocket(irListener.TLS, http3Listener) + if err != nil { + return err + } + } + filterChain.TransportSocket = tSocket + if err := addServerNamesMatch(xdsListener, filterChain, irListener.Hostnames); err != nil { + return err + } + + xdsListener.FilterChains = append(xdsListener.FilterChains, filterChain) + } else { + // Add the HTTP filter chain as the default filter chain + // Make sure one does not exist + if xdsListener.DefaultFilterChain != nil { + return errors.New("default filter chain already exists") + } + xdsListener.DefaultFilterChain = filterChain + } + + return nil +} + +func addServerNamesMatch(xdsListener *listenerv3.Listener, filterChain *listenerv3.FilterChain, hostnames []string) error { + // Dont add a filter chain match if the hostname is a wildcard character. + if len(hostnames) > 0 && hostnames[0] != "*" { + filterChain.FilterChainMatch = &listenerv3.FilterChainMatch{ + ServerNames: hostnames, + } + + if err := addXdsTLSInspectorFilter(xdsListener); err != nil { + return err + } + } + + return nil +} + +// findXdsHTTPRouteConfigName finds the name of the route config associated with the +// http connection manager within the default filter chain and returns an empty string if +// not found. +func findXdsHTTPRouteConfigName(xdsListener *listenerv3.Listener) string { + if xdsListener == nil || xdsListener.DefaultFilterChain == nil || xdsListener.DefaultFilterChain.Filters == nil { + return "" + } + + for _, filter := range xdsListener.DefaultFilterChain.Filters { + if filter.Name == wellknown.HTTPConnectionManager { + m := new(hcmv3.HttpConnectionManager) + if err := filter.GetTypedConfig().UnmarshalTo(m); err != nil { + return "" + } + rds := m.GetRds() + if rds == nil { + return "" + } + return rds.GetRouteConfigName() + } + } + return "" +} + +func addXdsTCPFilterChain(xdsListener *listenerv3.Listener, irListener *ir.TCPListener, clusterName string, accesslog *ir.AccessLog) error { + if irListener == nil { + return errors.New("tcp listener is nil") + } + + isTLSPassthrough := irListener.TLS != nil && irListener.TLS.Passthrough != nil + isTLSTerminate := irListener.TLS != nil && irListener.TLS.Terminate != nil + statPrefix := "tcp" + if isTLSPassthrough { + statPrefix = "passthrough" + } + + if isTLSTerminate { + statPrefix = "terminate" + } + + mgr := &tcpv3.TcpProxy{ + AccessLog: buildXdsAccessLog(accesslog, false), + StatPrefix: statPrefix, + ClusterSpecifier: &tcpv3.TcpProxy_Cluster{ + Cluster: clusterName, + }, + } + mgrAny, err := anypb.New(mgr) + if err != nil { + return err + } + + filterChain := &listenerv3.FilterChain{ + Filters: []*listenerv3.Filter{{ + Name: wellknown.TCPProxy, + ConfigType: &listenerv3.Filter_TypedConfig{ + TypedConfig: mgrAny, + }, + }}, + } + + if isTLSPassthrough { + if err := addServerNamesMatch(xdsListener, filterChain, irListener.TLS.Passthrough.SNIs); err != nil { + return err + } + } + + if isTLSTerminate { + tSocket, err := buildXdsDownstreamTLSSocket(irListener.TLS.Terminate, false) + if err != nil { + return err + } + filterChain.TransportSocket = tSocket + } + + xdsListener.FilterChains = append(xdsListener.FilterChains, filterChain) + + return nil +} + +// addXdsTLSInspectorFilter adds a Tls Inspector filter if it does not yet exist. +func addXdsTLSInspectorFilter(xdsListener *listenerv3.Listener) error { + // Return early if it exists + for _, filter := range xdsListener.ListenerFilters { + if filter.Name == wellknown.TlsInspector { + return nil + } + } + + tlsInspector := &tls_inspectorv3.TlsInspector{} + tlsInspectorAny, err := anypb.New(tlsInspector) + if err != nil { + return err + } + + filter := &listenerv3.ListenerFilter{ + Name: wellknown.TlsInspector, + ConfigType: &listenerv3.ListenerFilter_TypedConfig{ + TypedConfig: tlsInspectorAny, + }, + } + + xdsListener.ListenerFilters = append(xdsListener.ListenerFilters, filter) + + return nil +} + +func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig, http3Listener bool) (*corev3.TransportSocket, error) { + tlsCtx := &quicv3.QuicDownstreamTransport{ + DownstreamTlsContext: &tlsv3.DownstreamTlsContext{ + CommonTlsContext: &tlsv3.CommonTlsContext{ + TlsParams: buildTLSParams(tlsConfig), + AlpnProtocols: buildALPNProtocols(tlsConfig.ALPNProtocols, http3Listener), + }, + }, + } + + for _, tlsConfig := range tlsConfig.Certificates { + tlsCtx.DownstreamTlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append( + tlsCtx.DownstreamTlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs, + &tlsv3.SdsSecretConfig{ + Name: tlsConfig.Name, + SdsConfig: makeConfigSource(), + }) + } + + if tlsConfig.CACertificate != nil { + tlsCtx.DownstreamTlsContext.RequireClientCertificate = &wrappers.BoolValue{Value: true} + tlsCtx.DownstreamTlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ + ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{ + Name: tlsConfig.CACertificate.Name, + SdsConfig: makeConfigSource(), + }, + } + } + + tlsCtxAny, err := anypb.New(tlsCtx) + if err != nil { + return nil, err + } + return &corev3.TransportSocket{ + Name: wellknown.TransportSocketQuic, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsCtxAny, + }, + }, nil +} + +func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig, http3Listener bool) (*corev3.TransportSocket, error) { + tlsCtx := &tlsv3.DownstreamTlsContext{ + CommonTlsContext: &tlsv3.CommonTlsContext{ + TlsParams: buildTLSParams(tlsConfig), + AlpnProtocols: buildALPNProtocols(tlsConfig.ALPNProtocols, http3Listener), + TlsCertificateSdsSecretConfigs: []*tlsv3.SdsSecretConfig{}, + }, + } + + for _, tlsConfig := range tlsConfig.Certificates { + tlsCtx.CommonTlsContext.TlsCertificateSdsSecretConfigs = append( + tlsCtx.CommonTlsContext.TlsCertificateSdsSecretConfigs, + &tlsv3.SdsSecretConfig{ + Name: tlsConfig.Name, + SdsConfig: makeConfigSource(), + }) + } + + if tlsConfig.CACertificate != nil { + tlsCtx.RequireClientCertificate = &wrappers.BoolValue{Value: true} + tlsCtx.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ + ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{ + Name: tlsConfig.CACertificate.Name, + SdsConfig: makeConfigSource(), + }, + } + } + + tlsCtxAny, err := anypb.New(tlsCtx) + if err != nil { + return nil, err + } + + return &corev3.TransportSocket{ + Name: wellknown.TransportSocketTls, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsCtxAny, + }, + }, nil +} + +func buildTLSParams(tlsConfig *ir.TLSConfig) *tlsv3.TlsParameters { + p := &tlsv3.TlsParameters{} + isEmpty := true + if tlsConfig.MinVersion != nil { + p.TlsMinimumProtocolVersion = buildTLSVersion(tlsConfig.MinVersion) + isEmpty = false + } + if tlsConfig.MaxVersion != nil { + p.TlsMaximumProtocolVersion = buildTLSVersion(tlsConfig.MaxVersion) + isEmpty = false + } + if len(tlsConfig.Ciphers) > 0 { + p.CipherSuites = tlsConfig.Ciphers + isEmpty = false + } + if len(tlsConfig.ECDHCurves) > 0 { + p.EcdhCurves = tlsConfig.ECDHCurves + isEmpty = false + } + if len(tlsConfig.SignatureAlgorithms) > 0 { + p.SignatureAlgorithms = tlsConfig.SignatureAlgorithms + isEmpty = false + } + if isEmpty { + return nil + } + return p +} + +func buildTLSVersion(version *ir.TLSVersion) tlsv3.TlsParameters_TlsProtocol { + lookup := map[ir.TLSVersion]tlsv3.TlsParameters_TlsProtocol{ + ir.TLSv10: tlsv3.TlsParameters_TLSv1_0, + ir.TLSv11: tlsv3.TlsParameters_TLSv1_1, + ir.TLSv12: tlsv3.TlsParameters_TLSv1_2, + ir.TLSv13: tlsv3.TlsParameters_TLSv1_3, + } + if r, found := lookup[*version]; found { + return r + } + return tlsv3.TlsParameters_TLS_AUTO +} + +func buildALPNProtocols(alpn []string, http3Listener bool) []string { + if len(alpn) == 0 { + out := []string{"h2", "http/1.1"} + if http3Listener { + out = append(out, "h3") + } + return out + } + return alpn +} + +func buildXdsTLSCertSecret(tlsConfig ir.TLSCertificate) *tlsv3.Secret { + return &tlsv3.Secret{ + Name: tlsConfig.Name, + Type: &tlsv3.Secret_TlsCertificate{ + TlsCertificate: &tlsv3.TlsCertificate{ + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{InlineBytes: tlsConfig.ServerCertificate}, + }, + PrivateKey: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{InlineBytes: tlsConfig.PrivateKey}, + }, + }, + }, + } +} + +func buildXdsTLSCaCertSecret(caCertificate *ir.TLSCACertificate) *tlsv3.Secret { + return &tlsv3.Secret{ + Name: caCertificate.Name, + Type: &tlsv3.Secret_ValidationContext{ + ValidationContext: &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{InlineBytes: caCertificate.Certificate}, + }, + }, + }, + } +} + +func buildXdsUDPListener(clusterName string, udpListener *ir.UDPListener, accesslog *ir.AccessLog) (*listenerv3.Listener, error) { + if udpListener == nil { + return nil, errors.New("udp listener is nil") + } + + statPrefix := "service" + + route := &udpv3.Route{ + Cluster: clusterName, + } + routeAny, err := anypb.New(route) + if err != nil { + return nil, err + } + + udpProxy := &udpv3.UdpProxyConfig{ + StatPrefix: statPrefix, + AccessLog: buildXdsAccessLog(accesslog, false), + RouteSpecifier: &udpv3.UdpProxyConfig_Matcher{ + Matcher: &matcher.Matcher{ + OnNoMatch: &matcher.Matcher_OnMatch{ + OnMatch: &matcher.Matcher_OnMatch_Action{ + Action: &xdscore.TypedExtensionConfig{ + Name: "route", + TypedConfig: routeAny, + }, + }, + }, + }, + }, + } + udpProxyAny, err := anypb.New(udpProxy) + if err != nil { + return nil, err + } + + xdsListener := &listenerv3.Listener{ + Name: udpListener.Name, + AccessLog: buildXdsAccessLog(accesslog, true), + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_UDP, + Address: udpListener.Address, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: udpListener.Port, + }, + }, + }, + }, + ListenerFilters: []*listenerv3.ListenerFilter{{ + Name: "envoy.filters.udp_listener.udp_proxy", + ConfigType: &listenerv3.ListenerFilter_TypedConfig{ + TypedConfig: udpProxyAny, + }, + }}, + } + + return xdsListener, nil +} + +// Point to xds cluster. +func makeConfigSource() *corev3.ConfigSource { + source := &corev3.ConfigSource{} + source.ResourceApiVersion = resource.DefaultAPIVersion + source.ConfigSourceSpecifier = &corev3.ConfigSource_Ads{ + Ads: &corev3.AggregatedConfigSource{}, + } + return source +} + +// func translateEscapePath(in ir.PathEscapedSlashAction) hcmv3.HttpConnectionManager_PathWithEscapedSlashesAction { + +// lookup := map[ir.PathEscapedSlashAction]hcmv3.HttpConnectionManager_PathWithEscapedSlashesAction{ +// ir.KeepUnchangedAction: hcmv3.HttpConnectionManager_KEEP_UNCHANGED, +// ir.RejectRequestAction: hcmv3.HttpConnectionManager_REJECT_REQUEST, +// ir.UnescapeAndRedirect: hcmv3.HttpConnectionManager_UNESCAPE_AND_REDIRECT, +// ir.UnescapeAndForward: hcmv3.HttpConnectionManager_UNESCAPE_AND_FORWARD, +// } +// if r, found := lookup[in]; found { +// return r +// } +// return hcmv3.HttpConnectionManager_IMPLEMENTATION_SPECIFIC_DEFAULT +// } diff --git a/adapter/internal/operator/gateway-api/translator/oidc.go b/adapter/internal/operator/gateway-api/translator/oidc.go new file mode 100644 index 0000000000..08d87017f8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/oidc.go @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + oauth2v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/golang/protobuf/ptypes/duration" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "google.golang.org/protobuf/types/known/anypb" + "k8s.io/utils/ptr" +) + +const ( + oauth2Filter = "envoy.filters.http.oauth2" + envoyTrustBundle = "/etc/ssl/certs/ca-certificates.crt" +) + +func init() { + registerHTTPFilter(&oidc{}) +} + +type oidc struct { +} + +var _ httpFilter = &oidc{} + +// patchHCM builds and appends the oauth2 Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an oauth2 filter for each route that contains an OIDC config. +// the filter is disabled by default. It is enabled on the route level. +func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !routeContainsOIDC(route) { + continue + } + + filter, err := buildHCMOAuth2Filter(route) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return errs +} + +// buildHCMOAuth2Filter returns an OAuth2 HTTP filter from the provided IR HTTPRoute. +func buildHCMOAuth2Filter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) { + oauth2Proto, err := oauth2Config(route) + if err != nil { + return nil, err + } + + if err := oauth2Proto.ValidateAll(); err != nil { + return nil, err + } + + OAuth2Any, err := anypb.New(oauth2Proto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: oauth2FilterName(route), + Disabled: true, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: OAuth2Any, + }, + }, nil +} + +func oauth2FilterName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s_%s", oauth2Filter, route.Name) +} + +func oauth2Config(route *ir.HTTPRoute) (*oauth2v3.OAuth2, error) { + cluster, err := url2Cluster(route.OIDC.Provider.TokenEndpoint) + if err != nil { + return nil, err + } + if cluster.endpointType == EndpointTypeStatic { + return nil, fmt.Errorf( + "static IP cluster is not allowed: %s", + route.OIDC.Provider.TokenEndpoint) + } + + oauth2 := &oauth2v3.OAuth2{ + Config: &oauth2v3.OAuth2Config{ + TokenEndpoint: &corev3.HttpUri{ + Uri: route.OIDC.Provider.TokenEndpoint, + HttpUpstreamType: &corev3.HttpUri_Cluster{ + Cluster: cluster.name, + }, + Timeout: &duration.Duration{ + Seconds: defaultExtServiceRequestTimeout, + }, + }, + AuthorizationEndpoint: route.OIDC.Provider.AuthorizationEndpoint, + RedirectUri: route.OIDC.RedirectURL, + RedirectPathMatcher: &matcherv3.PathMatcher{ + Rule: &matcherv3.PathMatcher_Path{ + Path: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: route.OIDC.RedirectPath, + }, + }, + }, + }, + SignoutPath: &matcherv3.PathMatcher{ + Rule: &matcherv3.PathMatcher_Path{ + Path: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: route.OIDC.LogoutPath, + }, + }, + }, + }, + ForwardBearerToken: true, + Credentials: &oauth2v3.OAuth2Credentials{ + ClientId: route.OIDC.ClientID, + TokenSecret: &tlsv3.SdsSecretConfig{ + Name: oauth2ClientSecretName(route), + SdsConfig: makeConfigSource(), + }, + TokenFormation: &oauth2v3.OAuth2Credentials_HmacSecret{ + HmacSecret: &tlsv3.SdsSecretConfig{ + Name: oauth2HMACSecretName(route), + SdsConfig: makeConfigSource(), + }, + }, + CookieNames: &oauth2v3.OAuth2Credentials_CookieNames{ + BearerToken: fmt.Sprintf("BearerToken-%s", route.OIDC.CookieSuffix), + OauthHmac: fmt.Sprintf("OauthHMAC-%s", route.OIDC.CookieSuffix), + OauthExpires: fmt.Sprintf("OauthExpires-%s", route.OIDC.CookieSuffix), + IdToken: fmt.Sprintf("IdToken-%s", route.OIDC.CookieSuffix), + RefreshToken: fmt.Sprintf("RefreshToken-%s", route.OIDC.CookieSuffix), + }, + }, + // every OIDC provider supports basic auth + AuthType: oauth2v3.OAuth2Config_BASIC_AUTH, + AuthScopes: route.OIDC.Scopes, + }, + } + return oauth2, nil +} + +// routeContainsOIDC returns true if OIDC exists for the provided route. +func routeContainsOIDC(irRoute *ir.HTTPRoute) bool { + if irRoute == nil { + return false + } + + if irRoute != nil && + irRoute.OIDC != nil { + return true + } + + return false +} + +func (*oidc) patchResources(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if err := createOAuth2TokenEndpointClusters(tCtx, routes); err != nil { + return err + } + return createOAuth2Secrets(tCtx, routes) +} + +// createOAuth2TokenEndpointClusters creates token endpoint clusters from the +// provided routes, if needed. +func createOAuth2TokenEndpointClusters(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute) error { + if tCtx == nil || tCtx.XdsResources == nil { + return errors.New("xds resource table is nil") + } + + var errs error + for _, route := range routes { + if !routeContainsOIDC(route) { + continue + } + + var ( + cluster *urlCluster + ds *ir.DestinationSetting + tSocket *corev3.TransportSocket + err error + ) + + cluster, err = url2Cluster(route.OIDC.Provider.TokenEndpoint) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + // EG does not support static IP clusters for token endpoint clusters. + // This validation could be removed since it's already validated in the + // Gateway API translator. + if cluster.endpointType == EndpointTypeStatic { + errs = errors.Join(errs, fmt.Errorf( + "static IP cluster is not allowed: %s", + route.OIDC.Provider.TokenEndpoint)) + continue + } + + ds = &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint( + cluster.hostname, + cluster.port), + }, + } + + clusterArgs := &xdsClusterArgs{ + name: cluster.name, + settings: []*ir.DestinationSetting{ds}, + tSocket: tSocket, + endpointType: cluster.endpointType, + } + if cluster.tls { + tSocket, err = buildXdsUpstreamTLSSocket(cluster.hostname) + if err != nil { + errs = errors.Join(errs, err) + continue + } + clusterArgs.tSocket = tSocket + } + + if err = addXdsCluster(tCtx, clusterArgs); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } + + return errs +} + +// createOAuth2Secrets creates OAuth2 client and HMAC secrets from the provided +// routes, if needed. +func createOAuth2Secrets(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute) error { + var errs error + + for _, route := range routes { + if !routeContainsOIDC(route) { + continue + } + + // a separate secret is created for each route, even they share the same + // oauth2 client ID and secret. + clientSecret := buildOAuth2ClientSecret(route) + if err := addXdsSecret(tCtx, clientSecret); err != nil { + errs = errors.Join(errs, err) + } + + if err := addXdsSecret(tCtx, buildOAuth2HMACSecret(route)); err != nil { + errs = errors.Join(errs, err) + } + } + + return errs +} + +func buildOAuth2ClientSecret(route *ir.HTTPRoute) *tlsv3.Secret { + clientSecret := &tlsv3.Secret{ + Name: oauth2ClientSecretName(route), + Type: &tlsv3.Secret_GenericSecret{ + GenericSecret: &tlsv3.GenericSecret{ + Secret: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: route.OIDC.ClientSecret, + }, + }, + }, + }, + } + + return clientSecret +} + +func buildOAuth2HMACSecret(route *ir.HTTPRoute) *tlsv3.Secret { + hmacSecret := &tlsv3.Secret{ + Name: oauth2HMACSecretName(route), + Type: &tlsv3.Secret_GenericSecret{ + GenericSecret: &tlsv3.GenericSecret{ + Secret: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: route.OIDC.HMACSecret, + }, + }, + }, + }, + } + + return hmacSecret +} + +func oauth2ClientSecretName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s/oauth2/client_secret", route.Name) +} + +func oauth2HMACSecretName(route *ir.HTTPRoute) string { + return fmt.Sprintf("%s/oauth2/hmac_secret", route.Name) +} + +// patchRoute patches the provided route with the oauth2 config if applicable. +// Note: this method enables the corresponding oauth2 filter for the provided route. +func (*oidc) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.OIDC == nil { + return nil + } + + return enableFilterOnRoute(oauth2Filter, route, irRoute) +} + +// buildXdsUpstreamTLSSocket returns an xDS TransportSocket that uses envoyTrustBundle +// as the CA to authenticate server certificates. +// TODO huabing: add support for custom CA and client certificate. +func buildXdsUpstreamTLSSocket(sni string) (*corev3.TransportSocket, error) { + tlsCtxProto := &tlsv3.UpstreamTlsContext{ + Sni: sni, + CommonTlsContext: &tlsv3.CommonTlsContext{ + ValidationContextType: &tlsv3.CommonTlsContext_ValidationContext{ + ValidationContext: &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{ + Filename: envoyTrustBundle, + }, + }, + }, + }, + }, + } + + tlsCtxAny, err := anypb.New(tlsCtxProto) + if err != nil { + return nil, err + } + + return &corev3.TransportSocket{ + Name: wellknown.TransportSocketTls, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsCtxAny, + }, + }, nil +} diff --git a/adapter/internal/operator/gateway-api/translator/proxy_protocol.go b/adapter/internal/operator/gateway-api/translator/proxy_protocol.go new file mode 100644 index 0000000000..99f529cf7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/proxy_protocol.go @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + proxyprotocolv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "google.golang.org/protobuf/types/known/anypb" +) + +// patchProxyProtocolFilter builds and appends the Proxy Protocol Filter to the +// HTTP Listener's Listener Filters if applicable. +func patchProxyProtocolFilter(xdsListener *listenerv3.Listener, irListener *ir.HTTPListener) { + // Return early if unset + if xdsListener == nil || irListener == nil || !irListener.EnableProxyProtocol { + return + } + + // Return early if filter already exists. + for _, filter := range xdsListener.ListenerFilters { + if filter.Name == wellknown.ProxyProtocol { + return + } + } + + proxyProtocolFilter := buildProxyProtocolFilter() + + if proxyProtocolFilter != nil { + xdsListener.ListenerFilters = append(xdsListener.ListenerFilters, proxyProtocolFilter) + } +} + +// buildProxypProtocolFilter returns a Proxy Protocol listener filter from the provided IR listener. +func buildProxyProtocolFilter() *listenerv3.ListenerFilter { + pp := &proxyprotocolv3.ProxyProtocol{} + + ppAny, err := anypb.New(pp) + if err != nil { + return nil + } + + return &listenerv3.ListenerFilter{ + Name: wellknown.ProxyProtocol, + ConfigType: &listenerv3.ListenerFilter_TypedConfig{ + TypedConfig: ppAny, + }, + } +} diff --git a/adapter/internal/operator/gateway-api/translator/route.go b/adapter/internal/operator/gateway-api/translator/route.go new file mode 100644 index 0000000000..d0ee7873a9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/route.go @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "strings" + "time" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + previoushost "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/pkg/utils/protocov" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +const ( + retryDefaultRetryOn = "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes" + retryDefaultRetriableStatusCode = 503 + retryDefaultNumRetries = 2 +) + +func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) { + router := &routev3.Route{ + Name: httpRoute.Name, + Match: buildXdsRouteMatch(httpRoute.PathMatch, httpRoute.HeaderMatches, httpRoute.QueryParamMatches), + } + + if len(httpRoute.AddRequestHeaders) > 0 { + router.RequestHeadersToAdd = buildXdsAddedHeaders(httpRoute.AddRequestHeaders) + } + if len(httpRoute.RemoveRequestHeaders) > 0 { + router.RequestHeadersToRemove = httpRoute.RemoveRequestHeaders + } + + if len(httpRoute.AddResponseHeaders) > 0 { + router.ResponseHeadersToAdd = buildXdsAddedHeaders(httpRoute.AddResponseHeaders) + } + if len(httpRoute.RemoveResponseHeaders) > 0 { + router.ResponseHeadersToRemove = httpRoute.RemoveResponseHeaders + } + + switch { + case httpRoute.DirectResponse != nil: + router.Action = &routev3.Route_DirectResponse{DirectResponse: buildXdsDirectResponseAction(httpRoute.DirectResponse)} + case httpRoute.Redirect != nil: + router.Action = &routev3.Route_Redirect{Redirect: buildXdsRedirectAction(httpRoute)} + case httpRoute.URLRewrite != nil: + routeAction := buildXdsURLRewriteAction(httpRoute.Destination.Name, httpRoute.URLRewrite, httpRoute.PathMatch) + if httpRoute.Mirrors != nil { + routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors) + } + + if !httpRoute.IsHTTP2 { + // Allow websocket upgrades for HTTP 1.1 + // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism + routeAction.UpgradeConfigs = []*routev3.RouteAction_UpgradeConfig{ + { + UpgradeType: "websocket", + }, + } + } + + router.Action = &routev3.Route_Route{Route: routeAction} + default: + var routeAction *routev3.RouteAction + if httpRoute.BackendWeights.Invalid != 0 { + // If there are invalid backends then a weighted cluster is required for the route + routeAction = buildXdsWeightedRouteAction(httpRoute) + } else { + routeAction = buildXdsRouteAction(httpRoute) + } + if httpRoute.Mirrors != nil { + routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors) + } + if !httpRoute.IsHTTP2 { + // Allow websocket upgrades for HTTP 1.1 + // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism + routeAction.UpgradeConfigs = []*routev3.RouteAction_UpgradeConfig{ + { + UpgradeType: "websocket", + }, + } + } + router.Action = &routev3.Route_Route{Route: routeAction} + } + + // Hash Policy + if router.GetRoute() != nil { + router.GetRoute().HashPolicy = buildHashPolicy(httpRoute) + } + + // Timeouts + if router.GetRoute() != nil && httpRoute.Timeout != nil && httpRoute.Timeout.HTTP != nil && + httpRoute.Timeout.HTTP.RequestTimeout != nil { + router.GetRoute().Timeout = durationpb.New(httpRoute.Timeout.HTTP.RequestTimeout.Duration) + } + + // Retries + if router.GetRoute() != nil && httpRoute.Retry != nil { + if rp, err := buildRetryPolicy(httpRoute); err == nil { + router.GetRoute().RetryPolicy = rp + } else { + return nil, err + } + } + + // Add per route filter configs to the route, if needed. + if err := patchRouteWithPerRouteConfig(router, httpRoute); err != nil { + return nil, err + } + + return router, nil +} + +func buildXdsRouteMatch(pathMatch *ir.StringMatch, headerMatches []*ir.StringMatch, queryParamMatches []*ir.StringMatch) *routev3.RouteMatch { + outMatch := &routev3.RouteMatch{} + + // Add a prefix match to '/' if no matches are specified + if pathMatch == nil { + // Setup default path specifier. It may be overwritten by :host:. + outMatch.PathSpecifier = &routev3.RouteMatch_Prefix{ + Prefix: "/", + } + } else { + // Path match + //nolint:gocritic + if pathMatch.Exact != nil { + outMatch.PathSpecifier = &routev3.RouteMatch_Path{ + Path: *pathMatch.Exact, + } + } else if pathMatch.Prefix != nil { + if *pathMatch.Prefix == "/" { + outMatch.PathSpecifier = &routev3.RouteMatch_Prefix{ + Prefix: "/", + } + } else { + // Remove trailing / + trimmedPrefix := strings.TrimSuffix(*pathMatch.Prefix, "/") + outMatch.PathSpecifier = &routev3.RouteMatch_PathSeparatedPrefix{ + PathSeparatedPrefix: trimmedPrefix, + } + } + } else if pathMatch.SafeRegex != nil { + outMatch.PathSpecifier = &routev3.RouteMatch_SafeRegex{ + SafeRegex: &matcherv3.RegexMatcher{ + Regex: *pathMatch.SafeRegex, + }, + } + } + } + // Header matches + for _, headerMatch := range headerMatches { + stringMatcher := buildXdsStringMatcher(headerMatch) + + headerMatcher := &routev3.HeaderMatcher{ + Name: headerMatch.Name, + HeaderMatchSpecifier: &routev3.HeaderMatcher_StringMatch{ + StringMatch: stringMatcher, + }, + } + outMatch.Headers = append(outMatch.Headers, headerMatcher) + } + + // Query param matches + for _, queryParamMatch := range queryParamMatches { + stringMatcher := buildXdsStringMatcher(queryParamMatch) + + queryParamMatcher := &routev3.QueryParameterMatcher{ + Name: queryParamMatch.Name, + QueryParameterMatchSpecifier: &routev3.QueryParameterMatcher_StringMatch{ + StringMatch: stringMatcher, + }, + } + outMatch.QueryParameters = append(outMatch.QueryParameters, queryParamMatcher) + } + + return outMatch +} + +func buildXdsStringMatcher(irMatch *ir.StringMatch) *matcherv3.StringMatcher { + stringMatcher := new(matcherv3.StringMatcher) + + //nolint:gocritic + if irMatch.Exact != nil { + stringMatcher = &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: *irMatch.Exact, + }, + } + } else if irMatch.Prefix != nil { + stringMatcher = &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Prefix{ + Prefix: *irMatch.Prefix, + }, + } + } else if irMatch.Suffix != nil { + stringMatcher = &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Suffix{ + Suffix: *irMatch.Suffix, + }, + } + } else if irMatch.SafeRegex != nil { + stringMatcher = &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_SafeRegex{ + SafeRegex: &matcherv3.RegexMatcher{ + Regex: *irMatch.SafeRegex, + }, + }, + } + } + + return stringMatcher +} + +func buildXdsRouteAction(httpRoute *ir.HTTPRoute) *routev3.RouteAction { + return &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: httpRoute.Destination.Name, + }, + IdleTimeout: idleTimeout(httpRoute), + } +} + +func buildXdsWeightedRouteAction(httpRoute *ir.HTTPRoute) *routev3.RouteAction { + clusters := []*routev3.WeightedCluster_ClusterWeight{ + { + Name: "invalid-backend-cluster", + Weight: &wrapperspb.UInt32Value{Value: httpRoute.BackendWeights.Invalid}, + }, + } + + if httpRoute.BackendWeights.Valid > 0 { + validCluster := &routev3.WeightedCluster_ClusterWeight{ + Name: httpRoute.Destination.Name, + Weight: &wrapperspb.UInt32Value{Value: httpRoute.BackendWeights.Valid}, + } + clusters = append(clusters, validCluster) + } + + return &routev3.RouteAction{ + // Intentionally route to a non-existent cluster and return a 500 error when it is not found + ClusterNotFoundResponseCode: routev3.RouteAction_INTERNAL_SERVER_ERROR, + ClusterSpecifier: &routev3.RouteAction_WeightedClusters{ + WeightedClusters: &routev3.WeightedCluster{ + Clusters: clusters, + }, + }, + IdleTimeout: idleTimeout(httpRoute), + } +} + +func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration { + if httpRoute.Timeout != nil && httpRoute.Timeout.HTTP != nil { + if httpRoute.Timeout.HTTP.RequestTimeout != nil { + timeout := time.Hour // Default to 1 hour + + // Ensure is not less than the request timeout + if timeout < httpRoute.Timeout.HTTP.RequestTimeout.Duration { + timeout = httpRoute.Timeout.HTTP.RequestTimeout.Duration + } + + // Disable idle timeout when request timeout is disabled + if httpRoute.Timeout.HTTP.RequestTimeout.Duration == 0 { + timeout = 0 + } + + return durationpb.New(timeout) + } + } + return nil +} + +func buildXdsRedirectAction(httpRoute *ir.HTTPRoute) *routev3.RedirectAction { + var ( + redirection = httpRoute.Redirect + routeAction = &routev3.RedirectAction{} + ) + + if redirection.Scheme != nil { + routeAction.SchemeRewriteSpecifier = &routev3.RedirectAction_SchemeRedirect{ + SchemeRedirect: *redirection.Scheme, + } + } + if redirection.Path != nil { + if redirection.Path.FullReplace != nil { + routeAction.PathRewriteSpecifier = &routev3.RedirectAction_PathRedirect{ + PathRedirect: *redirection.Path.FullReplace, + } + } else if redirection.Path.PrefixMatchReplace != nil { + if useRegexRewriteForPrefixMatchReplace(httpRoute.PathMatch, *redirection.Path.PrefixMatchReplace) { + routeAction.PathRewriteSpecifier = &routev3.RedirectAction_RegexRewrite{ + RegexRewrite: prefix2RegexRewrite(*httpRoute.PathMatch.Prefix), + } + } else { + routeAction.PathRewriteSpecifier = &routev3.RedirectAction_PrefixRewrite{ + PrefixRewrite: *redirection.Path.PrefixMatchReplace, + } + } + } + } + if redirection.Hostname != nil { + routeAction.HostRedirect = *redirection.Hostname + } + if redirection.Port != nil { + routeAction.PortRedirect = *redirection.Port + } + if redirection.StatusCode != nil { + if *redirection.StatusCode == 302 { + routeAction.ResponseCode = routev3.RedirectAction_FOUND + } // no need to check for 301 since Envoy will use 301 as the default if the field is not configured + } + + return routeAction +} + +// useRegexRewriteForPrefixMatchReplace checks if the regex rewrite should be used for prefix match replace +// due to the issue with Envoy not handling the case of "//" when the replace string is "/". +// See: https://github.com/envoyproxy/envoy/issues/26055 +func useRegexRewriteForPrefixMatchReplace(pathMatch *ir.StringMatch, prefixMatchReplace string) bool { + return pathMatch != nil && + pathMatch.Prefix != nil && + (prefixMatchReplace == "" || prefixMatchReplace == "/") +} + +func prefix2RegexRewrite(prefix string) *matcherv3.RegexMatchAndSubstitute { + return &matcherv3.RegexMatchAndSubstitute{ + Pattern: &matcherv3.RegexMatcher{ + Regex: "^" + prefix + `\/*`, + }, + Substitution: "/", + } +} + +func buildXdsURLRewriteAction(destName string, urlRewrite *ir.URLRewrite, pathMatch *ir.StringMatch) *routev3.RouteAction { + routeAction := &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: destName, + }, + } + + if urlRewrite.Path != nil { + if urlRewrite.Path.FullReplace != nil { + routeAction.RegexRewrite = &matcherv3.RegexMatchAndSubstitute{ + Pattern: &matcherv3.RegexMatcher{ + Regex: "/.+", + }, + Substitution: *urlRewrite.Path.FullReplace, + } + } else if urlRewrite.Path.PrefixMatchReplace != nil { + // Circumvent the case of "//" when the replace string is "/" + // An empty replace string does not seem to solve the issue so we are using + // a regex match and replace instead + // Remove this workaround once https://github.com/envoyproxy/envoy/issues/26055 is fixed + if useRegexRewriteForPrefixMatchReplace(pathMatch, *urlRewrite.Path.PrefixMatchReplace) { + routeAction.RegexRewrite = prefix2RegexRewrite(*pathMatch.Prefix) + } else { + routeAction.PrefixRewrite = *urlRewrite.Path.PrefixMatchReplace + } + } + } + + if urlRewrite.Hostname != nil { + routeAction.HostRewriteSpecifier = &routev3.RouteAction_HostRewriteLiteral{ + HostRewriteLiteral: *urlRewrite.Hostname, + } + + routeAction.AppendXForwardedHost = true + } + + return routeAction +} + +func buildXdsDirectResponseAction(res *ir.DirectResponse) *routev3.DirectResponseAction { + routeAction := &routev3.DirectResponseAction{Status: res.StatusCode} + + if res.Body != nil { + routeAction.Body = &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineString{ + InlineString: *res.Body, + }, + } + } + + return routeAction +} + +func buildXdsRequestMirrorPolicies(mirrorDestinations []*ir.RouteDestination) []*routev3.RouteAction_RequestMirrorPolicy { + var mirrorPolicies []*routev3.RouteAction_RequestMirrorPolicy + + for _, mirrorDest := range mirrorDestinations { + mirrorPolicies = append(mirrorPolicies, &routev3.RouteAction_RequestMirrorPolicy{ + Cluster: mirrorDest.Name, + }) + } + + return mirrorPolicies +} + +func buildXdsAddedHeaders(headersToAdd []ir.AddHeader) []*corev3.HeaderValueOption { + headerValueOptions := make([]*corev3.HeaderValueOption, len(headersToAdd)) + + for i, header := range headersToAdd { + var appendAction corev3.HeaderValueOption_HeaderAppendAction + + if header.Append { + appendAction = corev3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD + } else { + appendAction = corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD + } + + headerValueOptions[i] = &corev3.HeaderValueOption{ + Header: &corev3.HeaderValue{ + Key: header.Name, + Value: header.Value, + }, + AppendAction: appendAction, + } + + // Allow empty headers to be set, but don't add the config to do so unless necessary + if header.Value == "" { + headerValueOptions[i].KeepEmptyValue = true + } + } + + return headerValueOptions +} + +func buildHashPolicy(httpRoute *ir.HTTPRoute) []*routev3.RouteAction_HashPolicy { + // Return early + if httpRoute == nil || httpRoute.LoadBalancer == nil || httpRoute.LoadBalancer.ConsistentHash == nil { + return nil + } + + if httpRoute.LoadBalancer.ConsistentHash.SourceIP != nil && *httpRoute.LoadBalancer.ConsistentHash.SourceIP { + hashPolicy := &routev3.RouteAction_HashPolicy{ + PolicySpecifier: &routev3.RouteAction_HashPolicy_ConnectionProperties_{ + ConnectionProperties: &routev3.RouteAction_HashPolicy_ConnectionProperties{ + SourceIp: true, + }, + }, + } + return []*routev3.RouteAction_HashPolicy{hashPolicy} + } + + return nil +} + +func buildRetryPolicy(route *ir.HTTPRoute) (*routev3.RetryPolicy, error) { + if route.Retry != nil { + rr := route.Retry + rp := &routev3.RetryPolicy{ + RetryOn: retryDefaultRetryOn, + RetriableStatusCodes: []uint32{retryDefaultRetriableStatusCode}, + NumRetries: &wrapperspb.UInt32Value{Value: retryDefaultNumRetries}, + RetryHostPredicate: []*routev3.RetryPolicy_RetryHostPredicate{ + { + Name: "envoy.retry_host_predicates.previous_hosts", + ConfigType: &routev3.RetryPolicy_RetryHostPredicate_TypedConfig{ + TypedConfig: protocov.ToAny(&previoushost.PreviousHostsPredicate{}), + }, + }, + }, + HostSelectionRetryMaxAttempts: 5, + } + + if rr.NumRetries != nil { + rp.NumRetries = &wrapperspb.UInt32Value{Value: *rr.NumRetries} + } + + if rr.RetryOn != nil { + if rr.RetryOn.Triggers != nil && len(rr.RetryOn.Triggers) > 0 { + if ro, err := buildRetryOn(rr.RetryOn.Triggers); err == nil { + rp.RetryOn = ro + } else { + return nil, err + } + } + + if rr.RetryOn.HTTPStatusCodes != nil && len(rr.RetryOn.HTTPStatusCodes) > 0 { + rp.RetriableStatusCodes = buildRetryStatusCodes(rr.RetryOn.HTTPStatusCodes) + } + } + + if rr.PerRetry != nil { + if rr.PerRetry.Timeout != nil { + rp.PerTryTimeout = durationpb.New(rr.PerRetry.Timeout.Duration) + } + + if rr.PerRetry.BackOff != nil { + bbo := false + rbo := &routev3.RetryPolicy_RetryBackOff{} + if rr.PerRetry.BackOff.BaseInterval != nil { + rbo.BaseInterval = durationpb.New(rr.PerRetry.BackOff.BaseInterval.Duration) + bbo = true + } + + if rr.PerRetry.BackOff.MaxInterval != nil { + rbo.MaxInterval = durationpb.New(rr.PerRetry.BackOff.MaxInterval.Duration) + bbo = true + } + + if bbo { + rp.RetryBackOff = rbo + } + } + } + return rp, nil + } + + return nil, nil +} + +func buildRetryStatusCodes(codes []ir.HTTPStatus) []uint32 { + ret := make([]uint32, len(codes)) + for i, c := range codes { + ret[i] = uint32(c) + } + return ret +} + +// buildRetryOn concatenates triggers to a comma-delimited string. +// An error is returned if a trigger is not in the list of supported values (not likely, due to prior validations). +func buildRetryOn(triggers []ir.TriggerEnum) (string, error) { + if len(triggers) == 0 { + return "", nil + } + + lookup := map[ir.TriggerEnum]string{ + ir.Error5XX: "5xx", + ir.GatewayError: "gateway-error", + ir.Reset: "reset", + ir.ConnectFailure: "connect-failure", + ir.Retriable4XX: "retriable-4xx", + ir.RefusedStream: "refused-stream", + ir.RetriableStatusCodes: "retriable-status-codes", + ir.Cancelled: "cancelled", + ir.DeadlineExceeded: "deadline-exceeded", + ir.Internal: "internal", + ir.ResourceExhausted: "resource-exhausted", + ir.Unavailable: "unavailable", + } + + var b strings.Builder + + if t, found := lookup[triggers[0]]; found { + b.WriteString(t) + } else { + return "", errors.New("unsupported RetryOn trigger") + } + + for _, v := range triggers[1:] { + if t, found := lookup[v]; found { + b.WriteString(",") + b.WriteString(t) + } else { + return "", errors.New("unsupported RetryOn trigger") + } + } + + return b.String(), nil +} diff --git a/adapter/internal/operator/gateway-api/translator/runner/runner.go b/adapter/internal/operator/gateway-api/translator/runner/runner.go new file mode 100644 index 0000000000..3eedac7882 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/runner/runner.go @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/translator" + "github.com/wso2/apk/adapter/internal/operator/message" +) + +type Config struct { + XdsIR *message.XdsIR + Xds *message.Xds + // ExtensionManager extension.Manager + ProviderResources *message.ProviderResources +} + +type Runner struct { + Config +} + +func New(cfg *Config) *Runner { + return &Runner{Config: *cfg} +} + +func (r *Runner) Name() string { + return string("xds-translator") +} + +// Start starts the xds-translator runner +func (r *Runner) Start(ctx context.Context) (err error) { + go r.subscribeAndTranslate(ctx) + loggers.LoggerAPKOperator.Info("Started xds translator ...") + return +} + +func (r *Runner) subscribeAndTranslate(ctx context.Context) { + // Subscribe to resources + message.HandleSubscription(message.Metadata{Runner: "xds-translator", Message: "xds-ir"}, r.XdsIR.Subscribe(ctx), + func(update message.Update[string, *ir.Xds], errChan chan error) { + loggers.LoggerAPKOperator.Info("Received an update in xds translator ...") + key := update.Key + val := update.Value + + if update.Delete { + r.Xds.Delete(key) + } else { + // Translate to xds resources + t := &translator.Translator{} + + result, err := t.Translate(val) + if err != nil { + loggers.LoggerAPKOperator.Error("Failed to translate xds ir ", err) + errChan <- err + } + + // xDS translation is done in a best-effort manner, so the result + // may contain partial resources even if there are errors. + if result == nil { + loggers.LoggerAPKOperator.Info("No xds resources to publish") + return + } + + // Publish + r.Xds.Store(key, result) + } + }, + ) + loggers.LoggerAPKOperator.Info("subscriber shutting down") +} diff --git a/adapter/internal/operator/gateway-api/translator/runner/runner_test.go b/adapter/internal/operator/gateway-api/translator/runner/runner_test.go new file mode 100644 index 0000000000..205f362365 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/runner/runner_test.go @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + "testing" + "time" + + resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/message" + + "github.com/stretchr/testify/require" +) + +func TestRunner(t *testing.T) { + // Setup + xdsIR := new(message.XdsIR) + xds := new(message.Xds) + pResource := new(message.ProviderResources) + // cfg, err := config.New() + // require.NoError(t, err) + r := New(&Config{ + // Server: *cfg, + ProviderResources: pResource, + XdsIR: xdsIR, + Xds: xds, + }) + + ctx := context.Background() + // Start + err := r.Start(ctx) + require.NoError(t, err) + + // xDS is nil at start + require.Equal(t, map[string]*ir.Xds{}, xdsIR.LoadAll()) + + // test translation + path := "example" + res := ir.Xds{ + HTTP: []*ir.HTTPListener{ + { + Name: "test", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*ir.HTTPRoute{ + { + Name: "test-route", + PathMatch: &ir.StringMatch{ + Exact: &path, + }, + Destination: &ir.RouteDestination{ + Name: "test-dest", + Settings: []*ir.DestinationSetting{ + { + Endpoints: []*ir.DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + xdsIR.Store("test", &res) + require.Eventually(t, func() bool { + out := xds.LoadAll() + if out == nil { + return false + } + if out["test"] == nil { + return false + } + // Ensure an xds listener is created + return len(out["test"].XdsResources[resourcev3.ListenerType]) == 1 + }, time.Second*5, time.Millisecond*50) + + // Delete the IR triggering an xds delete + xdsIR.Delete("test") + require.Eventually(t, func() bool { + out := xds.LoadAll() + // Ensure that xds has no key, value pairs + return len(out) == 0 + }, time.Second*5, time.Millisecond*50) + +} + +func TestRunner_withExtensionManager(t *testing.T) { + // Setup + xdsIR := new(message.XdsIR) + xds := new(message.Xds) + pResource := new(message.ProviderResources) + + // cfg, err := config.New() + // require.NoError(t, err) + r := New(&Config{ + // Server: *cfg, + ProviderResources: pResource, + XdsIR: xdsIR, + Xds: xds, + // ExtensionManager: &extManagerMock{}, + }) + + ctx := context.Background() + // Start + err := r.Start(ctx) + require.NoError(t, err) + + // xDS is nil at start + require.Equal(t, map[string]*ir.Xds{}, xdsIR.LoadAll()) + + // test translation + path := "example" + res := ir.Xds{ + HTTP: []*ir.HTTPListener{ + { + Name: "test", + Address: "0.0.0.0", + Port: 80, + Hostnames: []string{"example.com"}, + Routes: []*ir.HTTPRoute{ + { + Name: "test-route", + PathMatch: &ir.StringMatch{ + Exact: &path, + }, + Destination: &ir.RouteDestination{ + Name: "test-dest", + Settings: []*ir.DestinationSetting{ + { + Endpoints: []*ir.DestinationEndpoint{ + { + Host: "10.11.12.13", + Port: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + xdsIR.Store("test", &res) + require.Eventually(t, func() bool { + out := xds.LoadAll() + // xDS translation is done in a best-effort manner, so event the extension + // manager returns an error, the xDS resources should still be created. + return len(out) == 1 + }, time.Second*5, time.Millisecond*50) +} + +// type extManagerMock struct { +// types.Manager +// } + +// func (m *extManagerMock) GetPostXDSHookClient(xdsHookType v1alpha1.XDSTranslatorHook) types.XDSHookClient { +// if xdsHookType == v1alpha1.XDSHTTPListener { +// return &xdsHookClientMock{} +// } + +// return nil +// } + +// type xdsHookClientMock struct { +// types.XDSHookClient +// } + +// func (c *xdsHookClientMock) PostHTTPListenerModifyHook(*listenerv3.Listener) (*listenerv3.Listener, error) { +// return nil, fmt.Errorf("assuming a network error during the call") +// } diff --git a/adapter/internal/operator/gateway-api/translator/tcpkeepalive.go b/adapter/internal/operator/gateway-api/translator/tcpkeepalive.go new file mode 100644 index 0000000000..a3d5b06f64 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/tcpkeepalive.go @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" +) + +// buildTCPSocketOptions converts listener downstream settings to xds socketOptions +func buildTCPSocketOptions(keepAlive *ir.TCPKeepalive) []*corev3.SocketOption { + if keepAlive == nil { + return nil + } + + socketOptions := make([]*corev3.SocketOption, 0) + // Enable Keep Alives + socketOption := &corev3.SocketOption{ + Description: "socket option to enable tcp keep alive", + Level: 0x1, // syscall.SOL_SOCKET has a different value for Darwin, resulting in `go test` failing + Name: 0x9, // syscall.SO_KEEPALIVE has a different value for Darwin, resulting in `go test` failing + Value: &corev3.SocketOption_IntValue{IntValue: 1}, // Enable + } + + socketOptions = append(socketOptions, socketOption) + + if keepAlive.Probes != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive probes", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x6, // Darwin lacks syscall.TCP_KEEPCNT, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Probes)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.IdleTime != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive idle time", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x4, // Darwin lacks syscall.TCP_KEEPIDLE, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.IdleTime)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.Interval != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive interval", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x5, // Darwin lacks syscall.TCP_KEEPINTVL, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Interval)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + return socketOptions + +} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog-invalid.yaml new file mode 100644 index 0000000000..37ce0958e7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog-invalid.yaml @@ -0,0 +1,43 @@ +name: "accesslog" +accesslog: + text: + - path: "/dev/stdout" + format: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + json: + - path: "/dev/stdout" + json: + start_time: "%START_TIME%" + method: "%REQ(:METHOD)%" + path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" + protocol: "%PROTOCOL%" + response_code: "%RESPONSE_CODE%" + openTelemetry: + - text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + attributes: + "response_code": "%RESPONSE_CODE%" + resources: + "cluster_name": "cluster1" + host: "" + port: 4317 +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog.yaml new file mode 100644 index 0000000000..f4ec43ac07 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/accesslog.yaml @@ -0,0 +1,44 @@ +name: "accesslog" +accesslog: + text: + - path: "/dev/stdout" + format: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + json: + - path: "/dev/stdout" + json: + start_time: "%START_TIME%" + method: "%REQ(:METHOD)%" + path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" + protocol: "%PROTOCOL%" + response_code: "%RESPONSE_CODE%" + openTelemetry: + - text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + attributes: + "response_code": "%RESPONSE_CODE%" + resources: + "cluster_name": "cluster1" + host: otel-collector.default.svc.cluster.local + port: 4317 +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/basic-auth.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/basic-auth.yaml new file mode 100644 index 0000000000..af6baa0b77 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/basic-auth.yaml @@ -0,0 +1,22 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + basicAuth: + users: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/circuit-breaker.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/circuit-breaker.yaml new file mode 100644 index 0000000000..a63297e264 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/circuit-breaker.yaml @@ -0,0 +1,24 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + circuitBreaker: + maxConnections: 1 + maxPendingRequests: 1 + maxParallelRequests: 1 + maxParallelRetries: 2 + maxRequestsPerConnection: 10 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/client-timeout.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/client-timeout.yaml new file mode 100644 index 0000000000..1c05b605a3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/client-timeout.yaml @@ -0,0 +1,22 @@ +http: + - name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + timeout: + http: + requestReceivedTimeout: "5s" + diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/cors.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/cors.yaml new file mode 100644 index 0000000000..c5a07bee84 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/cors.yaml @@ -0,0 +1,39 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + cors: + allowOrigins: + - name: example.com + stringMatch: + safeRegex: "*.example.com" + - name: foo.bar.com + stringMatch: + exact: foo.bar.com + allowMethods: + - GET + - POST + allowHeaders: + - "x-header-1" + - "x-header-2" + exposeHeaders: + - "x-header-3" + - "x-header-4" + allowCredentials: true + maxAge: 1000s diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/empty.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/empty.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/ext-auth.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/ext-auth.yaml new file mode 100644 index 0000000000..d1451e968e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/ext-auth.yaml @@ -0,0 +1,61 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "www.example.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: httproute/default/httproute-1/rule/0/match/0/www_example_com + hostname: "*" + pathMatch: + exact: "foo" + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - endpoints: + - host: "10.0.0.1" + port: 50000 + extAuth: + http: + authority: http-backend.envoy-gateway:80 + headersToBackend: + - header1 + - header2 + path: /auth + destination: + name: securitypolicy/default/policy-for-first-route/http-backend + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 80 + protocol: HTTP + weight: 1 + - name: httproute/default/httproute-2/rule/0/match/0/www_example_com + hostname: "*" + pathMatch: + exact: "bar" + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - endpoints: + - host: "10.0.0.2" + port: 60000 + extAuth: + grpc: + authority: grpc-backend.default:9000 + destination: + name: securitypolicy/default/policy-for-second-route/grpc-backend + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 9000 + protocol: GRPC + weight: 1 + headersToExtAuth: + - header1 + - header2 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/fault-injection.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/fault-injection.yaml new file mode 100644 index 0000000000..a99b77d5e0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/fault-injection.yaml @@ -0,0 +1,83 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + faultInjection: + abort: + httpStatus: 500 + percentage: 0.01 + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + faultInjection: + delay: + fixedDelay: 5.4s + percentage: 80 + pathMatch: + exact: "example" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + faultInjection: + abort: + httpStatus: 500 + percentage: 100 + delay: + fixedDelay: 5.005s + percentage: 100 + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fourth-route" + hostname: "*" + faultInjection: + abort: + grpcStatus: 14 + percentage: 100 + delay: + fixedDelay: 5.005s + percentage: 100 + pathMatch: + exact: "test" + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fifth-route" + hostname: "*" + faultInjection: + pathMatch: + exact: "test" + destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/health-check.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/health-check.yaml new file mode 100644 index 0000000000..a767bdab20 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/health-check.yaml @@ -0,0 +1,124 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + hostname: "*" + healthCheck: + active: + timeout: "500ms" + interval: "3s" + unhealthyThreshold: 3 + healthyThreshold: 1 + http: + host: "*" + path: "/healthz" + expectedResponse: + text: "ok" + expectedStatuses: + - 200 + - 300 + passive: + baseEjectionTime: 180s + interval: 2s + maxEjectionPercent: 100 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + healthCheck: + active: + timeout: "1s" + interval: "5s" + unhealthyThreshold: 3 + healthyThreshold: 3 + http: + host: "*" + path: "/healthz" + expectedResponse: + binary: "cG9uZw==" + expectedStatuses: + - 200 + - 201 + passive: + baseEjectionTime: 180s + interval: 1s + maxEjectionPercent: 100 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + healthCheck: + active: + timeout: "1s" + interval: "5s" + unhealthyThreshold: 3 + healthyThreshold: 3 + tcp: + send: + text: "ping" + receive: + text: "pong" + passive: + baseEjectionTime: 160s + interval: 1s + maxEjectionPercent: 100 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: false + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fourth-route" + hostname: "*" + healthCheck: + active: + timeout: "1s" + interval: "5s" + unhealthyThreshold: 3 + healthyThreshold: 3 + tcp: + send: + binary: "cGluZw==" + receive: + binary: "cG9uZw==" + passive: + baseEjectionTime: 180s + interval: 1s + maxEjectionPercent: 90 + consecutive5XxErrors: 5 + consecutiveGatewayErrors: 0 + consecutiveLocalOriginFailures: 5 + splitExternalLocalOriginErrors: true + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-direct-response.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-direct-response.yaml new file mode 100644 index 0000000000..9db15c7fb9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-direct-response.yaml @@ -0,0 +1,22 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml new file mode 100644 index 0000000000..1cb0be3ec2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-dns-cluster.yaml @@ -0,0 +1,26 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + prefix: / + queryParamMatches: + - exact: "yes" + name: debug + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "foo.bar" + port: 50000 + - host: "bar.foo" + port: 50001 + addressType: FQDN diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-invalid.yaml new file mode 100644 index 0000000000..42184d4047 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-invalid.yaml @@ -0,0 +1,28 @@ +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-mirror.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-mirror.yaml new file mode 100644 index 0000000000..b00449b384 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-mirror.yaml @@ -0,0 +1,24 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "mirror-route" + hostname: "*" + destination: + name: "route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + mirror: + name: "mirror-route-dest" + settings: + - endpoints: + - host: "2.3.4.5" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml new file mode 100644 index 0000000000..81fc7da108 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-matches.yaml @@ -0,0 +1,86 @@ +http: +- name: first-listener + address: 0.0.0.0 + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - destination: + name: "first-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: example.com + name: envoy-gateway/httproute-2/rule/0/match/0/example.com + pathMatch: + prefix: /v1/example + queryParamMatches: + - exact: "yes" + name: debug + - destination: + name: "second-route-dest" + settings: + - endpoints: + - host: 8.8.8.8 + port: 8080 + hostname: example.com + name: envoy-gateway/httproute-3/rule/0/match/0/example.com + pathMatch: + prefix: /v1/example + - destination: + name: "third-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + headerMatches: + - exact: one + name: version + hostname: example.net + name: envoy-gateway/httproute-4/rule/0/match/0/example.net + pathMatch: + prefix: /v1/status + - destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: 8.8.8.8 + port: 8080 + hostname: example.net + name: envoy-gateway/httproute-5/rule/0/match/0/example.net + pathMatch: + prefix: /v1/status + - destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: '*.com' + name: envoy-gateway/httproute-1/rule/0/match/0/*.com + pathMatch: + prefix: /foo + - destination: + name: "sixth-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: '*.net' + name: envoy-gateway/httproute-1/rule/0/match/0/*.net + pathMatch: + prefix: /foo + - destination: + name: "seventh-route-dest" + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + hostname: "*" + name: envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + prefix: / diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml new file mode 100644 index 0000000000..3d13de381b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-multiple-mirrors.yaml @@ -0,0 +1,28 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "mirror-route" + hostname: "*" + destination: + name: "route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + mirrors: + - name: "mirror-route-dest" + settings: + - endpoints: + - host: "2.3.4.5" + - name: "mirror-route-dest1" + settings: + - endpoints: + - host: "3.4.5.6" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml new file mode 100644 index 0000000000..d72ec1d2c6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-partial-invalid.yaml @@ -0,0 +1,46 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "valid-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "valid-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "invalid-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "invalid-route-dest" + settings: + - endpoints: + - host: "" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-redirect.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-redirect.yaml new file mode 100644 index 0000000000..36599609de --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-redirect.yaml @@ -0,0 +1,52 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "redirect-route-1" + hostname: "*" + destination: + name: "redirect-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + redirect: + scheme: https + statusCode: 302 + hostname: "redirected.com" + port: 8443 + path: + prefixMatchReplace: /redirected + - name: "redirect-route-2" + hostname: "*" + pathMatch: + prefix: "/redirect" + destination: + name: "redirect-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + redirect: + path: + prefixMatchReplace: / + - name: "redirect-route-3" + hostname: "*" + pathMatch: + prefix: "/redirect/" + destination: + name: "redirect-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + redirect: + path: + prefixMatchReplace: / diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-regex.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-regex.yaml new file mode 100644 index 0000000000..d9558ad99a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-regex.yaml @@ -0,0 +1,28 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "regex-route" + hostname: "*" + pathMatch: + safeRegex: "/v1/.*" + headerMatches: + - name: re_header + stringMatch: + safeRegex: ".*" + queryParamMatches: + - name: re_query + stringMatch: + safeRegex: ".*" + destination: + name: "regex-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-request-headers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-request-headers.yaml new file mode 100644 index 0000000000..c3dc4417dc --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-request-headers.yaml @@ -0,0 +1,38 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "request-header-route" + hostname: "*" + destination: + name: "request-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + addRequestHeaders: + - name: "some-header" + value: "some-value" + append: true + - name: "some-header-2" + value: "some-value" + append: true + - name: "some-header3" + value: "some-value" + append: false + - name: "some-header4" + value: "some-value" + append: false + - name: "empty-header" + value: "" + append: false + removeRequestHeaders: + - "some-header5" + - "some-header6" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml new file mode 100644 index 0000000000..e3114e2d25 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-headers.yaml @@ -0,0 +1,35 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "response-header-route" + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + addResponseHeaders: + - name: "some-header" + value: "some-value" + append: true + - name: "some-header-2" + value: "some-value" + append: true + - name: "some-header3" + value: "some-value" + append: false + - name: "some-header4" + value: "some-value" + append: false + - name: "empty-header" + value: "" + append: false diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml new file mode 100644 index 0000000000..0e59f8f124 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-add-remove-headers.yaml @@ -0,0 +1,38 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "response-header-route" + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + addResponseHeaders: + - name: "some-header" + value: "some-value" + append: true + - name: "some-header-2" + value: "some-value" + append: true + - name: "some-header3" + value: "some-value" + append: false + - name: "some-header4" + value: "some-value" + append: false + - name: "empty-header" + value: "" + append: false + removeResponseHeaders: + - "some-header5" + - "some-header6" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml new file mode 100644 index 0000000000..f7b30b3d7d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-response-remove-headers.yaml @@ -0,0 +1,22 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "response-header-route" + hostname: "*" + destination: + name: "response-header-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + removeResponseHeaders: + - "some-header5" + - "some-header6" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml new file mode 100644 index 0000000000..f430764451 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-root-path-url-prefix.yaml @@ -0,0 +1,27 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "rewrite-route" + pathMatch: + prefix: "/origin/" + hostname: gateway.envoyproxy.io + headerMatches: + - name: ":authority" + exact: gateway.envoyproxy.io + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + urlRewrite: + path: + prefixMatchReplace: / diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml new file mode 100644 index 0000000000..4d08acb93e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-fullpath.yaml @@ -0,0 +1,24 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "rewrite-route" + pathMatch: + prefix: "/origin" + hostname: gateway.envoyproxy.io + destination: + name: "rewrite-route" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + urlRewrite: + path: + fullReplace: /rewrite diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml new file mode 100644 index 0000000000..8cc673a7e5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml @@ -0,0 +1,28 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "rewrite-route" + pathMatch: + prefix: "/origin" + hostname: gateway.envoyproxy.io + headerMatches: + - name: ":authority" + exact: gateway.envoyproxy.io + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + urlRewrite: + hostname: "3.3.3.3" + path: + prefixMatchReplace: /rewrite diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml new file mode 100644 index 0000000000..df4f2e9c2b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-rewrite-url-prefix.yaml @@ -0,0 +1,27 @@ +name: "http-route" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "rewrite-route" + pathMatch: + prefix: "/origin" + hostname: gateway.envoyproxy.io + headerMatches: + - name: ":authority" + exact: gateway.envoyproxy.io + destination: + name: "rewrite-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + urlRewrite: + path: + prefixMatchReplace: /rewrite diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-timeout.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-timeout.yaml new file mode 100644 index 0000000000..fdd2b84ef8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-timeout.yaml @@ -0,0 +1,47 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + timeout: + http: + requestTimeout: 5s + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + timeout: + http: + requestTimeout: 4000s + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50001 + - name: "third-route" + hostname: "*" + timeout: + http: + requestTimeout: 0s + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50002 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml new file mode 100644 index 0000000000..2540dec625 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-backend.yaml @@ -0,0 +1,31 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.1.1.1" + port: 50001 + weight: 20 + - endpoints: + - host: "2.2.2.2" + port: 50002 + weight: 40 + - endpoints: + - host: "3.3.3.3" + port: 50003 + weight: 20 + - endpoints: + - host: "4.4.4.4" + port: 50004 + weight: 20 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml new file mode 100644 index 0000000000..4419c5222e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-weighted-invalid-backend.yaml @@ -0,0 +1,21 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + backendWeights: + invalid: 1 + valid: 1 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tls-system-truststore.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tls-system-truststore.yaml new file mode 100644 index 0000000000..3cc0e5e876 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tls-system-truststore.yaml @@ -0,0 +1,32 @@ +http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + tls: + sni: example.com + useSystemTrustStore: true + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml new file mode 100644 index 0000000000..7dd532ab49 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle-multiple-certs.yaml @@ -0,0 +1,86 @@ +http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.12 + port: 8080 + protocol: HTTPS + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/policies-ca2 + sni: bar.example.com + weight: 1 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTPS + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/policies-ca + sni: example.com + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" + - address: 0.0.0.0 + hostnames: + - 'example.com' + isHTTP2: false + name: envoy-gateway/gateway-btls-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10081 + routes: + - backendWeights: + invalid: 0 + valid: 0 + proxyProtocol: + version: "V2" + destination: + name: httproute/envoy-gateway/httproute-btls-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.12 + port: 8080 + protocol: HTTP + weight: 1 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTPS + tls: + caCertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls-2/policies-ca + sni: example.com + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-btls-2/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml new file mode 100644 index 0000000000..e28cd14d65 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route-with-tlsbundle.yaml @@ -0,0 +1,34 @@ +http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-btls/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/envoy-gateway/httproute-btls/rule/0 + settings: + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + protocol: HTTP + tls: + CACertificate: + certificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + name: policy-btls/policies-ca + SNI: example.com + weight: 1 + hostname: '*' + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route.yaml new file mode 100644 index 0000000000..0c89d5a184 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http-route.yaml @@ -0,0 +1,28 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + - name: test + stringMatch: + suffix: "end" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-preserve-case.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-preserve-case.yaml new file mode 100644 index 0000000000..f857ac8f85 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-preserve-case.yaml @@ -0,0 +1,40 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + http1: + preserveHeaderCase: true + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "second-listener" + address: "0.0.0.0" + port: 10081 + hostnames: + - "*" + http1: + preserveHeaderCase: true + enableTrailers: true + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "second-route" + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.5" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-trailers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-trailers.yaml new file mode 100644 index 0000000000..5117474497 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http1-trailers.yaml @@ -0,0 +1,20 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + http1: + enableTrailers: true + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http10.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http10.yaml new file mode 100644 index 0000000000..47f57a0442 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http10.yaml @@ -0,0 +1,21 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + http1: + http10: + defaultHost: "foo.com" + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http2-route.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http2-route.yaml new file mode 100644 index 0000000000..f3dc12c5aa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http2-route.yaml @@ -0,0 +1,30 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + isHTTP2: true + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + name: "test" + exact: "foo/bar" + headerMatches: + - name: user + stringMatch: + exact: "jason" + queryParamMatches: + - name: "debug" + exact: "yes" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + protocol: GRPC diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http3.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http3.yaml new file mode 100644 index 0000000000..299ea3241b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/http3.yaml @@ -0,0 +1,34 @@ +http: +- address: 0.0.0.0 + http3: + quicPort: 443 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/tls + port: 10443 + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - endpoints: + - host: 7.7.7.7 + port: 8080 + weight: 1 + hostname: '*' + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + tls: + certificates: + - name: envoy-gateway-tls-secret-1 + privateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + serverCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-add-op-without-value.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-add-op-without-value.yaml new file mode 100644 index 0000000000..ec101c8e87 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-add-op-without-value.yaml @@ -0,0 +1,45 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml new file mode 100644 index 0000000000..09806682e1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-listener.yaml @@ -0,0 +1,37 @@ +jsonPatches: +- type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} +- type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" + name: "first-route" + operation: + op: "replace" + path: "/endpoints/0/load_balancing_weight" + value: "50" +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml new file mode 100644 index 0000000000..6224d909fa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml @@ -0,0 +1,26 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml new file mode 100644 index 0000000000..2d970ef216 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-invalid.yaml @@ -0,0 +1,41 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" + name: "first-route" + operation: + op: "replace" + path: "/endpoints/0/load_balancing_weight" + value: "50" +http: +- name: "first-listener" + address: "" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml new file mode 100644 index 0000000000..6224d909fa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml @@ -0,0 +1,26 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-move-op-with-value.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-move-op-with-value.yaml new file mode 100644 index 0000000000..40ee65d1a5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jsonpatch-move-op-with-value.yaml @@ -0,0 +1,48 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "first-listener" + operation: + op: "remove" + from: "/filter_chains/0/filters/0/typed_config/http_filters/0" + path: "/filter_chains/0/filters/0/typed_config/http_filters/1" + value: + test: "abc" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml new file mode 100644 index 0000000000..2a362a469d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-custom-extractor.yaml @@ -0,0 +1,36 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + extractFrom: + cookies: + - session_access_token + headers: + - name: Authorization + valuePrefix: 'Bearer ' + params: + - token + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml new file mode 100644 index 0000000000..558049ed56 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-multi-provider.yaml @@ -0,0 +1,72 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route-www.test.com" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: http://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: one-route-example-key1 + claim: claim.neteased.key + - name: example2 + issuer: https://www.two.example.com + audiences: + - one.foo.com + - two.foo.com + remoteJWKS: + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + recomputeRoute: true + claimToHeaders: + - header: one-route-example2-key1 + claim: claim.neteased.key + - header: one-route-example2-key2 + claim: name + destination: + name: "first-route-www.test.com-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route-www.test.com" + hostname: "*" + pathMatch: + exact: "foo/baz" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: http://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: second-route-example-key1 + claim: claim.neteased.key + - name: example2 + issuer: https://www.two.example.com + audiences: + - one.foo.com + - two.foo.com + remoteJWKS: + uri: https://192.168.1.250:8080/jwt/public-key/jwks.json + destination: + name: "second-route-www.test.com-dest" + settings: + - endpoints: + - host: "5.6.7.8" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml new file mode 100644 index 0000000000..7dd2a4b73c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-multi-route-single-provider.yaml @@ -0,0 +1,52 @@ +accesslog: + text: + - path: "/dev/stdout" +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + claimToHeaders: + - header: first-route-key + claim: claim.neteased.key + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + pathMatch: + exact: "foo/baz" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "5.6.7.8" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-ratelimit.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-ratelimit.yaml new file mode 100644 index 0000000000..9857cb58da --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-ratelimit.yaml @@ -0,0 +1,72 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://192.168.1.250/jwt/public-key/jwks.json + - name: "second-route" + hostname: "*" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + pathMatch: + exact: "example" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + rateLimit: + global: + rules: + - limit: + requests: 5 + unit: second + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml new file mode 100644 index 0000000000..4ff905bdc5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/jwt-single-route-single-match.yaml @@ -0,0 +1,28 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml new file mode 100644 index 0000000000..245099796d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-proxy-protocol.yaml @@ -0,0 +1,32 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + enableProxyProtocol: true + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml new file mode 100644 index 0000000000..1e4cdcdc5d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml @@ -0,0 +1,66 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + tcpKeepalive: {} + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "second-listener" + address: "0.0.0.0" + port: 10081 + hostnames: + - "foo.net" + tcpKeepalive: + probes: 7 + interval: 200 + idleTime: 50 + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "second-route" + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +tcp: +- name: "third-listener" + address: "0.0.0.0" + port: 10082 + tcpKeepalive: {} + tls: + passthrough: + snis: + - bar.com + destination: + name: "tls-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "fourth-listener" + address: "0.0.0.0" + tcpKeepalive: + probes: 10 + port: 10083 + destination: + name: "tcp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/load-balancer.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/load-balancer.yaml new file mode 100644 index 0000000000..fbf13ae886 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/load-balancer.yaml @@ -0,0 +1,75 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + loadBalancer: + roundRobin: {} + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + loadBalancer: + random: {} + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + loadBalancer: + leastRequest: {} + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fourth-route" + hostname: "*" + loadBalancer: + consistentHash: + sourceIP: true + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "fifth-route" + hostname: "*" + loadBalancer: + leastRequest: + slowStart: + window: 60s + destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "sixth-route" + hostname: "*" + loadBalancer: + roundRobin: + slowStart: + window: 300s + destination: + name: "sixth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/local-ratelimit.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/local-ratelimit.yaml new file mode 100644 index 0000000000..9e818b4d15 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/local-ratelimit.yaml @@ -0,0 +1,84 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route-ratelimit-single-rule" + hostname: "*" + rateLimit: + local: + default: + requests: 10 + unit: Minute + rules: + - headerMatches: + - name: x-user-id + exact: one + - name: x-org-id + exact: foo + limit: + requests: 10 + unit: Hour + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route-ratelimit-multiple-rules" + hostname: "*" + rateLimit: + local: + default: + requests: 10 + unit: Minute + rules: + - headerMatches: + - name: x-user-id + exact: one + - name: x-org-id + exact: foo + limit: + requests: 10 + unit: Hour + - cidrMatch: + cidr: 192.168.0.0/16 + maskLen: 16 + headerMatches: + - name: x-user-id + exact: two + - name: x-org-id + exact: bar + limit: + requests: 10 + unit: Minute + pathMatch: + exact: "example" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route-ratelimit-no-rule" + hostname: "*" + rateLimit: + local: + default: + requests: 10 + unit: Minute + pathMatch: + exact: "test" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/metrics-virtual-host.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/metrics-virtual-host.yaml new file mode 100644 index 0000000000..39f1a23dc7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/metrics-virtual-host.yaml @@ -0,0 +1,21 @@ +name: "metrics" +metrics: + enableVirtualHostStats: true +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml new file mode 100644 index 0000000000..36984ea69f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mixed-tls-jwt-authn.yaml @@ -0,0 +1,35 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + - name: first-listener + # byte slice representation of "cert-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + requestAuthentication: + jwt: + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port-with-different-filters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port-with-different-filters.yaml new file mode 100644 index 0000000000..bec261bee5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port-with-different-filters.yaml @@ -0,0 +1,119 @@ +# This is a test file for multiple Gateway HTTP listeners on the same port with different filters. +# These HTTP listeners should be merged into a single HTTP connection manager, +# and the filters should be merged into the DefaultFilterChain of the HTTP connection manager. +http: + - name: default/gateway-1/http + address: 0.0.0.0 + hostnames: + - 'www.foo.com' + isHTTP2: false + http3: + quicPort: 443 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + hostname: www.foo.com + isHTTP2: false + pathMatch: + distinct: false + name: "" + prefix: /foo1 + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 192.168.1.1 + port: 8080 + protocol: HTTP + weight: 1 + basicAuth: + name: securitypolicy/default/policy-for-http-route-1 + users: "dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=" + - name: httproute/default/httproute-2/rule/0/match/0/www_foo_com + hostname: www.foo.com + isHTTP2: false + pathMatch: + distinct: false + name: "" + prefix: /foo2 + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 192.168.1.2 + port: 8080 + protocol: HTTP + weight: 1 + extAuth: + name: securitypolicy/default/policy-for-http-route-2 + failOpen: true + http: + authority: http-backend.envoy-gateway:80 + destination: + name: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 80 + protocol: HTTP + weight: 1 + headersToBackend: + - header1 + - header2 + path: /auth + - name: default/gateway-2/http + address: 0.0.0.0 + hostnames: + - 'www.bar.com' + isHTTP2: false + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - name: httproute/default/httproute-3/rule/0/match/0/www_bar_com + hostname: www.bar.com + isHTTP2: false + pathMatch: + distinct: false + name: "" + prefix: /bar + backendWeights: + invalid: 0 + valid: 0 + destination: + name: httproute/default/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 192.168.1.3 + port: 8080 + protocol: HTTP + weight: 1 + oidc: + name: securitypolicy/default/policy-for-gateway-2 + clientID: client.oauth.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + tokenEndpoint: https://oauth.foo.com/token + scopes: + - openid + - email + - profile + redirectURL: "https://www.example.com/foo/oauth2/callback" + redirectPath: "/foo/oauth2/callback" + logoutPath: "/foo/logout" + cookieSuffix: 5F93C2E4 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml new file mode 100644 index 0000000000..fccf73d51f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-listeners-same-port.yaml @@ -0,0 +1,116 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: first-listener + # byte slice representation of "cert-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "second-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.net" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: second-listener + # byte slice representation of "cert-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "second-route" + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "third-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "example.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "third-route" + hostname: "*" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "fourth-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "example.net" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "fourth-route" + hostname: "*" + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +tcp: +- name: "fifth-listener" + address: "0.0.0.0" + port: 10080 + tls: + passthrough: + snis: + - bar.com + destination: + name: "tcp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "sixth-listener" + address: "0.0.0.0" + port: 10080 + tls: + passthrough: + snis: + - bar.net + destination: + name: "tls-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml new file mode 100644 index 0000000000..fca8012cd1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/multiple-simple-tcp-route-same-port.yaml @@ -0,0 +1,56 @@ +tcp: +- name: "tcp-route-simple" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 +- name: "tcp-route-simple-1" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-1-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 +- name: "tcp-route-simple-2" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-2-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 +- name: "tcp-route-simple-3" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-3-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 +- name: "tcp-route-simple-4" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-4-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mutual-tls.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mutual-tls.yaml new file mode 100644 index 0000000000..ea7ebf48a4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/mutual-tls.yaml @@ -0,0 +1,34 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + caCertificate: + name: ca-cert + certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/oidc.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/oidc.yaml new file mode 100644 index 0000000000..b90a1c97f4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/oidc.yaml @@ -0,0 +1,58 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + oidc: + clientID: client.oauth.foo.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + tokenEndpoint: https://oauth.foo.com/token + scopes: + - openid + - email + - profile + redirectURL: "https://www.example.com/foo/oauth2/callback" + redirectPath: "/foo/oauth2/callback" + logoutPath: "/foo/logout" + cookieSuffix: 5F93C2E4 + - name: "second-route" + hostname: "*" + pathMatch: + exact: "bar" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + oidc: + clientID: client.oauth.bar.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + provider: + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + tokenEndpoint: https://oauth.bar.com/token + scopes: + - openid + - email + - profile + redirectURL: "https://www.example.com/bar/oauth2/callback" + redirectPath: "/bar/oauth2/callback" + logoutPath: "/bar/logout" + cookieSuffix: 5f93c2e4 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/path-settings.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/path-settings.yaml new file mode 100644 index 0000000000..1eddbaab25 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/path-settings.yaml @@ -0,0 +1,18 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: false + escapedSlashesAction: UnescapeAndForward + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml new file mode 100644 index 0000000000..adf21ca8fd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/proxy-protocol-upstream.yaml @@ -0,0 +1,20 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + proxyProtocol: + version: "V2" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/retry-partial-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/retry-partial-invalid.yaml new file mode 100644 index 0000000000..7f5938fd64 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/retry-partial-invalid.yaml @@ -0,0 +1,54 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + retry: + numRetries: 5 + retryOn: + httpStatusCodes: + - 429 + - 503 + triggers: + - reset + - connect-failure + - retriable-status-codes + perRetry: + timeout: 250ms + backoff: + baseInterval: 100ms + maxInterval: 10s + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route-defaults" + hostname: "foo" + retry: {} + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route-error" + hostname: "bar" + retry: + retryOn: + triggers: + - this-is-not-a-trigger + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/simple-tls.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/simple-tls.yaml new file mode 100644 index 0000000000..7309020334 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/simple-tls.yaml @@ -0,0 +1,31 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/suppress-envoy-headers.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/suppress-envoy-headers.yaml new file mode 100644 index 0000000000..f26d13b084 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/suppress-envoy-headers.yaml @@ -0,0 +1,33 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + tls: + alpnProtocols: + - h2 + - http/1.1 + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + headers: + enableEnvoyHeaders: true + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-complex.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-complex.yaml new file mode 100644 index 0000000000..05ac886693 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-complex.yaml @@ -0,0 +1,18 @@ +tcp: +- name: "tcp-route-complex" + address: "0.0.0.0" + port: 10080 + tls: + passthrough: + snis: + - foo.com + - bar.com + - example.com + destination: + name: "tcp-route-complex-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml new file mode 100644 index 0000000000..3885afa4fd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid-endpoint.yaml @@ -0,0 +1,11 @@ +tcp: +- name: "tcp-route-simple" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid.yaml new file mode 100644 index 0000000000..d14b325d3d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-invalid.yaml @@ -0,0 +1,12 @@ +tcp: +- name: "tcp-route-invalid" + address: "" + port: 10080 + destination: + name: "tcp-route-invalid-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-simple.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-simple.yaml new file mode 100644 index 0000000000..f79cebf3e6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-simple.yaml @@ -0,0 +1,12 @@ +tcp: +- name: "tcp-route-simple" + address: "0.0.0.0" + port: 10080 + destination: + name: "tcp-route-simple-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml new file mode 100644 index 0000000000..0bd68608bd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-tls-terminate.yaml @@ -0,0 +1,18 @@ +tcp: +- name: "tls-terminate" + address: "0.0.0.0" + port: 10080 + tls: + terminate: + certificates: + - Name: envoy-gateway-tls-secret-1 + PrivateKey: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + ServerCertificate: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + destination: + name: "tls-terminate-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml new file mode 100644 index 0000000000..0bd5ac621a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tcp-route-weighted-backend.yaml @@ -0,0 +1,29 @@ +tcp: +- name: "tcp-route-weighted-backend" + address: "0.0.0.0" + port: 10080 + tls: + passthrough: + snis: + - foo.com + - bar.com + - example.com + destination: + name: "tcp-route-weighted-backend-dest" + settings: + - endpoints: + - host: "1.1.1.1" + port: 50001 + weight: 20 + - endpoints: + - host: "2.2.2.2" + port: 50002 + weight: 40 + - endpoints: + - host: "3.3.3.3" + port: 50003 + weight: 20 + - endpoints: + - host: "4.4.4.4" + port: 50004 + weight: 20 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/timeout.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/timeout.yaml new file mode 100644 index 0000000000..daee154f05 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/timeout.yaml @@ -0,0 +1,24 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + timeout: + tcp: + connectTimeout: "31s" + http: + connectionIdleTimeout: "32s" + maxConnectionDuration: "33s" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-route-passthrough.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-route-passthrough.yaml new file mode 100644 index 0000000000..a798d00b8f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-route-passthrough.yaml @@ -0,0 +1,30 @@ +tcp: +- name: "tls-passthrough-foo" + address: "0.0.0.0" + port: 10080 + tls: + passthrough: + snis: + - foo.com + destination: + name: "tls-passthrough-foo-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 +- name: "tls-passthrough-bar" + address: "0.0.0.0" + port: 10081 + tls: + passthrough: + snis: + - bar.com + destination: + name: "tls-passthrough-bar-dest" + settings: + - endpoints: + - host: "bar" + port: 50000 + addressType: FQDN diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml new file mode 100644 index 0000000000..c2dcb03028 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml @@ -0,0 +1,50 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + hostnames: + - "foo.com" + tls: + ciphers: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + ecdhCurves: + - X25519 + - P-256 + signatureAlgorithms: + - ecdsa_secp256r1_sha256 + - rsa_pss_rsae_sha256 + - rsa_pkcs1_sha256 + - ecdsa_secp384r1_sha384 + - rsa_pss_rsae_sha384 + - rsa_pkcs1_sha384 + - rsa_pss_rsae_sha512 + - rsa_pkcs1_sha512 + - rsa_pkcs1_sha1 + maxVersion: "1.2" + minVersion: "1.0" + alpnProtocols: + - some-other-protocol + certificates: + - name: secret-1 + # byte slice representation of "key-data" + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + # byte slice representation of "key-data" + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + - name: secret-2 + serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97] + privateKey: [107, 101, 121, 45, 100, 97, 116, 97] + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing-invalid.yaml new file mode 100644 index 0000000000..d8b23c5d21 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing-invalid.yaml @@ -0,0 +1,43 @@ +name: "tracing" +tracing: + serviceName: "fake-name.fake-ns" + samplingRate: 90 + customTags: + "literal1": + type: Literal + literal: + value: "value1" + "env1": + type: Environment + environment: + name: "env1" + defaultValue: "-" + "req1": + type: RequestHeader + requestHeader: + name: "X-Request-Id" + defaultValue: "-" + provider: + host: "" + port: 4317 +http: + - name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing.yaml new file mode 100644 index 0000000000..d67ab2d015 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/tracing.yaml @@ -0,0 +1,43 @@ +name: "tracing" +tracing: + serviceName: "fake-name.fake-ns" + samplingRate: 90 + customTags: + "literal1": + type: Literal + literal: + value: "value1" + "env1": + type: Environment + environment: + name: "env1" + defaultValue: "-" + "req1": + type: RequestHeader + requestHeader: + name: "X-Request-Id" + defaultValue: "-" + provider: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 +http: + - name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route-invalid.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route-invalid.yaml new file mode 100644 index 0000000000..39ab4e7f40 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route-invalid.yaml @@ -0,0 +1,10 @@ +udp: +- name: "udp-route" + port: 10080 + destination: + name: "udp-reoute-dest" + settings: + - endpoints: + - host: "" + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route.yaml new file mode 100644 index 0000000000..fa4ffafc13 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/udp-route.yaml @@ -0,0 +1,12 @@ +udp: +- name: "udp-route" + address: "0.0.0.0" + port: 10080 + destination: + name: "udp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - host: "5.6.7.8" + port: 50001 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml new file mode 100644 index 0000000000..bb7febec8b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/in/xds-ir/upstream-tcpkeepalive.yaml @@ -0,0 +1,22 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + tcpKeepalive: + idleTime: 1200 + interval: 60 + probes: 3 + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.clusters.yaml new file mode 100644 index 0000000000..7168156486 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.clusters.yaml @@ -0,0 +1,49 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.default.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog|otel-collector.default.svc.cluster.local|4317/backend/0 + name: accesslog|otel-collector.default.svc.cluster.local|4317 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.endpoints.yaml new file mode 100644 index 0000000000..20c80b3aaa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.listeners.yaml new file mode 100644 index 0000000000..21e2c2f66c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.listeners.yaml @@ -0,0 +1,143 @@ +- accessLog: + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + path: /dev/stdout + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + path: /dev/stdout + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.routes.yaml new file mode 100644 index 0000000000..d4a7fa5ae2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/accesslog.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - directResponse: + body: + inlineString: 'Unknown custom filter type: UnsupportedType' + status: 500 + match: + prefix: / + name: direct-route diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.listeners.yaml new file mode 100644 index 0000000000..6fd92310d0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.listeners.yaml @@ -0,0 +1,39 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.basic_auth_first-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth + users: + inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.routes.yaml new file mode 100644 index 0000000000..f87be11474 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/basic-auth.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.basic_auth_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.clusters.yaml new file mode 100644 index 0000000000..90636e8ffe --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.clusters.yaml @@ -0,0 +1,27 @@ +- circuitBreakers: + thresholds: + - maxConnections: 1 + maxPendingRequests: 1 + maxRequests: 1 + maxRetries: 2 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + commonHttpProtocolOptions: + maxRequestsPerConnection: 10 + explicitHttpConfig: + httpProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/circuit-breaker.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.listeners.yaml new file mode 100644 index 0000000000..af7af60272 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.listeners.yaml @@ -0,0 +1,34 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + requestTimeout: 5s + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/client-timeout.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.listeners.yaml new file mode 100644 index 0000000000..624c747048 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.listeners.yaml @@ -0,0 +1,36 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.cors + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.routes.yaml new file mode 100644 index 0000000000..f776257868 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/cors.routes.yaml @@ -0,0 +1,26 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.cors: + '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy + allowCredentials: true + allowHeaders: x-header-1, x-header-2 + allowMethods: GET, POST + allowOriginStringMatch: + - safeRegex: + regex: '*.example.com' + - exact: foo.bar.com + exposeHeaders: x-header-3, x-header-4 + maxAge: "1000" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.clusters.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.clusters.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.endpoints.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.endpoints.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.listeners.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.listeners.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/empty.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.clusters.yaml new file mode 100644 index 0000000000..d02aa6b4aa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.clusters.yaml @@ -0,0 +1,73 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-1/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-1/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-2/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-2/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: securitypolicy/default/policy-for-first-route/http-backend + lbPolicy: LEAST_REQUEST + name: securitypolicy/default/policy-for-first-route/http-backend + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: securitypolicy/default/policy-for-second-route/grpc-backend + lbPolicy: LEAST_REQUEST + name: securitypolicy/default/policy-for-second-route/grpc-backend + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.endpoints.yaml new file mode 100644 index 0000000000..ce53193cdb --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: httproute/default/httproute-1/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.0.0.1 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-1/rule/0/backend/0 +- clusterName: httproute/default/httproute-2/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.0.0.2 + portValue: 60000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-2/rule/0/backend/0 +- clusterName: securitypolicy/default/policy-for-first-route/http-backend + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 80 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: securitypolicy/default/policy-for-first-route/http-backend/backend/0 +- clusterName: securitypolicy/default/policy-for-second-route/grpc-backend + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 8.8.8.8 + portValue: 9000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: securitypolicy/default/policy-for-second-route/grpc-backend/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.listeners.yaml new file mode 100644 index 0000000000..0913264d99 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.listeners.yaml @@ -0,0 +1,63 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.ext_authz_httproute/default/httproute-1/rule/0/match/0/www_example_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + httpService: + authorizationResponse: + allowedUpstreamHeaders: + patterns: + - exact: header1 + - exact: header2 + pathPrefix: /auth + serverUri: + cluster: securitypolicy/default/policy-for-first-route/http-backend + timeout: 10s + uri: http://http-backend.envoy-gateway:80/auth + transportApiVersion: V3 + - disabled: true + name: envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_example_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + allowedHeaders: + patterns: + - exact: header1 + - exact: header2 + grpcService: + envoyGrpc: + authority: grpc-backend.default:9000 + clusterName: securitypolicy/default/policy-for-second-route/grpc-backend + timeout: 10s + transportApiVersion: V3 + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.routes.yaml new file mode 100644 index 0000000000..0b039af3da --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/ext-auth.routes.yaml @@ -0,0 +1,29 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo + name: httproute/default/httproute-1/rule/0/match/0/www_example_com + route: + cluster: httproute/default/httproute-1/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.ext_authz_httproute/default/httproute-1/rule/0/match/0/www_example_com: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - match: + path: bar + name: httproute/default/httproute-2/rule/0/match/0/www_example_com + route: + cluster: httproute/default/httproute-2/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_example_com: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.clusters.yaml new file mode 100644 index 0000000000..03e10ccd7f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.clusters.yaml @@ -0,0 +1,85 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.endpoints.yaml new file mode 100644 index 0000000000..b93d9b43bd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.endpoints.yaml @@ -0,0 +1,60 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.listeners.yaml new file mode 100644 index 0000000000..ff6bf7f2ac --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.listeners.yaml @@ -0,0 +1,36 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.fault + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.routes.yaml new file mode 100644 index 0000000000..1f093f5a0c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/fault-injection.routes.yaml @@ -0,0 +1,84 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.fault: + '@type': type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault + abort: + httpStatus: 500 + percentage: + denominator: MILLION + numerator: 100 + - match: + path: example + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.fault: + '@type': type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault + delay: + fixedDelay: 5.400s + percentage: + denominator: MILLION + numerator: 800000 + - match: + path: test + name: third-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.fault: + '@type': type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault + abort: + httpStatus: 500 + percentage: + denominator: MILLION + numerator: 1000000 + delay: + fixedDelay: 5.005s + percentage: + denominator: MILLION + numerator: 1000000 + - match: + path: test + name: fourth-route + route: + cluster: fourth-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.fault: + '@type': type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault + abort: + grpcStatus: 14 + percentage: + denominator: MILLION + numerator: 1000000 + delay: + fixedDelay: 5.005s + percentage: + denominator: MILLION + numerator: 1000000 + - match: + path: test + name: fifth-route + route: + cluster: fifth-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.clusters.yaml new file mode 100644 index 0000000000..b789b876c3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.clusters.yaml @@ -0,0 +1,141 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + healthChecks: + - healthyThreshold: 1 + httpHealthCheck: + expectedStatuses: + - end: "201" + start: "200" + - end: "301" + start: "300" + host: '*' + path: /healthz + receive: + - text: 6f6b + interval: 3s + timeout: 0.500s + unhealthyThreshold: 3 + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: + baseEjectionTime: 180s + consecutive5xx: 5 + consecutiveGatewayFailure: 0 + consecutiveLocalOriginFailure: 5 + interval: 2s + maxEjectionPercent: 100 + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + healthChecks: + - healthyThreshold: 3 + httpHealthCheck: + expectedStatuses: + - end: "202" + start: "200" + host: '*' + path: /healthz + receive: + - binary: cG9uZw== + interval: 5s + timeout: 1s + unhealthyThreshold: 3 + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: + baseEjectionTime: 180s + consecutive5xx: 5 + consecutiveGatewayFailure: 0 + consecutiveLocalOriginFailure: 5 + interval: 1s + maxEjectionPercent: 100 + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + healthChecks: + - healthyThreshold: 3 + interval: 5s + tcpHealthCheck: + receive: + - text: 706f6e67 + send: + text: "70696e67" + timeout: 1s + unhealthyThreshold: 3 + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: + baseEjectionTime: 160s + consecutive5xx: 5 + consecutiveGatewayFailure: 0 + consecutiveLocalOriginFailure: 5 + interval: 1s + maxEjectionPercent: 100 + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + healthChecks: + - healthyThreshold: 3 + interval: 5s + tcpHealthCheck: + receive: + - binary: cG9uZw== + send: + binary: cGluZw== + timeout: 1s + unhealthyThreshold: 3 + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: + baseEjectionTime: 180s + consecutive5xx: 5 + consecutiveGatewayFailure: 0 + consecutiveLocalOriginFailure: 5 + interval: 1s + maxEjectionPercent: 90 + splitExternalLocalOriginErrors: true + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.endpoints.yaml new file mode 100644 index 0000000000..f185af17da --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.routes.yaml new file mode 100644 index 0000000000..de4249178e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/health-check.routes.yaml @@ -0,0 +1,35 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: fourth-route + route: + cluster: fourth-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml new file mode 100644 index 0000000000..f0ea3b3232 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml new file mode 100644 index 0000000000..20c80b3aaa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml new file mode 100644 index 0000000000..d4a7fa5ae2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-direct-response.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - directResponse: + body: + inlineString: 'Unknown custom filter type: UnsupportedType' + status: 500 + match: + prefix: / + name: direct-route diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml new file mode 100644 index 0000000000..876e1084c8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.clusters.yaml @@ -0,0 +1,33 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: foo.bar + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: bar.foo + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.endpoints.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml new file mode 100644 index 0000000000..c27e2ea577 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-dns-cluster.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml new file mode 100644 index 0000000000..53d1f9a7c1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: route-dest + lbPolicy: LEAST_REQUEST + name: route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml new file mode 100644 index 0000000000..43cba2e797 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml new file mode 100644 index 0000000000..c5a187d2c2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-mirror.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: mirror-route + route: + cluster: route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml new file mode 100644 index 0000000000..0322cbb616 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.clusters.yaml @@ -0,0 +1,119 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: sixth-route-dest + lbPolicy: LEAST_REQUEST + name: sixth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: seventh-route-dest + lbPolicy: LEAST_REQUEST + name: seventh-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml new file mode 100644 index 0000000000..3787880a31 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.endpoints.yaml @@ -0,0 +1,84 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 8.8.8.8 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 8.8.8.8 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 +- clusterName: sixth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: sixth-route-dest/backend/0 +- clusterName: seventh-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: seventh-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml new file mode 100644 index 0000000000..25584aa3a9 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-matches.routes.yaml @@ -0,0 +1,80 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - example.com + name: first-listener/example_com + routes: + - match: + pathSeparatedPrefix: /v1/example + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: envoy-gateway/httproute-2/rule/0/match/0/example.com + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + pathSeparatedPrefix: /v1/example + name: envoy-gateway/httproute-3/rule/0/match/0/example.com + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + - domains: + - example.net + name: first-listener/example_net + routes: + - match: + headers: + - name: version + stringMatch: + exact: one + pathSeparatedPrefix: /v1/status + name: envoy-gateway/httproute-4/rule/0/match/0/example.net + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + pathSeparatedPrefix: /v1/status + name: envoy-gateway/httproute-5/rule/0/match/0/example.net + route: + cluster: fourth-route-dest + upgradeConfigs: + - upgradeType: websocket + - domains: + - '*.com' + name: first-listener/*_com + routes: + - match: + pathSeparatedPrefix: /foo + name: envoy-gateway/httproute-1/rule/0/match/0/*.com + route: + cluster: fifth-route-dest + upgradeConfigs: + - upgradeType: websocket + - domains: + - '*.net' + name: first-listener/*_net + routes: + - match: + pathSeparatedPrefix: /foo + name: envoy-gateway/httproute-1/rule/0/match/0/*.net + route: + cluster: sixth-route-dest + upgradeConfigs: + - upgradeType: websocket + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: envoy-gateway/httproute-1/rule/0/match/0/* + route: + cluster: seventh-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml new file mode 100644 index 0000000000..046021604d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.clusters.yaml @@ -0,0 +1,51 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: route-dest + lbPolicy: LEAST_REQUEST + name: route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: mirror-route-dest + lbPolicy: LEAST_REQUEST + name: mirror-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: mirror-route-dest1 + lbPolicy: LEAST_REQUEST + name: mirror-route-dest1 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml new file mode 100644 index 0000000000..f741af39cd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: route-dest/backend/0 +- clusterName: mirror-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 2.3.4.5 + portValue: 0 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: mirror-route-dest/backend/0 +- clusterName: mirror-route-dest1 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 3.4.5.6 + portValue: 0 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: mirror-route-dest1/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml new file mode 100644 index 0000000000..f46cc50eb5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-multiple-mirrors.routes.yaml @@ -0,0 +1,17 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: mirror-route + route: + cluster: route-dest + requestMirrorPolicies: + - cluster: mirror-route-dest + - cluster: mirror-route-dest1 + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml new file mode 100644 index 0000000000..6149681771 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: valid-route-dest + lbPolicy: LEAST_REQUEST + name: valid-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml new file mode 100644 index 0000000000..4fdb0384f3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: valid-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: valid-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml new file mode 100644 index 0000000000..61face5a3a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-partial-invalid.routes.yaml @@ -0,0 +1,43 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + - name: test + stringMatch: + suffix: end + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: valid-route + route: + cluster: valid-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + headers: + - name: user + stringMatch: + exact: jason + - name: test + stringMatch: + suffix: end + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: invalid-route + route: + cluster: invalid-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml new file mode 100644 index 0000000000..b435363bef --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: redirect-route-dest + lbPolicy: LEAST_REQUEST + name: redirect-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml new file mode 100644 index 0000000000..7543b702fb --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: redirect-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: redirect-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml new file mode 100644 index 0000000000..d2c46435ee --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-redirect.routes.yaml @@ -0,0 +1,32 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: redirect-route-1 + redirect: + hostRedirect: redirected.com + portRedirect: 8443 + prefixRewrite: /redirected + responseCode: FOUND + schemeRedirect: https + - match: + pathSeparatedPrefix: /redirect + name: redirect-route-2 + redirect: + regexRewrite: + pattern: + regex: ^/redirect\/* + substitution: / + - match: + pathSeparatedPrefix: /redirect + name: redirect-route-3 + redirect: + regexRewrite: + pattern: + regex: ^/redirect/\/* + substitution: / diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml new file mode 100644 index 0000000000..0f75e67e27 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: regex-route-dest + lbPolicy: LEAST_REQUEST + name: regex-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml new file mode 100644 index 0000000000..b36ee45005 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: regex-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: regex-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.routes.yaml new file mode 100644 index 0000000000..7bc7d162a8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-regex.routes.yaml @@ -0,0 +1,25 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: re_header + stringMatch: + safeRegex: + regex: .* + queryParameters: + - name: re_query + stringMatch: + safeRegex: + regex: .* + safeRegex: + regex: /v1/.* + name: regex-route + route: + cluster: regex-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml new file mode 100644 index 0000000000..2adb8e01e4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: request-header-route-dest + lbPolicy: LEAST_REQUEST + name: request-header-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml new file mode 100644 index 0000000000..846a89074e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: request-header-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: request-header-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml new file mode 100644 index 0000000000..f91a70cb2e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-request-headers.routes.yaml @@ -0,0 +1,36 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: request-header-route + requestHeadersToAdd: + - header: + key: some-header + value: some-value + - header: + key: some-header-2 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header3 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header4 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: empty-header + keepEmptyValue: true + requestHeadersToRemove: + - some-header5 + - some-header6 + route: + cluster: request-header-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml new file mode 100644 index 0000000000..ca020e482f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml new file mode 100644 index 0000000000..d21491f621 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: response-header-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: response-header-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml new file mode 100644 index 0000000000..b2241a1181 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-headers.routes.yaml @@ -0,0 +1,33 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: response-header-route + responseHeadersToAdd: + - header: + key: some-header + value: some-value + - header: + key: some-header-2 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header3 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header4 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: empty-header + keepEmptyValue: true + route: + cluster: response-header-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml new file mode 100644 index 0000000000..ca020e482f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml new file mode 100644 index 0000000000..d21491f621 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: response-header-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: response-header-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml new file mode 100644 index 0000000000..b09594aecd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-add-remove-headers.routes.yaml @@ -0,0 +1,36 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: response-header-route + responseHeadersToAdd: + - header: + key: some-header + value: some-value + - header: + key: some-header-2 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header3 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: some-header4 + value: some-value + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: empty-header + keepEmptyValue: true + responseHeadersToRemove: + - some-header5 + - some-header6 + route: + cluster: response-header-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml new file mode 100644 index 0000000000..ca020e482f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: response-header-route-dest + lbPolicy: LEAST_REQUEST + name: response-header-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml new file mode 100644 index 0000000000..d21491f621 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: response-header-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: response-header-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml new file mode 100644 index 0000000000..58a4e27209 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-response-remove-headers.routes.yaml @@ -0,0 +1,17 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: response-header-route + responseHeadersToRemove: + - some-header5 + - some-header6 + route: + cluster: response-header-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml new file mode 100644 index 0000000000..027db39fb2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml new file mode 100644 index 0000000000..256dda0908 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: rewrite-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: rewrite-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml new file mode 100644 index 0000000000..d5a0bd9899 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-root-path-url-prefix.routes.yaml @@ -0,0 +1,22 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io + routes: + - match: + headers: + - name: :authority + stringMatch: + exact: gateway.envoyproxy.io + pathSeparatedPrefix: /origin + name: rewrite-route + route: + cluster: rewrite-route-dest + regexRewrite: + pattern: + regex: ^/origin/\/* + substitution: / + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml new file mode 100644 index 0000000000..3a2b7308d8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: rewrite-route + lbPolicy: LEAST_REQUEST + name: rewrite-route + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml new file mode 100644 index 0000000000..ca1ef21c98 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: rewrite-route + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: rewrite-route/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml new file mode 100644 index 0000000000..f8b81712da --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-fullpath.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io + routes: + - match: + pathSeparatedPrefix: /origin + name: rewrite-route + route: + cluster: rewrite-route + regexRewrite: + pattern: + regex: /.+ + substitution: /rewrite + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml new file mode 100644 index 0000000000..027db39fb2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml new file mode 100644 index 0000000000..256dda0908 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: rewrite-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: rewrite-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml new file mode 100644 index 0000000000..680a67404e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml @@ -0,0 +1,21 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io + routes: + - match: + headers: + - name: :authority + stringMatch: + exact: gateway.envoyproxy.io + pathSeparatedPrefix: /origin + name: rewrite-route + route: + appendXForwardedHost: true + cluster: rewrite-route-dest + hostRewriteLiteral: 3.3.3.3 + prefixRewrite: /rewrite + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml new file mode 100644 index 0000000000..027db39fb2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: rewrite-route-dest + lbPolicy: LEAST_REQUEST + name: rewrite-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml new file mode 100644 index 0000000000..256dda0908 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: rewrite-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: rewrite-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml new file mode 100644 index 0000000000..84bc70f04b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-rewrite-url-prefix.routes.yaml @@ -0,0 +1,19 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - gateway.envoyproxy.io + name: first-listener/gateway_envoyproxy_io + routes: + - match: + headers: + - name: :authority + stringMatch: + exact: gateway.envoyproxy.io + pathSeparatedPrefix: /origin + name: rewrite-route + route: + cluster: rewrite-route-dest + prefixRewrite: /rewrite + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml new file mode 100644 index 0000000000..a89644e62d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml @@ -0,0 +1,51 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml new file mode 100644 index 0000000000..42a346c404 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml new file mode 100644 index 0000000000..23d2bf4b70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml @@ -0,0 +1,38 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + idleTimeout: 3600s + timeout: 5s + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + idleTimeout: 4000s + timeout: 4000s + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + idleTimeout: 0s + timeout: 0s + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml new file mode 100644 index 0000000000..f5633bd158 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.endpoints.yaml @@ -0,0 +1,42 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.1.1.1 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/0 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 2.2.2.2 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 40 + locality: + region: first-route-dest/backend/1 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 3.3.3.3 + portValue: 50003 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/2 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 4.4.4.4 + portValue: 50004 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: first-route-dest/backend/3 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-backend.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml new file mode 100644 index 0000000000..235dea4272 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-weighted-invalid-backend.routes.yaml @@ -0,0 +1,20 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + clusterNotFoundResponseCode: INTERNAL_SERVER_ERROR + upgradeConfigs: + - upgradeType: websocket + weightedClusters: + clusters: + - name: invalid-backend-cluster + weight: 1 + - name: first-route-dest + weight: 1 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.clusters.yaml new file mode 100644 index 0000000000..2c908f7ef6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.clusters.yaml @@ -0,0 +1,13 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route + name: first-route + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.listeners.yaml new file mode 100644 index 0000000000..9f0e050cfb --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-stripped-host-port.listeners.yaml @@ -0,0 +1,30 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.clusters.yaml new file mode 100644 index 0000000000..670a5464ed --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.clusters.yaml @@ -0,0 +1,30 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway/httproute-btls/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway/httproute-btls/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocketMatches: + - match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + sni: example.com + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.endpoints.yaml new file mode 100644 index 0000000000..3d0e816076 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.endpoints.yaml @@ -0,0 +1,16 @@ +- clusterName: httproute/envoy-gateway/httproute-btls/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.11 + portValue: 8080 + loadBalancingWeight: 1 + metadata: + filterMetadata: + envoy.transport_socket_match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.listeners.yaml new file mode 100644 index 0000000000..0f95e32bde --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-btls/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: envoy-gateway/gateway-btls/http + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.routes.yaml new file mode 100644 index 0000000000..bd4f9cfe7e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-btls/http + virtualHosts: + - domains: + - '*' + name: envoy-gateway/gateway-btls/http/* + routes: + - match: + path: /exact + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + route: + cluster: httproute/envoy-gateway/httproute-btls/rule/0 + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.secrets.yaml new file mode 100755 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tls-system-truststore.secrets.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.clusters.yaml new file mode 100755 index 0000000000..8a7225b4cc --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.clusters.yaml @@ -0,0 +1,94 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway/httproute-btls/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway/httproute-btls/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocketMatches: + - match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContextSdsSecretConfig: + name: policy-btls/policies-ca2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + sni: bar.example.com + - match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/1 + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/1 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContextSdsSecretConfig: + name: policy-btls/policies-ca + sdsConfig: + ads: {} + resourceApiVersion: V3 + sni: example.com + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway/httproute-btls-2/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway/httproute-btls-2/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.upstream_proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport + config: + version: V2 + transportSocket: + name: envoy.transport_sockets.raw_buffer + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer + transportSocketMatches: + - match: + name: httproute/envoy-gateway/httproute-btls-2/rule/0/tls/1 + name: httproute/envoy-gateway/httproute-btls-2/rule/0/tls/1 + transportSocket: + name: envoy.transport_sockets.upstream_proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport + config: + version: V2 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContextSdsSecretConfig: + name: policy-btls-2/policies-ca + sdsConfig: + ads: {} + resourceApiVersion: V3 + sni: example.com + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.endpoints.yaml new file mode 100755 index 0000000000..94a0deb060 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.endpoints.yaml @@ -0,0 +1,56 @@ +- clusterName: httproute/envoy-gateway/httproute-btls/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.12 + portValue: 8080 + loadBalancingWeight: 1 + metadata: + filterMetadata: + envoy.transport_socket_match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.11 + portValue: 8080 + loadBalancingWeight: 1 + metadata: + filterMetadata: + envoy.transport_socket_match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/1 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls/rule/0/backend/1 +- clusterName: httproute/envoy-gateway/httproute-btls-2/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.12 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls-2/rule/0/backend/0 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.11 + portValue: 8080 + loadBalancingWeight: 1 + metadata: + filterMetadata: + envoy.transport_socket_match: + name: httproute/envoy-gateway/httproute-btls-2/rule/0/tls/1 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls-2/rule/0/backend/1 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.listeners.yaml new file mode 100755 index 0000000000..8e0e6200c6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.listeners.yaml @@ -0,0 +1,66 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-btls/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: envoy-gateway/gateway-btls/http + perConnectionBufferLimitBytes: 32768 +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-btls-2/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: envoy-gateway/gateway-btls-2/http + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.routes.yaml new file mode 100755 index 0000000000..1d716e5c21 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.routes.yaml @@ -0,0 +1,28 @@ +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-btls/http + virtualHosts: + - domains: + - '*' + name: envoy-gateway/gateway-btls/http/* + routes: + - match: + path: /exact + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + route: + cluster: httproute/envoy-gateway/httproute-btls/rule/0 + upgradeConfigs: + - upgradeType: websocket +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-btls-2/http + virtualHosts: + - domains: + - '*' + name: envoy-gateway/gateway-btls-2/http/* + routes: + - match: + path: /exact + name: httproute/envoy-gateway/httproute-btls-2/rule/0/match/0/* + route: + cluster: httproute/envoy-gateway/httproute-btls-2/rule/0 + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.secrets.yaml new file mode 100755 index 0000000000..6e227be87f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle-multiple-certs.secrets.yaml @@ -0,0 +1,12 @@ +- name: policy-btls/policies-ca2 + validationContext: + trustedCa: + inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +- name: policy-btls/policies-ca + validationContext: + trustedCa: + inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K +- name: policy-btls-2/policies-ca + validationContext: + trustedCa: + inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml new file mode 100644 index 0000000000..695386bb24 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.clusters.yaml @@ -0,0 +1,32 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/envoy-gateway/httproute-btls/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/envoy-gateway/httproute-btls/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocketMatches: + - match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContextSdsSecretConfig: + name: policy-btls/policies-ca + sdsConfig: + ads: {} + resourceApiVersion: V3 + sni: example.com + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.endpoints.yaml new file mode 100644 index 0000000000..3d0e816076 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.endpoints.yaml @@ -0,0 +1,16 @@ +- clusterName: httproute/envoy-gateway/httproute-btls/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.244.0.11 + portValue: 8080 + loadBalancingWeight: 1 + metadata: + filterMetadata: + envoy.transport_socket_match: + name: httproute/envoy-gateway/httproute-btls/rule/0/tls/0 + loadBalancingWeight: 1 + locality: + region: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.listeners.yaml new file mode 100644 index 0000000000..0f95e32bde --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-btls/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: envoy-gateway/gateway-btls/http + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.routes.yaml new file mode 100644 index 0000000000..bd4f9cfe7e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-btls/http + virtualHosts: + - domains: + - '*' + name: envoy-gateway/gateway-btls/http/* + routes: + - match: + path: /exact + name: httproute/envoy-gateway/httproute-btls/rule/0/match/0/* + route: + cluster: httproute/envoy-gateway/httproute-btls/rule/0 + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.secrets.yaml new file mode 100644 index 0000000000..be757009ca --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route-with-tlsbundle.secrets.yaml @@ -0,0 +1,4 @@ +- name: policy-btls/policies-ca + validationContext: + trustedCa: + inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKekNDQWcrZ0F3SUJBZ0lVQWw2VUtJdUttenRlODFjbGx6NVBmZE4ySWxJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHQTFVRUNnd0dhM1ZpWldSaU1CNFhEVEl6TVRBdwpNakExTkRFMU4xb1hEVEkwTVRBd01UQTFOREUxTjFvd0l6RVFNQTRHQTFVRUF3d0hiWGxqYVdWdWRERVBNQTBHCkExVUVDZ3dHYTNWaVpXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdTVGMKMXlqOEhXNjJueW5rRmJYbzRWWEt2MmpDMFBNN2RQVmt5ODdGd2VaY1RLTG9XUVZQUUUycDJrTERLNk9Fc3ptTQp5eXIreHhXdHlpdmVyZW1yV3FuS2tOVFloTGZZUGhnUWtjemliN2VVYWxtRmpVYmhXZEx2SGFrYkVnQ29kbjNiCmt6NTdtSW5YMlZwaURPS2c0a3lIZml1WFdwaUJxckN4MEtOTHB4bzNERVFjRmNzUVRlVEh6aDQ3NTJHVjA0UlUKVGkvR0VXeXpJc2w0Umc3dEd0QXdtY0lQZ1VOVWZZMlEzOTBGR3FkSDRhaG4rbXcvNmFGYlczMVc2M2Q5WUpWcQppb3lPVmNhTUlwTTVCL2M3UWM4U3VoQ0kxWUdoVXlnNGNSSExFdzVWdGlraW95RTNYMDRrbmEzalFBajU0WWJSCmJwRWhjMzVhcEtMQjIxSE9VUUlEQVFBQm8xTXdVVEFkQmdOVkhRNEVGZ1FVeXZsMFZJNXZKVlN1WUZYdTdCNDgKNlBiTUVBb3dId1lEVlIwakJCZ3dGb0FVeXZsMFZJNXZKVlN1WUZYdTdCNDg2UGJNRUFvd0R3WURWUjBUQVFILwpCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFNTHhyZ0ZWTXVOUnEyd0F3Y0J0N1NuTlI1Q2Z6CjJNdlhxNUVVbXVhd0lVaTlrYVlqd2RWaURSRUdTams3SlcxN3ZsNTc2SGpEa2RmUndpNEUyOFN5ZFJJblpmNkoKaThIWmNaN2NhSDZEeFIzMzVmZ0hWekxpNU5pVGNlL09qTkJRelEyTUpYVkRkOERCbUc1ZnlhdEppT0pRNGJXRQpBN0ZsUDBSZFAzQ08zR1dFME01aVhPQjJtMXFXa0UyZXlPNFVIdndUcU5RTGRyZEFYZ0RRbGJhbTllNEJHM0dnCmQvNnRoQWtXRGJ0L1FOVCtFSkhEQ3ZoRFJLaDFSdUdIeWcrWSsvbmViVFdXckZXc2t0UnJiT29IQ1ppQ3BYSTEKM2VYRTZudDBZa2d0RHhHMjJLcW5ocEFnOWdVU3MyaGxob3h5dmt6eUYwbXU2TmhQbHdBZ25xNysvUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.routes.yaml new file mode 100644 index 0000000000..7030f6f4cd --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http-route.routes.yaml @@ -0,0 +1,25 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + - name: test + stringMatch: + suffix: end + prefix: / + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.clusters.yaml new file mode 100644 index 0000000000..1489e95f6f --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.clusters.yaml @@ -0,0 +1,55 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + httpProtocolOptions: + headerKeyFormat: + statefulFormatter: + name: preserve_case + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + httpProtocolOptions: + enableTrailers: true + headerKeyFormat: + statefulFormatter: + name: preserve_case + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.endpoints.yaml new file mode 100644 index 0000000000..28a57caf3b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.endpoints.yaml @@ -0,0 +1,24 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.5 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.listeners.yaml new file mode 100644 index 0000000000..b3b279d84c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.listeners.yaml @@ -0,0 +1,79 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + httpProtocolOptions: + headerKeyFormat: + statefulFormatter: + name: preserve_case + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + httpProtocolOptions: + enableTrailers: true + headerKeyFormat: + statefulFormatter: + name: preserve_case + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: second-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: second-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.routes.yaml new file mode 100644 index 0000000000..ff93cfff36 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-preserve-case.routes.yaml @@ -0,0 +1,28 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket +- ignorePortInHostMatching: true + name: second-listener + virtualHosts: + - domains: + - '*' + name: second-listener/* + routes: + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.clusters.yaml new file mode 100644 index 0000000000..8c3dd7a549 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.clusters.yaml @@ -0,0 +1,23 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + httpProtocolOptions: + enableTrailers: true diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.listeners.yaml new file mode 100644 index 0000000000..06b72b5ac4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.listeners.yaml @@ -0,0 +1,35 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + httpProtocolOptions: + enableTrailers: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http1-trailers.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.clusters.yaml new file mode 100644 index 0000000000..2cb022cfad --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.clusters.yaml @@ -0,0 +1,24 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + httpProtocolOptions: + acceptHttp10: true + defaultHostForHttp10: foo.com diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.listeners.yaml new file mode 100644 index 0000000000..ebd95e6e62 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.listeners.yaml @@ -0,0 +1,36 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + httpProtocolOptions: + acceptHttp10: true + defaultHostForHttp10: foo.com + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http10.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.clusters.yaml new file mode 100644 index 0000000000..6086cfbb86 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.clusters.yaml @@ -0,0 +1,22 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.listeners.yaml new file mode 100644 index 0000000000..f885597176 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.listeners.yaml @@ -0,0 +1,41 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.grpc_web + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb + - name: envoy.filters.http.grpc_stats + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig + emitFilterState: true + statsForAllMethods: true + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.routes.yaml new file mode 100644 index 0000000000..a32852fcd8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http2-route.routes.yaml @@ -0,0 +1,22 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + path: foo/bar + queryParameters: + - name: debug + stringMatch: + exact: "yes" + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.clusters.yaml new file mode 100644 index 0000000000..9714612e3d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-1/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-1/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.endpoints.yaml new file mode 100644 index 0000000000..29bb6b4e44 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: httproute/default/httproute-1/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-1/rule/0/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.listeners.yaml new file mode 100644 index 0000000000..277b2065ab --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.listeners.yaml @@ -0,0 +1,99 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10443 + protocol: UDP + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codecType: HTTP3 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + http3ProtocolOptions: {} + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-1/tls + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.quic + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicDownstreamTransport + downstreamTlsContext: + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + - h3 + tlsCertificateSdsSecretConfigs: + - name: envoy-gateway-tls-secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: envoy-gateway/gateway-1/tls-quic + udpListenerConfig: + downstreamSocketConfig: {} + quicOptions: {} +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10443 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-1/tls + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: envoy-gateway-tls-secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: envoy-gateway/gateway-1/tls + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.routes.yaml new file mode 100644 index 0000000000..f3e513cea4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.routes.yaml @@ -0,0 +1,19 @@ +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-1/tls + virtualHosts: + - domains: + - '*' + name: envoy-gateway/gateway-1/tls/* + routes: + - match: + prefix: / + name: httproute/default/httproute-1/rule/0/match/0/* + responseHeadersToAdd: + - append: true + header: + key: alt-svc + value: h3=":443"; ma=86400 + route: + cluster: httproute/default/httproute-1/rule/0 + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.secrets.yaml new file mode 100644 index 0000000000..d4d502ac09 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/http3.secrets.yaml @@ -0,0 +1,6 @@ +- name: envoy-gateway-tls-secret-1 + tlsCertificate: + certificateChain: + inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQVpBQ0NRREVNZ1lZblFyQ29EQU5CZ2txaGtpRzl3MEJBUXNGQURBV01SUXdFZ1lEVlFRRERBdG0KYjI4dVltRnlMbU52YlRBZUZ3MHlNekF4TURVeE16UXpNalJhRncweU5EQXhNRFV4TXpRek1qUmFNQll4RkRBUwpCZ05WQkFNTUMyWnZieTVpWVhJdVkyOXRNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDCkFRRUFuZEh6d21wS2NUSUViamhGZ2RXd1RSTjc1Y3A4b3VsWnhMMUdydlI2SXc3ejdqaTBSNFcvTm85bkdmOU0KWVAyQ1JqaXN6NTFtd3hTeGVCcm9jTGVBK21reGkxK2lEdk5kQytyU0x4MTN6RUxTQ25xYnVzUHM3bUdmSlpxOAo5TGhlbmx5bzQzaDVjYTZINUxqTXd1L1JHVWlGMzFYck5yaVlGQlB2RTJyQitkd24vTkVrUTRoOFJxcXlwcmtuCkYvcWM5Sk1ZQVlGRld1VkNwa0lFbmRYMUN5dlFOT2FkZmN2cmd6dDV2SmwwT2kxQWdyaU5hWGJFUEdudWY3STQKcXBCSEdVWE5lMVdsOVdlVklxS1g0T2FFWERWQzZGQzdHOHptZWVMVzFBa1lFVm5pcFg2b1NCK0JjL1NIVlZOaApzQkxSbXRuc3pmTnRUMlFyZCttcGt4ODBaUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1VKOElDCkJveUVqT3V3enBHYVJoR044QjRqT1B6aHVDT0V0ZDM3UzAybHUwN09IenlCdmJzVEd6S3dCZ0x5bVdmR2tINEIKajdDTHNwOEZ6TkhLWnVhQmdwblo5SjZETE9Od2ZXZTJBWXA3TGRmT0tWQlVkTVhRaU9tN2pKOUhob0Ntdk1ONwpic2pjaFdKb013ckZmK3dkQUthdHowcUFQeWhMeWUvRnFtaVZ4a09SWmF3K1Q5bURaK0g0OXVBU2d1SnVOTXlRClY2RXlYNmd0Z1dxMzc2SHZhWE1TLzNoYW1Zb1ZXWEk1TXhpUE9ZeG5BQmtKQjRTQ2dJUmVqYkpmVmFRdG9RNGEKejAyaVVMZW5ESUllUU9Zb2JLY01CWGYxQjRQQVFtc2VocVZJYnpzUUNHaTU0VkRyczZiWmQvN0pzMXpDcHBncwpKaUQ1SXFNaktXRHdxN2FLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + privateKey: + inlineBytes: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2QwZlBDYWtweE1nUnUKT0VXQjFiQk5FM3ZseW55aTZWbkV2VWF1OUhvakR2UHVPTFJIaGI4MmoyY1ovMHhnL1lKR09LelBuV2JERkxGNApHdWh3dDRENmFUR0xYNklPODEwTDZ0SXZIWGZNUXRJS2VwdTZ3K3p1WVo4bG1yejB1RjZlWEtqamVIbHhyb2ZrCnVNekM3OUVaU0lYZlZlczJ1SmdVRSs4VGFzSDUzQ2Y4MFNSRGlIeEdxckttdVNjWCtwejBreGdCZ1VWYTVVS20KUWdTZDFmVUxLOUEwNXAxOXkrdURPM204bVhRNkxVQ0N1STFwZHNROGFlNS9zamlxa0VjWlJjMTdWYVgxWjVVaQpvcGZnNW9SY05VTG9VTHNiek9aNTR0YlVDUmdSV2VLbGZxaElINEZ6OUlkVlUyR3dFdEdhMmV6TjgyMVBaQ3QzCjZhbVRIelJsQWdNQkFBRUNnZ0VBWTFGTUlLNDVXTkVNUHJ6RTZUY3NNdVV2RkdhQVZ4bVk5NW5SMEtwajdvb3IKY21CVys2ZXN0TTQ4S1AwaitPbXd3VFpMY29Cd3VoWGN0V1Bob1lXcDhteWUxRUlEdjNyaHRHMDdocEQ1NGg2dgpCZzh3ejdFYStzMk9sT0N6UnlKNzBSY281YlhjWDNGaGJjdnFlRWJwaFFyQnpOSEtLMjZ4cmZqNWZIT3p6T1FGCmJHdUZ3SDVic3JGdFhlajJXM3c4eW90N0ZQSDV3S3RpdnhvSWU5RjMyOXNnOU9EQnZqWnpiaG1LVTArckFTK1kKRGVield2bFJyaEUrbXVmQTN6M0N0QXhDOFJpNzNscFNoTDRQQWlvcG1SUXlxZXRXMjYzOFFxcnM0R3hnNzhwbApJUXJXTmNBc2s3Slg5d3RZenV6UFBXSXRWTTFscFJiQVRhNTJqdFl2NVFLQmdRRE5tMTFtZTRYam1ZSFV2cStZCmFTUzdwK2UybXZEMHVaOU9JeFluQnBWMGkrckNlYnFFMkE1Rm5hcDQ5Yld4QTgwUElldlVkeUpCL2pUUkoxcVMKRUpXQkpMWm1LVkg2K1QwdWw1ZUtOcWxFTFZHU0dCSXNpeE9SUXpDZHBoMkx0UmtBMHVjSVUzY3hiUmVMZkZCRQpiSkdZWENCdlNGcWd0VDlvZTFldVpMVmFOd0tCZ1FERWdENzJENk81eGIweEQ1NDQ1M0RPMUJhZmd6aThCWDRTCk1SaVd2LzFUQ0w5N05sRWtoeXovNmtQd1owbXJRcE5CMzZFdkpKZFVteHdkU2MyWDhrOGcxMC85NVlLQkdWQWoKL3d0YVZYbE9WeEFvK0ZSelpZeFpyQ29uWWFSMHVwUzFybDRtenN4REhlZU9mUVZUTUgwUjdZN0pnbTA5dXQ4SwplanAvSXZBb1F3S0JnQjNaRWlRUWhvMVYrWjBTMlpiOG5KS0plMy9zMmxJTXFHM0ZkaS9RS3Q0eWViQWx6OGY5ClBZVXBzRmZEQTg5Z3grSU1nSm5sZVptdTk2ZnRXSjZmdmJSenllN216TG5zZU05TXZua1lHbGFGWmJRWnZubXMKN3ZoRmtzY3dHRlh4d21GMlBJZmU1Z3pNMDRBeVdjeTFIaVhLS2dNOXM3cGsxWUdyZGowZzdacmRBb0dCQUtLNApDR3MrbkRmMEZTMFJYOWFEWVJrRTdBNy9YUFhtSG5YMkRnU1h5N0Q4NTRPaWdTTWNoUmtPNTErbVNJejNQbllvCk41T1FXM2lHVVl1M1YvYmhnc0VSUzM1V2xmRk9BdDBzRUR5bjF5SVdXcDF5dG93d3BUNkVvUXVuZ2NYZjA5RjMKS1NROXowd3M4VmsvRWkvSFVXcU5LOWFXbU51cmFaT0ZqL2REK1ZkOUFvR0FMWFN3dEE3K043RDRkN0VEMURSRQpHTWdZNVd3OHFvdDZSdUNlNkpUY0FnU3B1MkhNU3JVY2dXclpiQnJZb09FUnVNQjFoMVJydk5ybU1qQlM0VW9FClgyZC8vbGhpOG1wL2VESWN3UDNRa2puanBJRFJWMFN1eWxrUkVaZURKZjVZb3R6eDdFdkJhbzFIbkQrWEg4eUIKVUtmWGJTaHZKVUdhRmgxT3Q1Y3JoM1k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.envoypatchpolicies.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.envoypatchpolicies.yaml new file mode 100644 index 0000000000..55cdd5c4cb --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.envoypatchpolicies.yaml @@ -0,0 +1,9 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: The add operation requires a value + reason: Invalid + status: "False" + type: Programmed diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.listeners.yaml new file mode 100644 index 0000000000..03bb654b47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.listeners.yaml @@ -0,0 +1,50 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.routes.yaml new file mode 100644 index 0000000000..4a412b3576 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-add-op-without-value.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml new file mode 100644 index 0000000000..4a412b3576 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml new file mode 100644 index 0000000000..26d7c0af42 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml @@ -0,0 +1,10 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: 'unable to find xds resource type.googleapis.com/envoy.config.listener.v3.Listener: + non-existant-listener' + reason: ResourceNotFound + status: "False" + type: Programmed diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml new file mode 100644 index 0000000000..4a412b3576 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.envoypatchpolicies.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.envoypatchpolicies.yaml new file mode 100644 index 0000000000..fd77280814 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.envoypatchpolicies.yaml @@ -0,0 +1,9 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: The value field can not be set for the remove operation + reason: Invalid + status: "False" + type: Programmed diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.listeners.yaml new file mode 100644 index 0000000000..03bb654b47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.listeners.yaml @@ -0,0 +1,50 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.routes.yaml new file mode 100644 index 0000000000..4a412b3576 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch-move-op-with-value.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml new file mode 100644 index 0000000000..4043644d4e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml @@ -0,0 +1,9 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: successfully applied patches. + reason: Programmed + status: "True" + type: Programmed diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml new file mode 100644 index 0000000000..03bb654b47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml @@ -0,0 +1,50 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.routes.yaml new file mode 100644 index 0000000000..4a412b3576 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.secrets.yaml new file mode 100644 index 0000000000..d1c4b32fd5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/jsonpatch.secrets.yaml @@ -0,0 +1,16 @@ +- name: secret-1 + tlsCertificate: + certificateChain: + inlineBytes: a2V5LWRhdGE= + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: secret-2 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: test_secret + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml new file mode 100644 index 0000000000..729a0304ab --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml @@ -0,0 +1,60 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + listenerFilters: + - name: envoy.filters.listener.proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-proxy-protocol.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml new file mode 100644 index 0000000000..a52251e32b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml @@ -0,0 +1,68 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-route-dest + lbPolicy: LEAST_REQUEST + name: tls-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml new file mode 100644 index 0000000000..5b4fe89e58 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: tls-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-route-dest/backend/0 +- clusterName: tcp-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml new file mode 100644 index 0000000000..d73eb92937 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml @@ -0,0 +1,137 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: second-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: second-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "7" + level: "6" + name: "6" + - description: socket option for keep alive idle time + intValue: "50" + level: "6" + name: "4" + - description: socket option for keep alive interval + intValue: "200" + level: "6" + name: "5" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10082 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - bar.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-route-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: third-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10083 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-dest + statPrefix: tcp + name: fourth-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "10" + level: "6" + name: "6" diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml new file mode 100644 index 0000000000..ff93cfff36 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml @@ -0,0 +1,28 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket +- ignorePortInHostMatching: true + name: second-listener + virtualHosts: + - domains: + - '*' + name: second-listener/* + routes: + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.clusters.yaml new file mode 100644 index 0000000000..fc755fed36 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.clusters.yaml @@ -0,0 +1,106 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: RANDOM + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: MAGLEV + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + leastRequestLbConfig: + slowStartConfig: + slowStartWindow: 60s + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: sixth-route-dest + name: sixth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + roundRobinLbConfig: + slowStartConfig: + slowStartWindow: 300s + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml new file mode 100644 index 0000000000..ee22b49d2b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.endpoints.yaml @@ -0,0 +1,72 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 +- clusterName: sixth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: sixth-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.routes.yaml new file mode 100644 index 0000000000..49db596538 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/load-balancer.routes.yaml @@ -0,0 +1,52 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: fourth-route + route: + cluster: fourth-route-dest + hashPolicy: + - connectionProperties: + sourceIp: true + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: fifth-route + route: + cluster: fifth-route-dest + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: sixth-route + route: + cluster: sixth-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml new file mode 100644 index 0000000000..41594a227d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/metrics-virtual-host.routes.yaml @@ -0,0 +1,20 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + virtualClusters: + - headers: + - name: :authority + stringMatch: + prefix: '*' + name: '*' diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml new file mode 100644 index 0000000000..7a0c933174 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.clusters.yaml @@ -0,0 +1,45 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} + name: localhost_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml new file mode 100644 index 0000000000..0d68b430c2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.endpoints.yaml @@ -0,0 +1,11 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml new file mode 100644 index 0000000000..f090a8d127 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml @@ -0,0 +1,67 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example: + audiences: + - foo.com + issuer: https://www.example.com + payloadInMetadata: https://www.example.com + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 5s + uri: https://localhost/jwt/public-key/jwks.json + retryPolicy: {} + requirementMap: + first-route: + providerName: first-route/example + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: first-listener + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml new file mode 100644 index 0000000000..c73bec0909 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml new file mode 100644 index 0000000000..9155e5c882 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.secrets.yaml @@ -0,0 +1,6 @@ +- name: first-listener + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.clusters.yaml new file mode 100755 index 0000000000..2b9b567cf3 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.clusters.yaml @@ -0,0 +1,104 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-1/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-1/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-2/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-2/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + lbPolicy: LEAST_REQUEST + name: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-3/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-3/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: oauth_foo_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.foo.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: oauth_foo_com_443/backend/0 + name: oauth_foo_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + sni: oauth.foo.com + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.endpoints.yaml new file mode 100755 index 0000000000..1f4fcb70da --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.endpoints.yaml @@ -0,0 +1,48 @@ +- clusterName: httproute/default/httproute-1/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 192.168.1.1 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-1/rule/0/backend/0 +- clusterName: httproute/default/httproute-2/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 192.168.1.2 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-2/rule/0/backend/0 +- clusterName: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 80 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend/backend/0 +- clusterName: httproute/default/httproute-3/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 192.168.1.3 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-3/rule/0/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml new file mode 100755 index 0000000000..910c6728e4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml @@ -0,0 +1,156 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + protocol: UDP + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codecType: HTTP3 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + http3ProtocolOptions: {} + httpFilters: + - disabled: true + name: envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_foo_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + httpService: + authorizationResponse: + allowedUpstreamHeaders: + patterns: + - exact: header1 + - exact: header2 + pathPrefix: /auth + serverUri: + cluster: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + timeout: 10s + uri: http://http-backend.envoy-gateway:80/auth + transportApiVersion: V3 + - disabled: true + name: envoy.filters.http.basic_auth_httproute/default/httproute-1/rule/0/match/0/www_foo_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth + users: + inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: default/gateway-1/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: default/gateway-1/http-quic + udpListenerConfig: + downstreamSocketConfig: {} + quicOptions: {} +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_foo_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz + httpService: + authorizationResponse: + allowedUpstreamHeaders: + patterns: + - exact: header1 + - exact: header2 + pathPrefix: /auth + serverUri: + cluster: securitypolicy/default/policy-for-http-route-2/envoy-gateway/http-backend + timeout: 10s + uri: http://http-backend.envoy-gateway:80/auth + transportApiVersion: V3 + - disabled: true + name: envoy.filters.http.basic_auth_httproute/default/httproute-1/rule/0/match/0/www_foo_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth + users: + inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo= + - disabled: true + name: envoy.filters.http.oauth2_httproute/default/httproute-3/rule/0/match/0/www_bar_com + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + credentials: + clientId: client.oauth.foo.com + cookieNames: + bearerToken: BearerToken-5F93C2E4 + idToken: IdToken-5F93C2E4 + oauthExpires: OauthExpires-5F93C2E4 + oauthHmac: OauthHMAC-5F93C2E4 + refreshToken: RefreshToken-5F93C2E4 + hmacSecret: + name: httproute/default/httproute-3/rule/0/match/0/www_bar_com/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: httproute/default/httproute-3/rule/0/match/0/www_bar_com/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /foo/oauth2/callback + redirectUri: https://www.example.com/foo/oauth2/callback + signoutPath: + path: + exact: /foo/logout + tokenEndpoint: + cluster: oauth_foo_com_443 + timeout: 10s + uri: https://oauth.foo.com/token + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: default/gateway-1/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: default/gateway-1/http + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.routes.yaml new file mode 100755 index 0000000000..1a4ec4068d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.routes.yaml @@ -0,0 +1,54 @@ +- ignorePortInHostMatching: true + name: default/gateway-1/http + virtualHosts: + - domains: + - www.foo.com + name: default/gateway-1/http/www_foo_com + routes: + - match: + pathSeparatedPrefix: /foo1 + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + responseHeadersToAdd: + - append: true + header: + key: alt-svc + value: h3=":443"; ma=86400 + route: + cluster: httproute/default/httproute-1/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.basic_auth_httproute/default/httproute-1/rule/0/match/0/www_foo_com: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - match: + pathSeparatedPrefix: /foo2 + name: httproute/default/httproute-2/rule/0/match/0/www_foo_com + responseHeadersToAdd: + - append: true + header: + key: alt-svc + value: h3=":443"; ma=86400 + route: + cluster: httproute/default/httproute-2/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_foo_com: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - domains: + - www.bar.com + name: default/gateway-2/http/www_bar_com + routes: + - match: + pathSeparatedPrefix: /bar + name: httproute/default/httproute-3/rule/0/match/0/www_bar_com + route: + cluster: httproute/default/httproute-3/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.oauth2_httproute/default/httproute-3/rule/0/match/0/www_bar_com: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml new file mode 100644 index 0000000000..ce7f4361a4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.clusters.yaml @@ -0,0 +1,102 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-route-dest + lbPolicy: LEAST_REQUEST + name: tls-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml new file mode 100644 index 0000000000..ceed007af6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.endpoints.yaml @@ -0,0 +1,72 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: tcp-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-dest/backend/0 +- clusterName: tls-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml new file mode 100644 index 0000000000..170017bc4d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml @@ -0,0 +1,138 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: third-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: first-listener + sdsConfig: + ads: {} + resourceApiVersion: V3 + - filterChainMatch: + serverNames: + - foo.net + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: second-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: second-listener + sdsConfig: + ads: {} + resourceApiVersion: V3 + - filterChainMatch: + serverNames: + - bar.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-dest + statPrefix: passthrough + - filterChainMatch: + serverNames: + - bar.net + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-route-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml new file mode 100644 index 0000000000..c94d2c09f7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.routes.yaml @@ -0,0 +1,53 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket +- ignorePortInHostMatching: true + name: second-listener + virtualHosts: + - domains: + - '*' + name: second-listener/* + routes: + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket +- ignorePortInHostMatching: true + name: third-listener + virtualHosts: + - domains: + - '*' + name: third-listener/* + routes: + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + - domains: + - '*' + name: fourth-listener/* + routes: + - match: + prefix: / + name: fourth-route + route: + cluster: fourth-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.secrets.yaml new file mode 100644 index 0000000000..47ebba7468 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-listeners-same-port.secrets.yaml @@ -0,0 +1,12 @@ +- name: first-listener + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: second-listener + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml new file mode 100644 index 0000000000..e0f57c2a69 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.clusters.yaml @@ -0,0 +1,85 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-1-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-1-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-2-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-2-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-3-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-3-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-4-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-4-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml new file mode 100644 index 0000000000..8d9b5f2277 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.endpoints.yaml @@ -0,0 +1,90 @@ +- clusterName: tcp-route-simple-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-dest/backend/0 +- clusterName: tcp-route-simple-1-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-1-dest/backend/0 +- clusterName: tcp-route-simple-2-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-2-dest/backend/0 +- clusterName: tcp-route-simple-3-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-3-dest/backend/0 +- clusterName: tcp-route-simple-4-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-4-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml new file mode 100644 index 0000000000..33e4073d89 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.listeners.yaml @@ -0,0 +1,38 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-dest + statPrefix: tcp + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-1-dest + statPrefix: tcp + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-2-dest + statPrefix: tcp + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-3-dest + statPrefix: tcp + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-4-dest + statPrefix: tcp + name: tcp-route-simple + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/multiple-simple-tcp-route-same-port.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml new file mode 100644 index 0000000000..80a48e25ba --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml @@ -0,0 +1,56 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + validationContextSdsSecretConfig: + name: ca-cert + sdsConfig: + ads: {} + resourceApiVersion: V3 + requireClientCertificate: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.secrets.yaml new file mode 100644 index 0000000000..052882baf5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/mutual-tls.secrets.yaml @@ -0,0 +1,16 @@ +- name: secret-1 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: secret-2 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: ca-cert + validationContext: + trustedCa: + inlineBytes: Y2VydC1kYXRh diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.clusters.yaml new file mode 100644 index 0000000000..5309331d01 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.clusters.yaml @@ -0,0 +1,106 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: oauth_foo_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.foo.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: oauth_foo_com_443/backend/0 + name: oauth_foo_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + sni: oauth.foo.com + type: STRICT_DNS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: oauth_bar_com_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.bar.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: oauth_bar_com_443/backend/0 + name: oauth_bar_com_443 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + sni: oauth.bar.com + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.endpoints.yaml new file mode 100644 index 0000000000..de95bf555b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.endpoints.yaml @@ -0,0 +1,24 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.listeners.yaml new file mode 100644 index 0000000000..4d4721c664 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.listeners.yaml @@ -0,0 +1,115 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.oauth2_first-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + credentials: + clientId: client.oauth.foo.com + cookieNames: + bearerToken: BearerToken-5F93C2E4 + idToken: IdToken-5F93C2E4 + oauthExpires: OauthExpires-5F93C2E4 + oauthHmac: OauthHMAC-5F93C2E4 + refreshToken: RefreshToken-5F93C2E4 + hmacSecret: + name: first-route/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: first-route/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /foo/oauth2/callback + redirectUri: https://www.example.com/foo/oauth2/callback + signoutPath: + path: + exact: /foo/logout + tokenEndpoint: + cluster: oauth_foo_com_443 + timeout: 10s + uri: https://oauth.foo.com/token + - disabled: true + name: envoy.filters.http.oauth2_second-route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + - email + - profile + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.bar.com/oauth2/v2/auth + credentials: + clientId: client.oauth.bar.com + cookieNames: + bearerToken: BearerToken-5f93c2e4 + idToken: IdToken-5f93c2e4 + oauthExpires: OauthExpires-5f93c2e4 + oauthHmac: OauthHMAC-5f93c2e4 + refreshToken: RefreshToken-5f93c2e4 + hmacSecret: + name: second-route/oauth2/hmac_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: second-route/oauth2/client_secret + sdsConfig: + ads: {} + resourceApiVersion: V3 + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /bar/oauth2/callback + redirectUri: https://www.example.com/bar/oauth2/callback + signoutPath: + path: + exact: /bar/logout + tokenEndpoint: + cluster: oauth_bar_com_443 + timeout: 10s + uri: https://oauth.bar.com/token + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.routes.yaml new file mode 100644 index 0000000000..a093d6967a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/oidc.routes.yaml @@ -0,0 +1,29 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.oauth2_first-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} + - match: + path: bar + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.oauth2_second-route: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.listeners.yaml new file mode 100644 index 0000000000..02167bb10c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.listeners.yaml @@ -0,0 +1,32 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/path-settings.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml new file mode 100644 index 0000000000..6441952eae --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.clusters.yaml @@ -0,0 +1,27 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + transportSocket: + name: envoy.transport_sockets.upstream_proxy_protocol + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport + config: + version: V2 + transportSocket: + name: envoy.transport_sockets.raw_buffer + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/proxy-protocol-upstream.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.routes.yaml new file mode 100644 index 0000000000..f2cacab526 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/retry-partial-invalid.routes.yaml @@ -0,0 +1,53 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + retryPolicy: + hostSelectionRetryMaxAttempts: "5" + numRetries: 5 + perTryTimeout: 0.250s + retriableStatusCodes: + - 429 + - 503 + retryBackOff: + baseInterval: 0.100s + maxInterval: 10s + retryHostPredicate: + - name: envoy.retry_host_predicates.previous_hosts + typedConfig: + '@type': type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate + retryOn: reset,connect-failure,retriable-status-codes + upgradeConfigs: + - upgradeType: websocket + - domains: + - foo + name: first-listener/foo + routes: + - match: + prefix: / + name: second-route-defaults + route: + cluster: first-route-dest + retryPolicy: + hostSelectionRetryMaxAttempts: "5" + numRetries: 2 + retriableStatusCodes: + - 503 + retryHostPredicate: + - name: envoy.retry_host_predicates.previous_hosts + typedConfig: + '@type': type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate + retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes + upgradeConfigs: + - upgradeType: websocket + - domains: + - bar + name: first-listener/bar diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.listeners.yaml new file mode 100644 index 0000000000..03bb654b47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.listeners.yaml @@ -0,0 +1,50 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.secrets.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.secrets.yaml new file mode 100644 index 0000000000..ad88ffe43c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/simple-tls.secrets.yaml @@ -0,0 +1,12 @@ +- name: secret-1 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= +- name: secret-2 + tlsCertificate: + certificateChain: + inlineBytes: Y2VydC1kYXRh + privateKey: + inlineBytes: a2V5LWRhdGE= diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml new file mode 100644 index 0000000000..d4e9eb3591 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml @@ -0,0 +1,56 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/suppress-envoy-headers.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml new file mode 100644 index 0000000000..382c2857a1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-complex-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-complex-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml new file mode 100644 index 0000000000..f1cd2f6a2d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.endpoints.yaml @@ -0,0 +1,18 @@ +- clusterName: tcp-route-complex-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-complex-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml new file mode 100644 index 0000000000..0c343d46e1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.listeners.yaml @@ -0,0 +1,23 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + - bar.com + - example.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-complex-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: tcp-route-complex + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-complex.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml new file mode 100644 index 0000000000..c845c64037 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-simple-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-simple-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml new file mode 100644 index 0000000000..7eb06a08f4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.endpoints.yaml @@ -0,0 +1,18 @@ +- clusterName: tcp-route-simple-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tcp-route-simple-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml new file mode 100644 index 0000000000..7fb5696df1 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.listeners.yaml @@ -0,0 +1,14 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-simple-dest + statPrefix: tcp + name: tcp-route-simple + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-simple.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml new file mode 100644 index 0000000000..af4d15a65a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-terminate-dest + lbPolicy: LEAST_REQUEST + name: tls-terminate-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml new file mode 100644 index 0000000000..164abc9de2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.endpoints.yaml @@ -0,0 +1,18 @@ +- clusterName: tls-terminate-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-terminate-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml new file mode 100644 index 0000000000..34bc646e4d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml @@ -0,0 +1,27 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-terminate-dest + statPrefix: terminate + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - h2 + - http/1.1 + tlsCertificateSdsSecretConfigs: + - name: envoy-gateway-tls-secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + name: tls-terminate + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-tls-terminate.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml new file mode 100644 index 0000000000..849359c138 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-weighted-backend-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-weighted-backend-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml new file mode 100644 index 0000000000..73db3a800a --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.endpoints.yaml @@ -0,0 +1,42 @@ +- clusterName: tcp-route-weighted-backend-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.1.1.1 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/0 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 2.2.2.2 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 40 + locality: + region: tcp-route-weighted-backend-dest/backend/1 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 3.3.3.3 + portValue: 50003 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/2 + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 4.4.4.4 + portValue: 50004 + loadBalancingWeight: 1 + loadBalancingWeight: 20 + locality: + region: tcp-route-weighted-backend-dest/backend/3 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml new file mode 100644 index 0000000000..74cd0effc0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.listeners.yaml @@ -0,0 +1,23 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + - bar.com + - example.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-weighted-backend-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: tcp-route-weighted-backend + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tcp-route-weighted-backend.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.clusters.yaml new file mode 100644 index 0000000000..e2156cb6af --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.clusters.yaml @@ -0,0 +1,25 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 31s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + commonHttpProtocolOptions: + idleTimeout: 32s + maxConnectionDuration: 33s + explicitHttpConfig: + httpProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/timeout.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml new file mode 100644 index 0000000000..f60942991d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.clusters.yaml @@ -0,0 +1,44 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-passthrough-foo-dest + lbPolicy: LEAST_REQUEST + name: tls-passthrough-foo-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: tls-passthrough-bar-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: bar + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-passthrough-bar-dest/backend/0 + name: tls-passthrough-bar-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml new file mode 100644 index 0000000000..ee66753c47 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.endpoints.yaml @@ -0,0 +1,18 @@ +- clusterName: tls-passthrough-foo-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tls-passthrough-foo-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml new file mode 100644 index 0000000000..a1c312fa0b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.listeners.yaml @@ -0,0 +1,42 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-passthrough-foo-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: tls-passthrough-foo + perConnectionBufferLimitBytes: 32768 +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - bar.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-passthrough-bar-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: tls-passthrough-bar + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-route-passthrough.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.clusters.yaml new file mode 100644 index 0000000000..d53a7a1b2c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml new file mode 100644 index 0000000000..36bc8d1ec4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml @@ -0,0 +1,77 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + drainType: MODIFY_ONLY + filterChains: + - filterChainMatch: + serverNames: + - foo.com + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: https + useRemoteAddress: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + alpnProtocols: + - some-other-protocol + tlsCertificateSdsSecretConfigs: + - name: secret-1 + sdsConfig: + ads: {} + resourceApiVersion: V3 + - name: secret-2 + sdsConfig: + ads: {} + resourceApiVersion: V3 + tlsParams: + cipherSuites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-ECDSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES256-GCM-SHA384 + ecdhCurves: + - X25519 + - P-256 + signatureAlgorithms: + - ecdsa_secp256r1_sha256 + - rsa_pss_rsae_sha256 + - rsa_pkcs1_sha256 + - ecdsa_secp384r1_sha384 + - rsa_pss_rsae_sha384 + - rsa_pkcs1_sha384 + - rsa_pss_rsae_sha512 + - rsa_pkcs1_sha512 + - rsa_pkcs1_sha1 + tlsMaximumProtocolVersion: TLSv1_2 + tlsMinimumProtocolVersion: TLSv1_0 + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.clusters.yaml new file mode 100644 index 0000000000..eea2936ce6 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.clusters.yaml @@ -0,0 +1,49 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.monitoring.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: tracing|otel-collector.monitoring.svc.cluster.local|4317/backend/0 + name: tracing|otel-collector.monitoring.svc.cluster.local|4317 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.endpoints.yaml new file mode 100644 index 0000000000..20c80b3aaa --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.listeners.yaml new file mode 100644 index 0000000000..d4737ef1fe --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.listeners.yaml @@ -0,0 +1,62 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + tracing: + clientSampling: + value: 100 + customTags: + - environment: + defaultValue: '-' + name: env1 + tag: env1 + - literal: + value: value1 + tag: literal1 + - requestHeader: + defaultValue: '-' + name: X-Request-Id + tag: req1 + overallSampling: + value: 100 + provider: + name: envoy.tracers.opentelemetry + typedConfig: + '@type': type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig + grpcService: + envoyGrpc: + authority: otel-collector.monitoring.svc.cluster.local + clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + serviceName: fake-name.fake-ns + randomSampling: + value: 90 + spawnUpstreamSpan: true + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.routes.yaml new file mode 100644 index 0000000000..d4a7fa5ae2 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/tracing.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - directResponse: + body: + inlineString: 'Unknown custom filter type: UnsupportedType' + status: 500 + match: + prefix: / + name: direct-route diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.clusters.yaml new file mode 100644 index 0000000000..0656b7c45e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: udp-route-dest + lbPolicy: LEAST_REQUEST + name: udp-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.endpoints.yaml new file mode 100644 index 0000000000..2e3c84e672 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.endpoints.yaml @@ -0,0 +1,18 @@ +- clusterName: udp-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50001 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: udp-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.listeners.yaml new file mode 100644 index 0000000000..317a7ddc4d --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.listeners.yaml @@ -0,0 +1,18 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + protocol: UDP + listenerFilters: + - name: envoy.filters.udp_listener.udp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.UdpProxyConfig + matcher: + onNoMatch: + action: + name: route + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route + cluster: udp-route-dest + statPrefix: service + name: udp-route diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.routes.yaml new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/udp-route.routes.yaml @@ -0,0 +1 @@ +[] diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.clusters.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.clusters.yaml new file mode 100644 index 0000000000..6d5dffadf8 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.clusters.yaml @@ -0,0 +1,22 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + upstreamConnectionOptions: + tcpKeepalive: + keepaliveInterval: 60 + keepaliveProbes: 3 + keepaliveTime: 1200 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.endpoints.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.listeners.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.listeners.yaml new file mode 100644 index 0000000000..ea50f4a05e --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.routes.yaml b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.routes.yaml new file mode 100644 index 0000000000..0b5b4bee7b --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/testdata/out/xds-ir/upstream-tcpkeepalive.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket diff --git a/adapter/internal/operator/gateway-api/translator/tracing.go b/adapter/internal/operator/gateway-api/translator/tracing.go new file mode 100644 index 0000000000..d4ff5b6bdc --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/tracing.go @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + "sort" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + tracecfg "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" + hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tracingtype "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" + xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" + "google.golang.org/protobuf/types/known/wrapperspb" + "k8s.io/utils/ptr" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + egv1a1 "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + "github.com/wso2/apk/adapter/internal/types" + "github.com/wso2/apk/adapter/pkg/utils/protocov" +) + +func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, error) { + if tracing == nil { + return nil, nil + } + + oc := &tracecfg.OpenTelemetryConfig{ + GrpcService: &corev3.GrpcService{ + TargetSpecifier: &corev3.GrpcService_EnvoyGrpc_{ + EnvoyGrpc: &corev3.GrpcService_EnvoyGrpc{ + ClusterName: buildClusterName("tracing", tracing.Provider.Host, uint32(tracing.Provider.Port)), + Authority: tracing.Provider.Host, + }, + }, + }, + ServiceName: tracing.ServiceName, + } + + ocAny, err := protocov.ToAnyWithError(oc) + if err != nil { + return nil, fmt.Errorf("failed to marshal OpenTelemetryConfig: %w", err) + } + + tags := []*tracingtype.CustomTag{} + // TODO: consider add some default tags for better UX + for k, v := range tracing.CustomTags { + switch v.Type { + case egv1a1.CustomTagTypeLiteral: + tags = append(tags, &tracingtype.CustomTag{ + Tag: k, + Type: &tracingtype.CustomTag_Literal_{ + Literal: &tracingtype.CustomTag_Literal{ + Value: v.Literal.Value, + }, + }, + }) + case egv1a1.CustomTagTypeEnvironment: + defaultVal := "" + if v.Environment.DefaultValue != nil { + defaultVal = *v.Environment.DefaultValue + } + + tags = append(tags, &tracingtype.CustomTag{ + Tag: k, + Type: &tracingtype.CustomTag_Environment_{ + Environment: &tracingtype.CustomTag_Environment{ + Name: v.Environment.Name, + DefaultValue: defaultVal, + }, + }, + }) + case egv1a1.CustomTagTypeRequestHeader: + defaultVal := "" + if v.RequestHeader.DefaultValue != nil { + defaultVal = *v.RequestHeader.DefaultValue + } + + tags = append(tags, &tracingtype.CustomTag{ + Tag: k, + Type: &tracingtype.CustomTag_RequestHeader{ + RequestHeader: &tracingtype.CustomTag_Header{ + Name: v.RequestHeader.Name, + DefaultValue: defaultVal, + }, + }, + }) + default: + return nil, fmt.Errorf("unknown custom tag type: %s", v.Type) + } + } + // sort tags by tag name, make result consistent + sort.Slice(tags, func(i, j int) bool { + return tags[i].Tag < tags[j].Tag + }) + + return &hcm.HttpConnectionManager_Tracing{ + ClientSampling: &xdstype.Percent{ + Value: 100.0, + }, + OverallSampling: &xdstype.Percent{ + Value: 100.0, + }, + RandomSampling: &xdstype.Percent{ + Value: float64(*tracing.SamplingRate), + }, + Provider: &tracecfg.Tracing_Http{ + Name: "envoy.tracers.opentelemetry", + ConfigType: &tracecfg.Tracing_Http_TypedConfig{ + TypedConfig: ocAny, + }, + }, + CustomTags: tags, + SpawnUpstreamSpan: wrapperspb.Bool(true), + }, nil +} + +func processClusterForTracing(tCtx *types.ResourceVersionTable, tracing *ir.Tracing) error { + if tracing == nil { + return nil + } + + clusterName := buildClusterName("tracing", tracing.Provider.Host, uint32(tracing.Provider.Port)) + + ds := &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(tracing.Provider.Host, uint32(tracing.Provider.Port))}, + } + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: clusterName, + settings: []*ir.DestinationSetting{ds}, + tSocket: nil, + endpointType: EndpointTypeDNS, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + return nil +} diff --git a/adapter/internal/operator/gateway-api/translator/translator.go b/adapter/internal/operator/gateway-api/translator/translator.go new file mode 100644 index 0000000000..eae45eb505 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/translator.go @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + "strings" + "time" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/types" + "github.com/wso2/apk/adapter/pkg/utils/protocov" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var ( + ErrXdsClusterExists = errors.New("xds cluster exists") + ErrXdsSecretExists = errors.New("xds secret exists") +) + +const AuthorityHeaderKey = ":authority" + +// Translator translates the xDS IR into xDS resources. +type Translator struct { + // GlobalRateLimit holds the global rate limit settings + // required during xds translation. + GlobalRateLimit *GlobalRateLimitSettings + + // ExtensionManager holds the config for interacting with extensions when generating xDS + // resources. Only required during xds translation. + // ExtensionManager *extensionTypes.Manager +} + +type GlobalRateLimitSettings struct { + // ServiceURL is the URL of the global + // rate limit service. + ServiceURL string + + // Timeout specifies the timeout period for the proxy to access the ratelimit server + // If not set, timeout is 20000000(20ms). + Timeout time.Duration + + // FailClosed is a switch used to control the flow of traffic + // when the response from the ratelimit server cannot be obtained. + FailClosed bool +} + +// Translate translates the XDS IR into xDS resources +func (t *Translator) Translate(ir *ir.Xds) (*types.ResourceVersionTable, error) { + if ir == nil { + return nil, errors.New("ir is nil") + } + + tCtx := new(types.ResourceVersionTable) + + // xDS translation is done in a best-effort manner, so we collect all errors + // and return them at the end. + // + // Reasoning: The validation in the CRD validation and API Gateway API + // translator should already catch most errors, there are just few rare cases + // where xDS translation can fail, for example, failed to call an extension + // hook or failed to patch an EnvoyPatchPolicy. In those cases, we don't want + // to fail the entire xDS translation to panic users, but instead, we want + // to collect all errors and reflect them in the status of the CRDs. + var errs error + if err := t.processHTTPListenerXdsTranslation( + tCtx, ir.HTTP, ir.AccessLog, ir.Tracing, ir.Metrics); err != nil { + errs = errors.Join(errs, err) + } + + if err := processTCPListenerXdsTranslation(tCtx, ir.TCP, ir.AccessLog); err != nil { + errs = errors.Join(errs, err) + } + + if err := processUDPListenerXdsTranslation(tCtx, ir.UDP, ir.AccessLog); err != nil { + errs = errors.Join(errs, err) + } + + if err := processClusterForAccessLog(tCtx, ir.AccessLog); err != nil { + errs = errors.Join(errs, err) + } + if err := processClusterForTracing(tCtx, ir.Tracing); err != nil { + errs = errors.Join(errs, err) + } + + // Check if an extension want to inject any clusters/secrets + // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op + // if err := processExtensionPostTranslationHook(tCtx, t.ExtensionManager); err != nil { + // errs = errors.Join(errs, err) + // } + + return tCtx, errs +} + +func (t *Translator) processHTTPListenerXdsTranslation( + tCtx *types.ResourceVersionTable, + httpListeners []*ir.HTTPListener, + accessLog *ir.AccessLog, + tracing *ir.Tracing, + metrics *ir.Metrics, +) error { + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error + for _, httpListener := range httpListeners { + addFilterChain := true + var xdsRouteCfg *routev3.RouteConfiguration + + // Search for an existing listener, if it does not exist, create one. + xdsListener := findXdsListenerByHostPort(tCtx, httpListener.Address, httpListener.Port, corev3.SocketAddress_TCP) + var quicXDSListener *listenerv3.Listener + enabledHTTP3 := httpListener.HTTP3 != nil + if xdsListener == nil { + xdsListener = buildXdsTCPListener(httpListener.Name, httpListener.Address, httpListener.Port, httpListener.TCPKeepalive, accessLog) + if enabledHTTP3 { + quicXDSListener = buildXdsQuicListener(httpListener.Name, httpListener.Address, httpListener.Port, accessLog) + if err := tCtx.AddXdsResource(resourcev3.ListenerType, quicXDSListener); err != nil { + return err + } + } + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the + // resource version table. Normally, this should not happen. + errs = errors.Join(errs, err) + continue + } + } else if httpListener.TLS == nil { + // Find the route config associated with this listener that + // maps to the default filter chain for http traffic + routeName := findXdsHTTPRouteConfigName(xdsListener) + if routeName != "" { + // If an existing listener exists, dont create a new filter chain + // for HTTP traffic, match on the Domains field within VirtualHosts + // within the same RouteConfiguration instead + addFilterChain = false + xdsRouteCfg = findXdsRouteConfig(tCtx, routeName) + if xdsRouteCfg == nil { + // skip this listener if failed to find xds route config + errs = errors.Join(errs, errors.New("unable to find xds route config")) + continue + } + } + } + + if addFilterChain { + if err := t.addXdsHTTPFilterChain(xdsListener, httpListener, accessLog, tracing, false); err != nil { + return err + } + if enabledHTTP3 { + if err := t.addXdsHTTPFilterChain(quicXDSListener, httpListener, accessLog, tracing, true); err != nil { + return err + } + } + } else { + // When the DefaultFilterChain is shared by multiple Gateway HTTP + // Listeners, we need to add the HTTP filters associated with the + // HTTPListener to the HCM if they have not yet been added. + if err := t.addHTTPFiltersToHCM(xdsListener.DefaultFilterChain, httpListener); err != nil { + errs = errors.Join(errs, err) + continue + } + if enabledHTTP3 { + if err := t.addHTTPFiltersToHCM(quicXDSListener.DefaultFilterChain, httpListener); err != nil { + errs = errors.Join(errs, err) + continue + } + } + } + + // Create a route config if we have not found one yet + if xdsRouteCfg == nil { + xdsRouteCfg = &routev3.RouteConfiguration{ + IgnorePortInHostMatching: true, + Name: httpListener.Name, + } + + if err := tCtx.AddXdsResource(resourcev3.RouteType, xdsRouteCfg); err != nil { + errs = errors.Join(errs, err) + } + } + + // 1:1 between IR TLSListenerConfig and xDS Secret + if httpListener.TLS != nil { + for t := range httpListener.TLS.Certificates { + secret := buildXdsTLSCertSecret(httpListener.TLS.Certificates[t]) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + errs = errors.Join(errs, err) + } + } + + if httpListener.TLS.CACertificate != nil { + caSecret := buildXdsTLSCaCertSecret(httpListener.TLS.CACertificate) + if err := tCtx.AddXdsResource(resourcev3.SecretType, caSecret); err != nil { + errs = errors.Join(errs, err) + } + } + } + + // store virtual hosts by domain + vHosts := map[string]*routev3.VirtualHost{} + // keep track of order by using a list as well as the map + var vHostsList []*routev3.VirtualHost + + // Check if an extension is loaded that wants to modify xDS Routes after they have been generated + for _, httpRoute := range httpListener.Routes { + // 1:1 between IR HTTPRoute Hostname and xDS VirtualHost. + vHost := vHosts[httpRoute.Hostname] + if vHost == nil { + // Remove dots from the hostname before appending it to the virtualHost name + // since dots are special chars used in stats tag extraction in Envoy + underscoredHostname := strings.ReplaceAll(httpRoute.Hostname, ".", "_") + // Allocate virtual host for this httpRoute. + vHost = &routev3.VirtualHost{ + Name: fmt.Sprintf("%s/%s", httpListener.Name, underscoredHostname), + Domains: []string{httpRoute.Hostname}, + } + if metrics != nil && metrics.EnableVirtualHostStats { + vHost.VirtualClusters = []*routev3.VirtualCluster{ + { + Name: underscoredHostname, + Headers: []*routev3.HeaderMatcher{ + { + Name: AuthorityHeaderKey, + HeaderMatchSpecifier: &routev3.HeaderMatcher_StringMatch{ + StringMatch: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Prefix{ + Prefix: httpRoute.Hostname, + }, + }, + }, + }, + }, + }, + } + } + vHosts[httpRoute.Hostname] = vHost + vHostsList = append(vHostsList, vHost) + } + + // 1:1 between IR HTTPRoute and xDS config.route.v3.Route + xdsRoute, err := buildXdsRoute(httpRoute) + if err != nil { + // skip this route if failed to build xds route + errs = errors.Join(errs, err) + continue + } + + // Check if an extension want to modify the route we just generated + // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op. + // if err = processExtensionPostRouteHook(xdsRoute, vHost, httpRoute, t.ExtensionManager); err != nil { + // if err != nil { + // errs = errors.Join(errs, err) + // } + // } + + if enabledHTTP3 { + http3AltSvcHeader := buildHTTP3AltSvcHeader(int(httpListener.HTTP3.QUICPort)) + if xdsRoute.ResponseHeadersToAdd == nil { + xdsRoute.ResponseHeadersToAdd = make([]*corev3.HeaderValueOption, 0) + } + xdsRoute.ResponseHeadersToAdd = append(xdsRoute.ResponseHeadersToAdd, http3AltSvcHeader) + } + vHost.Routes = append(vHost.Routes, xdsRoute) + + if httpRoute.Destination != nil { + if err = processXdsCluster(tCtx, httpRoute, httpListener.HTTP1); err != nil { + errs = errors.Join(errs, err) + } + } + + if httpRoute.Mirrors != nil { + for _, mirrorDest := range httpRoute.Mirrors { + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: mirrorDest.Name, + settings: mirrorDest.Settings, + tSocket: nil, + endpointType: EndpointTypeStatic, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } + } + } + + // for _, vHost := range vHostsList { + // // Check if an extension want to modify the Virtual Host we just generated + // // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op. + // if err := processExtensionPostVHostHook(vHost, t.ExtensionManager); err != nil { + // errs = errors.Join(errs, err) + // } + // } + xdsRouteCfg.VirtualHosts = append(xdsRouteCfg.VirtualHosts, vHostsList...) + + // Add all the other needed resources referenced by this filter to the + // resource version table. + if err := patchResources(tCtx, httpListener.Routes); err != nil { + return err + } + + // RateLimit filter is handled separately because it relies on the global + // rate limit server configuration. + // Check if a ratelimit cluster exists, if not, add it, if it's needed. + // if err := t.createRateLimitServiceCluster(tCtx, httpListener); err != nil { + // errs = errors.Join(errs, err) + // } + + // // Check if an extension want to modify the listener that was just configured/created + // // If no extension exists (or it doesn't subscribe to this hook) then this is a quick no-op + // if err := processExtensionPostListenerHook(tCtx, xdsListener, t.ExtensionManager); err != nil { + // errs = errors.Join(errs, err) + // } + } + + return errs +} + +func (t *Translator) addHTTPFiltersToHCM(filterChain *listenerv3.FilterChain, httpListener *ir.HTTPListener) error { + var ( + hcm *hcmv3.HttpConnectionManager + err error + ) + + if hcm, err = findHCMinFilterChain(filterChain); err != nil { + return err // should not happen + } + + // Add http filters to the HCM if they have not yet been added. + if err = t.patchHCMWithFilters(hcm, httpListener); err != nil { + return err + } + + for i, filter := range filterChain.Filters { + if filter.Name == wellknown.HTTPConnectionManager { + var mgrAny *anypb.Any + if mgrAny, err = protocov.ToAnyWithError(hcm); err != nil { + return err + } + + filterChain.Filters[i] = &listenerv3.Filter{ + Name: wellknown.HTTPConnectionManager, + ConfigType: &listenerv3.Filter_TypedConfig{ + TypedConfig: mgrAny, + }, + } + } + } + return nil +} + +func findHCMinFilterChain(filterChain *listenerv3.FilterChain) (*hcmv3.HttpConnectionManager, error) { + for _, filter := range filterChain.Filters { + if filter.Name == wellknown.HTTPConnectionManager { + hcm := &hcmv3.HttpConnectionManager{} + if err := anypb.UnmarshalTo(filter.GetTypedConfig(), hcm, proto.UnmarshalOptions{}); err != nil { + return nil, err + } + return hcm, nil + } + } + return nil, errors.New("http connection manager not found") +} + +func buildHTTP3AltSvcHeader(port int) *corev3.HeaderValueOption { + return &corev3.HeaderValueOption{ + Append: &wrapperspb.BoolValue{Value: true}, + Header: &corev3.HeaderValue{ + Key: "alt-svc", + Value: strings.Join([]string{fmt.Sprintf(`%s=":%d"; ma=86400`, "h3", port)}, ", "), + }, + } +} + +func processTCPListenerXdsTranslation(tCtx *types.ResourceVersionTable, tcpListeners []*ir.TCPListener, accesslog *ir.AccessLog) error { + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error + for _, tcpListener := range tcpListeners { + // Search for an existing listener, if it does not exist, create one. + xdsListener := findXdsListenerByHostPort(tCtx, tcpListener.Address, tcpListener.Port, corev3.SocketAddress_TCP) + if xdsListener == nil { + xdsListener = buildXdsTCPListener(tcpListener.Name, tcpListener.Address, tcpListener.Port, tcpListener.TCPKeepalive, accesslog) + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the + errs = errors.Join(errs, err) + continue + } + } + + if err := addXdsTCPFilterChain(xdsListener, tcpListener, tcpListener.Destination.Name, accesslog); err != nil { + errs = errors.Join(errs, err) + } + + // 1:1 between IR TCPListener and xDS Cluster + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: tcpListener.Destination.Name, + settings: tcpListener.Destination.Settings, + tSocket: nil, + endpointType: buildEndpointType(tcpListener.Destination.Settings), + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + + if tcpListener.TLS != nil && tcpListener.TLS.Terminate != nil { + for _, s := range tcpListener.TLS.Terminate.Certificates { + secret := buildXdsTLSCertSecret(s) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + errs = errors.Join(errs, err) + } + } + if tcpListener.TLS.Terminate.CACertificate != nil { + caSecret := buildXdsTLSCaCertSecret(tcpListener.TLS.Terminate.CACertificate) + if err := tCtx.AddXdsResource(resourcev3.SecretType, caSecret); err != nil { + errs = errors.Join(errs, err) + } + } + } + } + return errs +} + +func processUDPListenerXdsTranslation(tCtx *types.ResourceVersionTable, udpListeners []*ir.UDPListener, accesslog *ir.AccessLog) error { + // The XDS translation is done in a best-effort manner, so we collect all + // errors and return them at the end. + var errs error + + for _, udpListener := range udpListeners { + // There won't be multiple UDP listeners on the same port since it's already been checked at the gateway api + // translator + xdsListener, err := buildXdsUDPListener(udpListener.Destination.Name, udpListener, accesslog) + if err != nil { + // skip this listener if failed to build xds listener + errs = errors.Join(errs, err) + continue + } + if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { + // skip this listener if failed to add xds listener to the resource version table + errs = errors.Join(errs, err) + continue + } + + // 1:1 between IR UDPListener and xDS Cluster + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: udpListener.Destination.Name, + settings: udpListener.Destination.Settings, + tSocket: nil, + endpointType: buildEndpointType(udpListener.Destination.Settings), + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } + return errs +} + +// findXdsListenerByHostPort finds a xds listener with the same address, port and protocol, and returns nil if there is no match. +func findXdsListenerByHostPort(tCtx *types.ResourceVersionTable, address string, port uint32, + protocol corev3.SocketAddress_Protocol) *listenerv3.Listener { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.ListenerType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.ListenerType] { + listener := r.(*listenerv3.Listener) + addr := listener.GetAddress() + if addr.GetSocketAddress().GetPortValue() == port && addr.GetSocketAddress().Address == address && addr. + GetSocketAddress().Protocol == protocol { + return listener + } + } + + return nil +} + +// findXdsListener finds a xds listener with the same name and returns nil if there is no match. +func findXdsListener(tCtx *types.ResourceVersionTable, name string) *listenerv3.Listener { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.ListenerType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.ListenerType] { + listener := r.(*listenerv3.Listener) + if listener.Name == name { + return listener + } + } + + return nil +} + +// findXdsRouteConfig finds a xds route with the name and returns nil if there is no match. +func findXdsRouteConfig(tCtx *types.ResourceVersionTable, name string) *routev3.RouteConfiguration { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.RouteType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.RouteType] { + route := r.(*routev3.RouteConfiguration) + if route.Name == name { + return route + } + } + + return nil +} + +// findXdsCluster finds a xds cluster with the same name, and returns nil if there is no match. +func findXdsCluster(tCtx *types.ResourceVersionTable, name string) *clusterv3.Cluster { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.ClusterType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.ClusterType] { + cluster := r.(*clusterv3.Cluster) + if cluster.Name == name { + return cluster + } + } + + return nil +} + +// findXdsEndpoint finds a xds endpoint with the same cluster name, and returns nil if there is no match. +func findXdsEndpoint(tCtx *types.ResourceVersionTable, name string) *endpointv3.ClusterLoadAssignment { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.EndpointType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.EndpointType] { + endpoint := r.(*endpointv3.ClusterLoadAssignment) + if endpoint.ClusterName == name { + return endpoint + } + } + + return nil +} + +// processXdsCluster processes a xds cluster by its endpoint address type. +func processXdsCluster(tCtx *types.ResourceVersionTable, httpRoute *ir.HTTPRoute, http1Settings *ir.HTTP1Settings) error { + if err := addXdsCluster(tCtx, &xdsClusterArgs{ + name: httpRoute.Destination.Name, + settings: httpRoute.Destination.Settings, + tSocket: nil, + endpointType: buildEndpointType(httpRoute.Destination.Settings), + loadBalancer: httpRoute.LoadBalancer, + proxyProtocol: httpRoute.ProxyProtocol, + circuitBreaker: httpRoute.CircuitBreaker, + healthCheck: httpRoute.HealthCheck, + http1Settings: http1Settings, + timeout: httpRoute.Timeout, + tcpkeepalive: httpRoute.TCPKeepalive, + }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + + return nil +} + +// processTLSSocket generates a xDS TransportSocket for a given TLS config. +// It also adds the necessary secrets to the resource version table. +func processTLSSocket(tlsConfig *ir.TLSUpstreamConfig, tCtx *types.ResourceVersionTable) (*corev3.TransportSocket, error) { + if tlsConfig == nil { + return nil, nil + } + // Create a secret for the CA certificate only if it's not using the system trust store + if !tlsConfig.UseSystemTrustStore { + CaSecret := buildXdsUpstreamTLSCASecret(tlsConfig) + if err := tCtx.AddXdsResource(resourcev3.SecretType, CaSecret); err != nil { + return nil, err + } + } + + // for upstreamTLS , a fixed sni can be used. use auto_sni otherwise + // https://www.envoyproxy.io/docs/envoy/latest/faq/configuration/sni#faq-how-to-setup-sni:~:text=For%20clusters%2C%20a,for%20trust%20anchor. + tlsSocket, err := buildXdsUpstreamTLSSocketWthCert(tlsConfig) + if err != nil { + return nil, err + } + return tlsSocket, nil +} + +// findXdsSecret finds a xds secret with the same name, and returns nil if there is no match. +func findXdsSecret(tCtx *types.ResourceVersionTable, name string) *tlsv3.Secret { + if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.SecretType] == nil { + return nil + } + + for _, r := range tCtx.XdsResources[resourcev3.SecretType] { + secret := r.(*tlsv3.Secret) + if secret.Name == name { + return secret + } + } + + return nil +} + +func addXdsSecret(tCtx *types.ResourceVersionTable, secret *tlsv3.Secret) error { + // Return early if cluster with the same name exists + if c := findXdsSecret(tCtx, secret.Name); c != nil { + return ErrXdsSecretExists + } + + return tCtx.AddXdsResource(resourcev3.SecretType, secret) +} + +func addXdsCluster(tCtx *types.ResourceVersionTable, args *xdsClusterArgs) error { + // Return early if cluster with the same name exists + if c := findXdsCluster(tCtx, args.name); c != nil { + return ErrXdsClusterExists + } + + xdsCluster := buildXdsCluster(args) + xdsEndpoints := buildXdsClusterLoadAssignment(args.name, args.settings) + for _, ds := range args.settings { + if ds.TLS != nil { + // Create a secret for the CA certificate only if it's not using the system trust store + if !ds.TLS.UseSystemTrustStore { + secret := buildXdsUpstreamTLSCASecret(ds.TLS) + if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil { + return err + } + } + } + } + // Use EDS for static endpoints + if args.endpointType == EndpointTypeStatic { + if err := tCtx.AddXdsResource(resourcev3.EndpointType, xdsEndpoints); err != nil { + return err + } + } else { + xdsCluster.LoadAssignment = xdsEndpoints + } + return tCtx.AddXdsResource(resourcev3.ClusterType, xdsCluster) +} + +const ( + DefaultEndpointType EndpointType = iota + Static + EDS +) + +func buildXdsUpstreamTLSCASecret(tlsConfig *ir.TLSUpstreamConfig) *tlsv3.Secret { + // Build the tls secret + // It's just a sanity check, we shouldn't call this function if the system trust store is used + if tlsConfig.UseSystemTrustStore { + return nil + } + return &tlsv3.Secret{ + Name: tlsConfig.CACertificate.Name, + Type: &tlsv3.Secret_ValidationContext{ + ValidationContext: &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{InlineBytes: tlsConfig.CACertificate.Certificate}, + }, + }, + }, + } +} + +func buildXdsUpstreamTLSSocketWthCert(tlsConfig *ir.TLSUpstreamConfig) (*corev3.TransportSocket, error) { + + var tlsCtx *tlsv3.UpstreamTlsContext + + if tlsConfig.UseSystemTrustStore { + tlsCtx = &tlsv3.UpstreamTlsContext{ + CommonTlsContext: &tlsv3.CommonTlsContext{ + ValidationContextType: &tlsv3.CommonTlsContext_ValidationContext{ + ValidationContext: &tlsv3.CertificateValidationContext{ + TrustedCa: &corev3.DataSource{ + Specifier: &corev3.DataSource_Filename{ + // This is the default location for the system trust store + // on Debian derivatives like the envoy-proxy image being used by the infrastructure + // controller. + // See https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ssl + // TODO: allow customizing this value via EnvoyGateway so that if a non-standard + // envoy image is being used, this can be modified to match + Filename: "/etc/ssl/certs/ca-certificates.crt", + }, + }, + }, + }, + }, + Sni: tlsConfig.SNI, + } + } else { + tlsCtx = &tlsv3.UpstreamTlsContext{ + CommonTlsContext: &tlsv3.CommonTlsContext{ + TlsCertificateSdsSecretConfigs: nil, + ValidationContextType: &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{ + ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{ + Name: tlsConfig.CACertificate.Name, + SdsConfig: makeConfigSource(), + }, + }, + }, + Sni: tlsConfig.SNI, + } + } + + tlsCtxAny, err := anypb.New(tlsCtx) + if err != nil { + return nil, err + } + + return &corev3.TransportSocket{ + Name: wellknown.TransportSocketTLS, + ConfigType: &corev3.TransportSocket_TypedConfig{ + TypedConfig: tlsCtxAny, + }, + }, nil +} diff --git a/adapter/internal/operator/gateway-api/translator/translator_test.go b/adapter/internal/operator/gateway-api/translator/translator_test.go new file mode 100644 index 0000000000..2f47340b0c --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/translator_test.go @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "embed" + "flag" + "path/filepath" + "strings" + "testing" + + "github.com/envoyproxy/go-control-plane/pkg/cache/types" + resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/stretchr/testify/require" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/xds/utils" + "github.com/wso2/apk/adapter/pkg/utils/file" + "sigs.k8s.io/yaml" +) + +var ( + //go:embed testdata/out/* + outFiles embed.FS + //go:embed testdata/in/* + inFiles embed.FS + + overrideTestData = flag.Bool("override-testdata", false, "if override the test output data.") +) + +func TestTranslateXds(t *testing.T) { + testCases := []struct { + name string + dnsDomain string + requireSecrets bool + requireEnvoyPatchPolicies bool + }{ + { + name: "empty", + }, + { + name: "http-route", + }, + { + name: "http-route-regex", + }, + { + name: "http-route-redirect", + }, + { + name: "http-route-mirror", + }, + { + name: "http-route-multiple-mirrors", + }, + { + name: "http-route-multiple-matches", + }, + { + name: "http-route-direct-response", + }, + { + name: "http-route-request-headers", + }, + { + name: "http-route-response-add-headers", + }, + { + name: "http-route-response-remove-headers", + }, + { + name: "http-route-response-add-remove-headers", + }, + { + name: "http-route-weighted-invalid-backend", + }, + { + name: "http-route-dns-cluster", + }, + { + name: "http-route-with-tls-system-truststore", + requireSecrets: true, + }, + { + name: "http-route-with-tlsbundle", + requireSecrets: true, + }, + { + name: "http-route-with-tlsbundle-multiple-certs", + requireSecrets: true, + }, + { + name: "simple-tls", + requireSecrets: true, + }, + { + name: "mutual-tls", + requireSecrets: true, + }, + { + name: "http3", + requireSecrets: true, + }, + { + name: "tls-route-passthrough", + }, + { + name: "tcp-route-simple", + }, + { + name: "tcp-route-complex", + }, + { + name: "tcp-route-tls-terminate", + }, + { + name: "multiple-simple-tcp-route-same-port", + }, + { + name: "http-route-weighted-backend", + }, + { + name: "tcp-route-weighted-backend", + }, + { + name: "multiple-listeners-same-port", + requireSecrets: true, + }, + { + name: "udp-route", + }, + { + name: "http2-route", + }, + { + name: "http-route-rewrite-url-prefix", + }, + { + name: "http-route-rewrite-root-path-url-prefix", + }, + { + name: "http-route-rewrite-url-fullpath", + }, + { + name: "http-route-rewrite-url-host", + }, + { + name: "http-route-timeout", + }, + { + name: "accesslog", + }, + { + name: "tracing", + }, + { + name: "metrics-virtual-host", + }, + { + name: "jsonpatch-missing-resource", + requireEnvoyPatchPolicies: true, + }, + { + name: "jsonpatch-invalid-patch", + requireEnvoyPatchPolicies: true, + }, + { + name: "jsonpatch-add-op-without-value", + requireEnvoyPatchPolicies: true, + }, + { + name: "jsonpatch-move-op-with-value", + requireEnvoyPatchPolicies: true, + }, + { + name: "listener-tcp-keepalive", + }, + { + name: "load-balancer", + }, + { + name: "cors", + }, + { + name: "oidc", + }, + { + name: "http-route-partial-invalid", + }, + { + name: "listener-proxy-protocol", + }, + { + name: "proxy-protocol-upstream", + }, + { + name: "basic-auth", + }, + { + name: "health-check", + }, + { + name: "circuit-breaker", + }, + { + name: "suppress-envoy-headers", + }, + { + name: "fault-injection", + }, + { + name: "tls-with-ciphers-versions-alpn", + }, + { + name: "path-settings", + }, + { + name: "http1-trailers", + }, + { + name: "http1-preserve-case", + }, + { + name: "timeout", + }, + { + name: "ext-auth", + }, + { + name: "http10", + }, + { + name: "upstream-tcpkeepalive", + }, + { + name: "client-timeout", + }, + { + name: "retry-partial-invalid", + }, + { + name: "multiple-listeners-same-port-with-different-filters", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + dnsDomain := tc.dnsDomain + if dnsDomain == "" { + dnsDomain = "cluster.local" + } + ir := requireXdsIRFromInputTestData(t, "xds-ir", tc.name+".yaml") + tr := &Translator{ + // GlobalRateLimit: &GlobalRateLimitSettings{ + // ServiceURL: ratelimit.GetServiceURL("envoy-gateway-system", dnsDomain), + // }, + } + + tCtx, err := tr.Translate(ir) + if !strings.HasSuffix(tc.name, "partial-invalid") { + require.NoError(t, err) + } + + listeners := tCtx.XdsResources[resourcev3.ListenerType] + routes := tCtx.XdsResources[resourcev3.RouteType] + clusters := tCtx.XdsResources[resourcev3.ClusterType] + endpoints := tCtx.XdsResources[resourcev3.EndpointType] + if *overrideTestData { + require.NoError(t, file.Write(requireResourcesToYAMLString(t, listeners), filepath.Join("testdata", "out", "xds-ir", tc.name+".listeners.yaml"))) + require.NoError(t, file.Write(requireResourcesToYAMLString(t, routes), filepath.Join("testdata", "out", "xds-ir", tc.name+".routes.yaml"))) + require.NoError(t, file.Write(requireResourcesToYAMLString(t, clusters), filepath.Join("testdata", "out", "xds-ir", tc.name+".clusters.yaml"))) + require.NoError(t, file.Write(requireResourcesToYAMLString(t, endpoints), filepath.Join("testdata", "out", "xds-ir", tc.name+".endpoints.yaml"))) + } + require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".listeners.yaml"), requireResourcesToYAMLString(t, listeners)) + require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".routes.yaml"), requireResourcesToYAMLString(t, routes)) + require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".clusters.yaml"), requireResourcesToYAMLString(t, clusters)) + require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".endpoints.yaml"), requireResourcesToYAMLString(t, endpoints)) + if tc.requireSecrets { + secrets := tCtx.XdsResources[resourcev3.SecretType] + if *overrideTestData { + require.NoError(t, file.Write(requireResourcesToYAMLString(t, secrets), filepath.Join("testdata", "out", "xds-ir", tc.name+".secrets.yaml"))) + } + require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".secrets.yaml"), requireResourcesToYAMLString(t, secrets)) + } + }) + } +} + +func TestTranslateXdsNegative(t *testing.T) { + testCases := []struct { + name string + dnsDomain string + requireSecrets bool + }{ + { + name: "http-route-invalid", + }, + { + name: "tcp-route-invalid", + }, + { + name: "tcp-route-invalid-endpoint", + }, + { + name: "udp-route-invalid", + }, + { + name: "jsonpatch-invalid", + }, + { + name: "accesslog-invalid", + }, + { + name: "tracing-invalid", + }, + { + name: "jsonpatch-invalid-listener", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + dnsDomain := tc.dnsDomain + if dnsDomain == "" { + dnsDomain = "cluster.local" + } + ir := requireXdsIRFromInputTestData(t, "xds-ir", tc.name+".yaml") + tr := &Translator{ + // GlobalRateLimit: &GlobalRateLimitSettings{ + // ServiceURL: ratelimit.GetServiceURL("envoy-gateway-system", dnsDomain), + // }, + } + + _, err := tr.Translate(ir) + require.Error(t, err) + if tc.name != "jsonpatch-invalid" { + require.Contains(t, err.Error(), "validation failed for xds resource") + } + }) + } +} + +// func TestTranslateRateLimitConfig(t *testing.T) { +// testCases := []struct { +// name string +// }{ +// { +// name: "empty-header-matches", +// }, +// { +// name: "distinct-match", +// }, +// { +// name: "distinct-remote-address-match", +// }, +// { +// name: "value-match", +// }, +// { +// name: "multiple-matches", +// }, +// { +// name: "multiple-rules", +// }, +// { +// name: "multiple-routes", +// }, +// { +// name: "masked-remote-address-match", +// }, +// { +// name: "multiple-masked-remote-address-match-with-same-cidr", +// }, +// } + +// for _, tc := range testCases { +// tc := tc +// t.Run(tc.name, func(t *testing.T) { +// in := requireXdsIRListenerFromInputTestData(t, "ratelimit-config", tc.name+".yaml") +// out := BuildRateLimitServiceConfig(in) +// if *overrideTestData { +// require.NoError(t, file.Write(requireYamlRootToYAMLString(t, out), filepath.Join("testdata", "out", "ratelimit-config", tc.name+".yaml"))) +// } +// require.Equal(t, requireTestDataOutFile(t, "ratelimit-config", tc.name+".yaml"), requireYamlRootToYAMLString(t, out)) +// }) +// } +// } + +// func TestTranslateXdsWithExtension(t *testing.T) { +// testCases := []struct { +// name string +// requireSecrets bool +// err string +// }{ +// // Require secrets for all the tests since the extension for testing always injects one +// { +// name: "empty", +// requireSecrets: true, +// err: "", +// }, +// { +// name: "http-route", +// requireSecrets: true, +// err: "", +// }, +// { +// name: "http-route-extension-filter", +// requireSecrets: true, +// err: "", +// }, +// { +// name: "http-route-extension-route-error", +// requireSecrets: true, +// err: "route hook resource error", +// }, +// { +// name: "http-route-extension-virtualhost-error", +// requireSecrets: true, +// err: "extension post xds virtual host hook error", +// }, +// { +// name: "http-route-extension-listener-error", +// requireSecrets: true, +// err: "extension post xds listener hook error", +// }, +// } + +// for _, tc := range testCases { +// tc := tc +// t.Run(tc.name, func(t *testing.T) { +// // Testdata for the extension tests is similar to the ir test dat +// // New directory is just to keep them separate and easy to understand +// ir := requireXdsIRFromInputTestData(t, "extension-xds-ir", tc.name+".yaml") +// tr := &Translator{ +// // GlobalRateLimit: &GlobalRateLimitSettings{ +// // ServiceURL: ratelimit.GetServiceURL("envoy-gateway-system", "cluster.local"), +// // }, +// } +// // ext := v1alpha1.ExtensionManager{ +// // Resources: []v1alpha1.GroupVersionKind{ +// // { +// // Group: "foo.example.io", +// // Version: "v1alpha1", +// // Kind: "examplefilter", +// // }, +// // }, +// // Hooks: &v1alpha1.ExtensionHooks{ +// // XDSTranslator: &v1alpha1.XDSTranslatorHooks{ +// // Post: []v1alpha1.XDSTranslatorHook{ +// // v1alpha1.XDSRoute, +// // v1alpha1.XDSVirtualHost, +// // v1alpha1.XDSHTTPListener, +// // v1alpha1.XDSTranslation, +// // }, +// // }, +// // }, +// // } +// // extMgr := testutils.NewManager(ext) +// // tr.ExtensionManager = &extMgr + +// tCtx, err := tr.Translate(ir) + +// if tc.err != "" { +// require.EqualError(t, err, tc.err) +// } else { +// require.NoError(t, err) +// listeners := tCtx.XdsResources[resourcev3.ListenerType] +// routes := tCtx.XdsResources[resourcev3.RouteType] +// clusters := tCtx.XdsResources[resourcev3.ClusterType] +// endpoints := tCtx.XdsResources[resourcev3.EndpointType] +// if *overrideTestData { +// require.NoError(t, file.Write(requireResourcesToYAMLString(t, listeners), filepath.Join("testdata", "out", "extension-xds-ir", tc.name+".listeners.yaml"))) +// require.NoError(t, file.Write(requireResourcesToYAMLString(t, routes), filepath.Join("testdata", "out", "extension-xds-ir", tc.name+".routes.yaml"))) +// require.NoError(t, file.Write(requireResourcesToYAMLString(t, clusters), filepath.Join("testdata", "out", "extension-xds-ir", tc.name+".clusters.yaml"))) +// require.NoError(t, file.Write(requireResourcesToYAMLString(t, endpoints), filepath.Join("testdata", "out", "extension-xds-ir", tc.name+".endpoints.yaml"))) +// } +// require.Equal(t, requireTestDataOutFile(t, "extension-xds-ir", tc.name+".listeners.yaml"), requireResourcesToYAMLString(t, listeners)) +// require.Equal(t, requireTestDataOutFile(t, "extension-xds-ir", tc.name+".routes.yaml"), requireResourcesToYAMLString(t, routes)) +// require.Equal(t, requireTestDataOutFile(t, "extension-xds-ir", tc.name+".clusters.yaml"), requireResourcesToYAMLString(t, clusters)) +// require.Equal(t, requireTestDataOutFile(t, "extension-xds-ir", tc.name+".endpoints.yaml"), requireResourcesToYAMLString(t, endpoints)) +// if tc.requireSecrets { +// secrets := tCtx.XdsResources[resourcev3.SecretType] +// if *overrideTestData { +// require.NoError(t, file.Write(requireResourcesToYAMLString(t, secrets), filepath.Join("testdata", "out", "extension-xds-ir", tc.name+".secrets.yaml"))) +// } +// require.Equal(t, requireTestDataOutFile(t, "extension-xds-ir", tc.name+".secrets.yaml"), requireResourcesToYAMLString(t, secrets)) +// } +// } +// }) +// } +// } + +func requireXdsIRFromInputTestData(t *testing.T, name ...string) *ir.Xds { + t.Helper() + elems := append([]string{"testdata", "in"}, name...) + content, err := inFiles.ReadFile(filepath.Join(elems...)) + require.NoError(t, err) + ir := &ir.Xds{} + err = yaml.Unmarshal(content, ir) + require.NoError(t, err) + return ir +} + +func requireXdsIRListenerFromInputTestData(t *testing.T, name ...string) *ir.HTTPListener { + t.Helper() + elems := append([]string{"testdata", "in"}, name...) + content, err := inFiles.ReadFile(filepath.Join(elems...)) + require.NoError(t, err) + listener := &ir.HTTPListener{} + err = yaml.Unmarshal(content, listener) + require.NoError(t, err) + return listener +} + +func requireTestDataOutFile(t *testing.T, name ...string) string { + t.Helper() + elems := append([]string{"testdata", "out"}, name...) + content, err := outFiles.ReadFile(filepath.Join(elems...)) + require.NoError(t, err) + return string(content) +} + +// func requireYamlRootToYAMLString(t *testing.T, pbRoot *ratelimitv3.RateLimitConfig) string { +// str, err := GetRateLimitServiceConfigStr(pbRoot) +// require.NoError(t, err) +// return str +// } + +func requireResourcesToYAMLString(t *testing.T, resources []types.Resource) string { + jsonBytes, err := utils.MarshalResourcesToJSON(resources) + require.NoError(t, err) + data, err := yaml.JSONToYAML(jsonBytes) + require.NoError(t, err) + return string(data) +} diff --git a/adapter/internal/operator/gateway-api/translator/utils.go b/adapter/internal/operator/gateway-api/translator/utils.go new file mode 100644 index 0000000000..90f1d38820 --- /dev/null +++ b/adapter/internal/operator/gateway-api/translator/utils.go @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package translator + +import ( + "errors" + "fmt" + "net/netip" + "net/url" + "strconv" + "strings" + + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + defaultHTTPSPort uint64 = 443 + defaultHTTPPort uint64 = 80 + defaultExtServiceRequestTimeout = 10 // 10 seconds +) + +// urlCluster is a cluster that is created from a URL. +type urlCluster struct { + name string + hostname string + port uint32 + endpointType EndpointType + tls bool +} + +// url2Cluster returns a urlCluster from the provided url. +func url2Cluster(strURL string) (*urlCluster, error) { + epType := EndpointTypeDNS + + // The URL should have already been validated in the gateway API translator. + u, err := url.Parse(strURL) + if err != nil { + return nil, err + } + + var port uint64 + if u.Scheme == "https" { + port = defaultHTTPSPort + } else { + port = defaultHTTPPort + } + + if u.Port() != "" { + port, err = strconv.ParseUint(u.Port(), 10, 32) + if err != nil { + return nil, err + } + } + + name := clusterName(u.Hostname(), uint32(port)) + + if ip, err := netip.ParseAddr(u.Hostname()); err == nil { + if ip.Unmap().Is4() { + epType = EndpointTypeStatic + } + } + + return &urlCluster{ + name: name, + hostname: u.Hostname(), + port: uint32(port), + endpointType: epType, + tls: u.Scheme == "https", + }, nil +} + +func clusterName(host string, port uint32) string { + return fmt.Sprintf("%s_%d", strings.ReplaceAll(host, ".", "_"), port) +} + +// enableFilterOnRoute enables a filterType on the provided route. +func enableFilterOnRoute(filterType string, route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + + filterName := perRouteFilterName(filterType, irRoute.Name) + filterCfg := route.GetTypedPerFilterConfig() + if _, ok := filterCfg[filterName]; ok { + // This should not happen since this is the only place where the filter + // config is added in a route. + return fmt.Errorf("route already contains filter config: %s, %+v", + filterType, route) + } + + // Enable the corresponding filter for this route. + routeCfgAny, err := anypb.New(&routev3.FilterConfig{ + Config: &anypb.Any{}, + }) + if err != nil { + return err + } + + if filterCfg == nil { + route.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + + route.TypedPerFilterConfig[filterName] = routeCfgAny + + return nil +} + +func perRouteFilterName(filterType, routeName string) string { + return fmt.Sprintf("%s_%s", filterType, routeName) +} diff --git a/adapter/internal/operator/gateway-api/types/tls_types.go b/adapter/internal/operator/gateway-api/types/tls_types.go new file mode 100644 index 0000000000..cc891cab3e --- /dev/null +++ b/adapter/internal/operator/gateway-api/types/tls_types.go @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package types + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// +kubebuilder:validation:XValidation:rule="has(self.minVersion) && self.minVersion == '1.3' ? !has(self.ciphers) : true", message="setting ciphers has no effect if the minimum possible TLS version is 1.3" +// +kubebuilder:validation:XValidation:rule="has(self.minVersion) && has(self.maxVersion) ? {\"Auto\":0,\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4}[self.minVersion] <= {\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4,\"Auto\":5}[self.maxVersion] : !has(self.minVersion) && has(self.maxVersion) ? 3 <= {\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4,\"Auto\":5}[self.maxVersion] : true", message="minVersion must be smaller or equal to maxVersion" +type TLSSettings struct { + + // Min specifies the minimal TLS protocol version to allow. + // The default is TLS 1.2 if this is not specified. + // + // +optional + MinVersion *TLSVersion `json:"minVersion,omitempty"` + + // Max specifies the maximal TLS protocol version to allow + // The default is TLS 1.3 if this is not specified. + // + // +optional + MaxVersion *TLSVersion `json:"maxVersion,omitempty"` + + // Ciphers specifies the set of cipher suites supported when + // negotiating TLS 1.0 - 1.2. This setting has no effect for TLS 1.3. + // In non-FIPS Envoy Proxy builds the default cipher list is: + // - [ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305] + // - [ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305] + // - ECDHE-ECDSA-AES256-GCM-SHA384 + // - ECDHE-RSA-AES256-GCM-SHA384 + // In builds using BoringSSL FIPS the default cipher list is: + // - ECDHE-ECDSA-AES128-GCM-SHA256 + // - ECDHE-RSA-AES128-GCM-SHA256 + // - ECDHE-ECDSA-AES256-GCM-SHA384 + // - ECDHE-RSA-AES256-GCM-SHA384 + // + // +optional + Ciphers []string `json:"ciphers,omitempty"` + + // ECDHCurves specifies the set of supported ECDH curves. + // In non-FIPS Envoy Proxy builds the default curves are: + // - X25519 + // - P-256 + // In builds using BoringSSL FIPS the default curve is: + // - P-256 + // + // +optional + ECDHCurves []string `json:"ecdhCurves,omitempty"` + + // SignatureAlgorithms specifies which signature algorithms the listener should + // support. + // + // +optional + SignatureAlgorithms []string `json:"signatureAlgorithms,omitempty"` + + // ALPNProtocols supplies the list of ALPN protocols that should be + // exposed by the listener. By default h2 and http/1.1 are enabled. + // Supported values are: + // - http/1.0 + // - http/1.1 + // - h2 + // + // +optional + ALPNProtocols []ALPNProtocol `json:"alpnProtocols,omitempty"` + + // ClientValidation specifies the configuration to validate the client + // initiating the TLS connection to the Gateway listener. + // +optional + ClientValidation *ClientValidationContext `json:"clientValidation,omitempty"` +} + +// ALPNProtocol specifies the protocol to be negotiated using ALPN +// +kubebuilder:validation:Enum=http/1.0;http/1.1;h2 +type ALPNProtocol string + +// When adding ALPN constants, they must be values that are defined +// in the IANA registry for ALPN identification sequences +// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids +const ( + // HTTPProtocolVersion1_0 specifies that HTTP/1.0 should be negotiable with ALPN + HTTPProtocolVersion1_0 ALPNProtocol = "http/1.0" + // HTTPProtocolVersion1_1 specifies that HTTP/1.1 should be negotiable with ALPN + HTTPProtocolVersion1_1 ALPNProtocol = "http/1.1" + // HTTPProtocolVersion2 specifies that HTTP/2 should be negotiable with ALPN + HTTPProtocolVersion2 ALPNProtocol = "h2" +) + +// TLSVersion specifies the TLS version +// +kubebuilder:validation:Enum=Auto;"1.0";"1.1";"1.2";"1.3" +type TLSVersion string + +const ( + // TLSAuto allows Envoy to choose the optimal TLS Version + TLSAuto TLSVersion = "Auto" + // TLS1.0 specifies TLS version 1.0 + TLSv10 TLSVersion = "1.0" + // TLS1.1 specifies TLS version 1.1 + TLSv11 TLSVersion = "1.1" + // TLSv1.2 specifies TLS version 1.2 + TLSv12 TLSVersion = "1.2" + // TLSv1.3 specifies TLS version 1.3 + TLSv13 TLSVersion = "1.3" +) + +// ClientValidationContext holds configuration that can be used to validate the client initiating the TLS connection +// to the Gateway. +// By default, no client specific configuration is validated. +type ClientValidationContext struct { + // CACertificateRefs contains one or more references to + // Kubernetes objects that contain TLS certificates of + // the Certificate Authorities that can be used + // as a trust anchor to validate the certificates presented by the client. + // + // A single reference to a Kubernetes ConfigMap or a Kubernetes Secret, + // with the CA certificate in a key named `ca.crt` is currently supported. + // + // References to a resource in different namespace are invalid UNLESS there + // is a ReferenceGrant in the target namespace that allows the certificate + // to be attached. + // + // +kubebuilder:validation:MaxItems=8 + // +optional + CACertificateRefs []gwapiv1.SecretObjectReference `json:"caCertificateRefs,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/accesslogging_types.go b/adapter/internal/operator/gateway-api/v1alpha1/accesslogging_types.go new file mode 100644 index 0000000000..0e72150a52 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/accesslogging_types.go @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +// +k8s:deepcopy-gen=true +type ProxyAccessLog struct { + // Disable disables access logging for managed proxies if set to true. + Disable bool `json:"disable,omitempty"` + // Settings defines accesslog settings for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + Settings []ProxyAccessLogSetting `json:"settings,omitempty"` +} + +// +k8s:deepcopy-gen=true +type ProxyAccessLogSetting struct { + // Format defines the format of accesslog. + Format ProxyAccessLogFormat `json:"format"` + // Sinks defines the sinks of accesslog. + // +kubebuilder:validation:MinItems=1 + Sinks []ProxyAccessLogSink `json:"sinks"` +} + +// +k8s:deepcopy-gen=true +type ProxyAccessLogFormatType string + +const ( + // ProxyAccessLogFormatTypeText defines the text accesslog format. + ProxyAccessLogFormatTypeText ProxyAccessLogFormatType = "Text" + // ProxyAccessLogFormatTypeJSON defines the JSON accesslog format. + ProxyAccessLogFormatTypeJSON ProxyAccessLogFormatType = "JSON" + // TODO: support format type "mix" in the future. +) + +// +k8s:deepcopy-gen=true +// ProxyAccessLogFormat defines the format of accesslog. +// By default accesslogs are written to standard output. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'Text' ? has(self.text) : !has(self.text)",message="If AccessLogFormat type is Text, text field needs to be set." +// +kubebuilder:validation:XValidation:rule="self.type == 'JSON' ? has(self.json) : !has(self.json)",message="If AccessLogFormat type is JSON, json field needs to be set." +type ProxyAccessLogFormat struct { + // Type defines the type of accesslog format. + // +kubebuilder:validation:Enum=Text;JSON + // +unionDiscriminator + Type ProxyAccessLogFormatType `json:"type,omitempty"` + // Text defines the text accesslog format, following Envoy accesslog formatting, + // It's required when the format type is "Text". + // Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) may be used in the format. + // The [format string documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-format-strings) provides more information. + // +optional + Text *string `json:"text,omitempty"` + // JSON is additional attributes that describe the specific event occurrence. + // Structured format for the envoy access logs. Envoy [command operators](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#command-operators) + // can be used as values for fields within the Struct. + // It's required when the format type is "JSON". + // +optional + JSON map[string]string `json:"json,omitempty"` +} + +// +k8s:deepcopy-gen=true +type ProxyAccessLogSinkType string + +const ( + // ProxyAccessLogSinkTypeFile defines the file accesslog sink. + ProxyAccessLogSinkTypeFile ProxyAccessLogSinkType = "File" + // ProxyAccessLogSinkTypeOpenTelemetry defines the OpenTelemetry accesslog sink. + // When the provider is Kubernetes, EnvoyGateway always sends `k8s.namespace.name` + // and `k8s.pod.name` as additional attributes. + ProxyAccessLogSinkTypeOpenTelemetry ProxyAccessLogSinkType = "OpenTelemetry" +) + +// +k8s:deepcopy-gen=true +// ProxyAccessLogSink defines the sink of accesslog. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'File' ? has(self.file) : !has(self.file)",message="If AccessLogSink type is File, file field needs to be set." +// +kubebuilder:validation:XValidation:rule="self.type == 'OpenTelemetry' ? has(self.openTelemetry) : !has(self.openTelemetry)",message="If AccessLogSink type is OpenTelemetry, openTelemetry field needs to be set." +type ProxyAccessLogSink struct { + // Type defines the type of accesslog sink. + // +kubebuilder:validation:Enum=File;OpenTelemetry + // +unionDiscriminator + Type ProxyAccessLogSinkType `json:"type,omitempty"` + // File defines the file accesslog sink. + // +optional + File *FileEnvoyProxyAccessLog `json:"file,omitempty"` + // OpenTelemetry defines the OpenTelemetry accesslog sink. + // +optional + OpenTelemetry *OpenTelemetryEnvoyProxyAccessLog `json:"openTelemetry,omitempty"` +} + +// +k8s:deepcopy-gen=true +type FileEnvoyProxyAccessLog struct { + // Path defines the file path used to expose envoy access log(e.g. /dev/stdout). + // +kubebuilder:validation:MinLength=1 + Path string `json:"path,omitempty"` +} + +// +k8s:deepcopy-gen=true +// TODO: consider reuse ExtensionService? +type OpenTelemetryEnvoyProxyAccessLog struct { + // Host define the extension service hostname. + Host string `json:"host"` + // Port defines the port the extension service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + // Resources is a set of labels that describe the source of a log entry, including envoy node info. + // It's recommended to follow [semantic conventions](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/). + // +optional + Resources map[string]string `json:"resources,omitempty"` + + // TODO: support more OpenTelemetry accesslog options(e.g. TLS, auth etc.) in the future. +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_helpers.go b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_helpers.go new file mode 100644 index 0000000000..666f39b999 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_helpers.go @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + "fmt" + "sort" + "strings" + + autoscalingv2 "k8s.io/api/autoscaling/v2" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/utils/ptr" +) + +// DefaultEnvoyProxyProvider returns a new EnvoyProxyProvider with default settings. +func DefaultEnvoyProxyProvider() *EnvoyProxyProvider { + return &EnvoyProxyProvider{ + Type: ProviderTypeKubernetes, + } +} + +// GetEnvoyProxyProvider returns the EnvoyProxyProvider of EnvoyProxy or a default EnvoyProxyProvider +// if unspecified. +func (e *EnvoyProxy) GetEnvoyProxyProvider() *EnvoyProxyProvider { + if e.Spec.Provider != nil { + return e.Spec.Provider + } + e.Spec.Provider = DefaultEnvoyProxyProvider() + + return e.Spec.Provider +} + +// DefaultEnvoyProxyKubeProvider returns a new EnvoyProxyKubernetesProvider with default settings. +func DefaultEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { + return &EnvoyProxyKubernetesProvider{ + EnvoyDeployment: DefaultKubernetesDeployment(DefaultEnvoyProxyImage, DefaultEnforcerImage), + EnvoyService: DefaultKubernetesService(), + } +} + +func DefaultEnvoyProxyHpaMetrics() []autoscalingv2.MetricSpec { + return []autoscalingv2.MetricSpec{ + { + Resource: &autoscalingv2.ResourceMetricSource{ + Name: v1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: ptr.To[int32](80), + }, + }, + Type: autoscalingv2.ResourceMetricSourceType, + }, + } +} + +// GetEnvoyProxyKubeProvider returns the EnvoyProxyKubernetesProvider of EnvoyProxyProvider or +// a default EnvoyProxyKubernetesProvider if unspecified. If EnvoyProxyProvider is not of +// type "Kubernetes", a nil EnvoyProxyKubernetesProvider is returned. +func (r *EnvoyProxyProvider) GetEnvoyProxyKubeProvider() *EnvoyProxyKubernetesProvider { + if r.Type != ProviderTypeKubernetes { + return nil + } + + if r.Kubernetes == nil { + r.Kubernetes = DefaultEnvoyProxyKubeProvider() + return r.Kubernetes + } + + if r.Kubernetes.EnvoyDeployment == nil { + r.Kubernetes.EnvoyDeployment = DefaultKubernetesDeployment(DefaultEnvoyProxyImage, DefaultEnforcerImage) + } + + r.Kubernetes.EnvoyDeployment.defaultKubernetesDeploymentSpec(DefaultEnvoyProxyImage, DefaultEnforcerImage) + + if r.Kubernetes.EnvoyService == nil { + r.Kubernetes.EnvoyService = DefaultKubernetesService() + } + + if r.Kubernetes.EnvoyService.Type == nil { + r.Kubernetes.EnvoyService.Type = GetKubernetesServiceType(ServiceTypeLoadBalancer) + } + + if r.Kubernetes.EnvoyHpa != nil { + r.Kubernetes.EnvoyHpa.setDefault() + } + + return r.Kubernetes +} + +// DefaultEnvoyProxyLoggingLevel returns envoy proxy v1alpha1.LogComponentGatewayDefault log level. +// If unspecified, defaults to "warn". When specified, all other logging components are ignored. +func (logging *ProxyLogging) DefaultEnvoyProxyLoggingLevel() LogLevel { + if logging.Level[LogComponentDefault] != "" { + return logging.Level[LogComponentDefault] + } + + return LogLevelWarn +} + +// GetEnvoyProxyComponentLevel returns envoy proxy component log level args. +// xref: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-component-log-level +func (logging *ProxyLogging) GetEnvoyProxyComponentLevel() string { + var args []string + + for component, level := range logging.Level { + if component == LogComponentDefault { + // Skip default component + continue + } + + if level != "" { + args = append(args, fmt.Sprintf("%s:%s", component, level)) + } + } + + sort.Strings(args) + + return strings.Join(args, ",") +} + +// DefaultShutdownManagerContainerResourceRequirements returns a new ResourceRequirements with default settings. +func DefaultShutdownManagerContainerResourceRequirements() *v1.ResourceRequirements { + return &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse(DefaultShutdownManagerCPUResourceRequests), + v1.ResourceMemory: resource.MustParse(DefaultShutdownManagerMemoryResourceRequests), + }, + } +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_metric_types.go b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_metric_types.go new file mode 100644 index 0000000000..a448b782a5 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_metric_types.go @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +type MetricSinkType string + +const ( + MetricSinkTypeOpenTelemetry MetricSinkType = "OpenTelemetry" +) + +// +k8s:deepcopy-gen=true +type ProxyMetrics struct { + // Prometheus defines the configuration for Admin endpoint `/stats/prometheus`. + Prometheus *ProxyPrometheusProvider `json:"prometheus,omitempty"` + // Sinks defines the metric sinks where metrics are sent to. + Sinks []ProxyMetricSink `json:"sinks,omitempty"` + // Matches defines configuration for selecting specific metrics instead of generating all metrics stats + // that are enabled by default. This helps reduce CPU and memory overhead in Envoy, but eliminating some stats + // may after critical functionality. Here are the stats that we strongly recommend not disabling: + // `cluster_manager.warming_clusters`, `cluster..membership_total`,`cluster..membership_healthy`, + // `cluster..membership_degraded`,reference https://github.com/envoyproxy/envoy/issues/9856, + // https://github.com/envoyproxy/envoy/issues/14610 + // + Matches []StringMatch `json:"matches,omitempty"` + + // EnableVirtualHostStats enables envoy stat metrics for virtual hosts. + EnableVirtualHostStats bool `json:"enableVirtualHostStats,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ProxyMetricSink defines the sink of metrics. +// Default metrics sink is OpenTelemetry. +// +union +// +// +kubebuilder:validation:XValidation:rule="self.type == 'OpenTelemetry' ? has(self.openTelemetry) : !has(self.openTelemetry)",message="If MetricSink type is OpenTelemetry, openTelemetry field needs to be set." +type ProxyMetricSink struct { + // Type defines the metric sink type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + // +unionDiscriminator + Type MetricSinkType `json:"type"` + // OpenTelemetry defines the configuration for OpenTelemetry sink. + // It's required if the sink type is OpenTelemetry. + // +optional + OpenTelemetry *ProxyOpenTelemetrySink `json:"openTelemetry,omitempty"` +} + +// +k8s:deepcopy-gen=true +type ProxyOpenTelemetrySink struct { + // Host define the service hostname. + Host string `json:"host"` + // Port defines the port the service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` + + // TODO: add support for customizing OpenTelemetry sink in https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/stat_sinks/open_telemetry/v3/open_telemetry.proto#envoy-v3-api-msg-extensions-stat-sinks-open-telemetry-v3-sinkconfig +} + +// +k8s:deepcopy-gen=true +type ProxyPrometheusProvider struct { + // Disable the Prometheus endpoint. + Disable bool `json:"disable,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_types.go b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_types.go new file mode 100644 index 0000000000..b0b36db1ab --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/envoyproxy_types.go @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // KindEnvoyProxy is the name of the EnvoyProxy kind. + KindEnvoyProxy = "EnvoyProxy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:categories=envoy-gateway,shortName=eproxy +// +kubebuilder:subresource:status + +// EnvoyProxy is the schema for the envoyproxies API. +type EnvoyProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // EnvoyProxySpec defines the desired state of EnvoyProxy. + Spec EnvoyProxySpec `json:"spec,omitempty"` + // EnvoyProxyStatus defines the actual state of EnvoyProxy. + Status EnvoyProxyStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen=true +// EnvoyProxySpec defines the desired state of EnvoyProxy. +type EnvoyProxySpec struct { + // Provider defines the desired resource provider and provider-specific configuration. + // If unspecified, the "Kubernetes" resource provider is used with default configuration + // parameters. + // + // +optional + Provider *EnvoyProxyProvider `json:"provider,omitempty"` + + // Logging defines logging parameters for managed proxies. + // +kubebuilder:default={level: {default: warn}} + Logging ProxyLogging `json:"logging,omitempty"` + + // Telemetry defines telemetry parameters for managed proxies. + // + // +optional + Telemetry *ProxyTelemetry `json:"telemetry,omitempty"` + + // Bootstrap defines the Envoy Bootstrap as a YAML string. + // Visit https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto#envoy-v3-api-msg-config-bootstrap-v3-bootstrap + // to learn more about the syntax. + // If set, this is the Bootstrap configuration used for the managed Envoy Proxy fleet instead of the default Bootstrap configuration + // set by Envoy Gateway. + // Some fields within the Bootstrap that are required to communicate with the xDS Server (Envoy Gateway) and receive xDS resources + // from it are not configurable and will result in the `EnvoyProxy` resource being rejected. + // Backward compatibility across minor versions is not guaranteed. + // We strongly recommend using `egctl x translate` to generate a `EnvoyProxy` resource with the `Bootstrap` field set to the default + // Bootstrap configuration used. You can edit this configuration, and rerun `egctl x translate` to ensure there are no validation errors. + // + // +optional + Bootstrap *ProxyBootstrap `json:"bootstrap,omitempty"` + + // Concurrency defines the number of worker threads to run. If unset, it defaults to + // the number of cpuset threads on the platform. + // + // +optional + Concurrency *int32 `json:"concurrency,omitempty"` + + // ExtraArgs defines additional command line options that are provided to Envoy. + // More info: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#command-line-options + // Note: some command line options are used internally(e.g. --log-level) so they cannot be provided here. + // + // +optional + ExtraArgs []string `json:"extraArgs,omitempty"` + + // MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. + // Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. + // This means that the port, protocol and hostname tuple must be unique for every listener. + // If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. + // + // +optional + MergeGateways *bool `json:"mergeGateways,omitempty"` + + // Shutdown defines configuration for graceful envoy shutdown process. + // + // +optional + Shutdown *ShutdownConfig `json:"shutdown,omitempty"` +} + +// +k8s:deepcopy-gen=true +type ProxyTelemetry struct { + // AccessLogs defines accesslog parameters for managed proxies. + // If unspecified, will send default format to stdout. + // +optional + AccessLog *ProxyAccessLog `json:"accessLog,omitempty"` + // Tracing defines tracing configuration for managed proxies. + // If unspecified, will not send tracing data. + // +optional + Tracing *ProxyTracing `json:"tracing,omitempty"` + + // Metrics defines metrics configuration for managed proxies. + Metrics *ProxyMetrics `json:"metrics,omitempty"` +} + +// +k8s:deepcopy-gen=true +// EnvoyProxyProvider defines the desired state of a resource provider. +// +union +type EnvoyProxyProvider struct { + // Type is the type of resource provider to use. A resource provider provides + // infrastructure resources for running the data plane, e.g. Envoy proxy, and + // optional auxiliary control planes. Supported types are "Kubernetes". + // + // +unionDiscriminator + Type ProviderType `json:"type"` + // Kubernetes defines the desired state of the Kubernetes resource provider. + // Kubernetes provides infrastructure resources for running the data plane, + // e.g. Envoy proxy. If unspecified and type is "Kubernetes", default settings + // for managed Kubernetes resources are applied. + // + // +optional + Kubernetes *EnvoyProxyKubernetesProvider `json:"kubernetes,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ShutdownConfig defines configuration for graceful envoy shutdown process. +type ShutdownConfig struct { + // DrainTimeout defines the graceful drain timeout. This should be less than the pod's terminationGracePeriodSeconds. + // If unspecified, defaults to 600 seconds. + // + // +optional + DrainTimeout *metav1.Duration `json:"drainTimeout,omitempty"` + // MinDrainDuration defines the minimum drain duration allowing time for endpoint deprogramming to complete. + // If unspecified, defaults to 5 seconds. + // + // +optional + MinDrainDuration *metav1.Duration `json:"minDrainDuration,omitempty"` +} + +// +k8s:deepcopy-gen=true +// EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource +// provider. +type EnvoyProxyKubernetesProvider struct { + // EnvoyDeployment defines the desired state of the Envoy deployment resource. + // If unspecified, default settings for the managed Envoy deployment resource + // are applied. + // + // +optional + EnvoyDeployment *KubernetesDeploymentSpec `json:"envoyDeployment,omitempty"` + + // EnvoyService defines the desired state of the Envoy service resource. + // If unspecified, default settings for the managed Envoy service resource + // are applied. + // + // +optional + EnvoyService *KubernetesServiceSpec `json:"envoyService,omitempty"` + + // EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment. + // Once the HPA is being set, Replicas field from EnvoyDeployment will be ignored. + // + // +optional + EnvoyHpa *KubernetesHorizontalPodAutoscalerSpec `json:"envoyHpa,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ProxyLogging defines logging parameters for managed proxies. +type ProxyLogging struct { + // Level is a map of logging level per component, where the component is the key + // and the log level is the value. If unspecified, defaults to "default: warn". + // + // +kubebuilder:default={default: warn} + Level map[ProxyLogComponent]LogLevel `json:"level,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ProxyLogComponent defines a component that supports a configured logging level. +// +kubebuilder:validation:Enum=system;upstream;http;connection;admin;client;filter;main;router;runtime +type ProxyLogComponent string + +const ( + // LogComponentDefault defines the default logging component. + // See more details: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#cmdoption-l + LogComponentDefault ProxyLogComponent = "default" + + // LogComponentUpstream defines the "upstream" logging component. + LogComponentUpstream ProxyLogComponent = "upstream" + + // LogComponentHTTP defines the "http" logging component. + LogComponentHTTP ProxyLogComponent = "http" + + // LogComponentConnection defines the "connection" logging component. + LogComponentConnection ProxyLogComponent = "connection" + + // LogComponentAdmin defines the "admin" logging component. + LogComponentAdmin ProxyLogComponent = "admin" + + // LogComponentClient defines the "client" logging component. + LogComponentClient ProxyLogComponent = "client" + + // LogComponentFilter defines the "filter" logging component. + LogComponentFilter ProxyLogComponent = "filter" + + // LogComponentMain defines the "main" logging component. + LogComponentMain ProxyLogComponent = "main" + + // LogComponentRouter defines the "router" logging component. + LogComponentRouter ProxyLogComponent = "router" + + // LogComponentRuntime defines the "runtime" logging component. + LogComponentRuntime ProxyLogComponent = "runtime" +) + +// +k8s:deepcopy-gen=true +// ProxyBootstrap defines Envoy Bootstrap configuration. +type ProxyBootstrap struct { + // Type is the type of the bootstrap configuration, it should be either Replace or Merge. + // If unspecified, it defaults to Replace. + // +optional + // +kubebuilder:default=Replace + Type *BootstrapType `json:"type"` + + // Value is a YAML string of the bootstrap. + Value string `json:"value"` +} + +// +k8s:deepcopy-gen=true +// BootstrapType defines the types of bootstrap supported by Envoy Gateway. +// +kubebuilder:validation:Enum=Merge;Replace +type BootstrapType string + +const ( + // Merge merges the provided bootstrap with the default one. The provided bootstrap can add or override a value + // within a map, or add a new value to a list. + // Please note that the provided bootstrap can't override a value within a list. + BootstrapTypeMerge BootstrapType = "Merge" + + // Replace replaces the default bootstrap with the provided one. + BootstrapTypeReplace BootstrapType = "Replace" +) + +// +k8s:deepcopy-gen=true +// EnvoyProxyStatus defines the observed state of EnvoyProxy. This type is not implemented +// until https://github.com/envoyproxy/gateway/issues/1007 is fixed. +type EnvoyProxyStatus struct { + // INSERT ADDITIONAL STATUS FIELDS - define observed state of cluster. + // Important: Run "make" to regenerate code after modifying this file. +} + +// +kubebuilder:object:root=true + +// EnvoyProxyList contains a list of EnvoyProxy +type EnvoyProxyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []EnvoyProxy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&EnvoyProxy{}, &EnvoyProxyList{}) +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/groupversion_info.go b/adapter/internal/operator/gateway-api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000000..ea5c3d8b05 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/groupversion_info.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +const GroupName = "dp.wso2.com" + +var ( + + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/adapter/internal/operator/gateway-api/v1alpha1/keepalive_types.go b/adapter/internal/operator/gateway-api/v1alpha1/keepalive_types.go new file mode 100644 index 0000000000..c261eecc7e --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/keepalive_types.go @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// +k8s:deepcopy-gen=true +// TCPKeepalive define the TCP Keepalive configuration. +type TCPKeepalive struct { + // The total number of unacknowledged probes to send before deciding + // the connection is dead. + // Defaults to 9. + // + // +optional + Probes *uint32 `json:"probes,omitempty"` + // The duration a connection needs to be idle before keep-alive + // probes start being sent. + // The duration format is + // Defaults to `7200s`. + // + // +optional + IdleTime *gwapiv1.Duration `json:"idleTime,omitempty"` + // The duration between keep-alive probes. + // Defaults to `75s`. + // + // +optional + Interval *gwapiv1.Duration `json:"interval,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/kubernetes_helpers.go b/adapter/internal/operator/gateway-api/v1alpha1/kubernetes_helpers.go new file mode 100644 index 0000000000..48554a5dfd --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/kubernetes_helpers.go @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + "encoding/json" + "fmt" + + jsonpatch "github.com/evanphx/json-patch" + appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/utils/ptr" +) + +// DefaultKubernetesDeploymentStrategy returns the default deployment strategy settings. +func DefaultKubernetesDeploymentStrategy() *appv1.DeploymentStrategy { + return &appv1.DeploymentStrategy{ + Type: appv1.RollingUpdateDeploymentStrategyType, + } +} + +// DefaultKubernetesContainerImage returns the default envoyproxy image. +func DefaultKubernetesContainerImage(image string) *string { + return ptr.To(image) +} + +// DefaultKubernetesDeployment returns a new KubernetesDeploymentSpec with default settings. +func DefaultKubernetesDeployment(envoyProxyImage string, enforcerImage string) *KubernetesDeploymentSpec { + return &KubernetesDeploymentSpec{ + Strategy: DefaultKubernetesDeploymentStrategy(), + Pod: DefaultKubernetesPod(), + EnvoyProxyContainer: DefaultKubernetesContainer(envoyProxyImage), + EnforcerContainer: DefaultKubernetesContainer(enforcerImage), + } +} + +// DefaultKubernetesPod returns a new KubernetesPodSpec with default settings. +func DefaultKubernetesPod() *KubernetesPodSpec { + return &KubernetesPodSpec{} +} + +// DefaultKubernetesContainer returns a new KubernetesContainerSpec with default settings. +func DefaultKubernetesContainer(image string) *KubernetesContainerSpec { + return &KubernetesContainerSpec{ + Resources: DefaultResourceRequirements(), + Image: DefaultKubernetesContainerImage(image), + } +} + +// DefaultResourceRequirements returns a new ResourceRequirements with default settings. +func DefaultResourceRequirements() *corev1.ResourceRequirements { + return &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(DefaultDeploymentCPUResourceRequests), + corev1.ResourceMemory: resource.MustParse(DefaultDeploymentMemoryResourceRequests), + }, + } +} + +// DefaultKubernetesService returns a new KubernetesServiceSpec with default settings. +func DefaultKubernetesService() *KubernetesServiceSpec { + return &KubernetesServiceSpec{ + Type: DefaultKubernetesServiceType(), + ExternalTrafficPolicy: DefaultKubernetesServiceExternalTrafficPolicy(), + } +} + +// DefaultKubernetesServiceType returns a new KubernetesServiceType with default settings. +func DefaultKubernetesServiceType() *ServiceType { + return GetKubernetesServiceType(ServiceTypeLoadBalancer) +} + +// GetKubernetesServiceType returns the KubernetesServiceType pointer. +func GetKubernetesServiceType(serviceType ServiceType) *ServiceType { + return &serviceType +} + +func DefaultKubernetesServiceExternalTrafficPolicy() *ServiceExternalTrafficPolicy { + return GetKubernetesServiceExternalTrafficPolicy(ServiceExternalTrafficPolicyLocal) +} + +func GetKubernetesServiceExternalTrafficPolicy(serviceExternalTrafficPolicy ServiceExternalTrafficPolicy) *ServiceExternalTrafficPolicy { + return &serviceExternalTrafficPolicy +} + +// defaultKubernetesDeploymentSpec fill a default KubernetesDeploymentSpec if unspecified. +func (deployment *KubernetesDeploymentSpec) defaultKubernetesDeploymentSpec(envoyProxyImage, enforcerImage string) { + if deployment.Strategy == nil { + deployment.Strategy = DefaultKubernetesDeploymentStrategy() + } + + if deployment.Pod == nil { + deployment.Pod = DefaultKubernetesPod() + } + + if deployment.EnvoyProxyContainer == nil { + deployment.EnvoyProxyContainer = DefaultKubernetesContainer(envoyProxyImage) + } + + if deployment.EnvoyProxyContainer.Resources == nil { + deployment.EnvoyProxyContainer.Resources = DefaultResourceRequirements() + } + + if deployment.EnvoyProxyContainer.Image == nil { + deployment.EnvoyProxyContainer.Image = ptr.To(envoyProxyImage) + } + + if deployment.EnforcerContainer == nil { + deployment.EnforcerContainer = DefaultKubernetesContainer(enforcerImage) + } + + if deployment.EnforcerContainer.Resources == nil { + deployment.EnforcerContainer.Resources = DefaultResourceRequirements() + } +} + +// setDefault fill a default HorizontalPodAutoscalerSpec if unspecified +func (hpa *KubernetesHorizontalPodAutoscalerSpec) setDefault() { + if len(hpa.Metrics) == 0 { + hpa.Metrics = DefaultEnvoyProxyHpaMetrics() + } +} + +// ApplyMergePatch applies a merge patch to a deployment based on the merge type +func (deployment *KubernetesDeploymentSpec) ApplyMergePatch(old *appv1.Deployment) (*appv1.Deployment, error) { + if deployment.Patch == nil { + return old, nil + } + + var patchedJSON []byte + var err error + + // Serialize the current deployment to JSON + originalJSON, err := json.Marshal(old) + if err != nil { + return nil, fmt.Errorf("error marshaling original deployment: %w", err) + } + + switch { + case deployment.Patch.Type == nil || *deployment.Patch.Type == StrategicMerge: + patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, deployment.Patch.Value.Raw, appv1.Deployment{}) + case *deployment.Patch.Type == JSONMerge: + patchedJSON, err = jsonpatch.MergePatch(originalJSON, deployment.Patch.Value.Raw) + default: + return nil, fmt.Errorf("unsupported merge type: %s", *deployment.Patch.Type) + } + if err != nil { + return nil, fmt.Errorf("error applying merge patch: %w", err) + } + + // Deserialize the patched JSON into a new deployment object + var patchedDeployment appv1.Deployment + if err := json.Unmarshal(patchedJSON, &patchedDeployment); err != nil { + return nil, fmt.Errorf("error unmarshaling patched deployment: %w", err) + } + + return &patchedDeployment, nil +} + +// ApplyMergePatch applies a merge patch to a service based on the merge type +func (service *KubernetesServiceSpec) ApplyMergePatch(old *corev1.Service) (*corev1.Service, error) { + if service.Patch == nil { + return old, nil + } + + var patchedJSON []byte + var err error + + // Serialize the current deployment to JSON + originalJSON, err := json.Marshal(old) + if err != nil { + return nil, fmt.Errorf("error marshaling original deployment: %w", err) + } + + switch { + case service.Patch.Type == nil || *service.Patch.Type == StrategicMerge: + patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, service.Patch.Value.Raw, corev1.Service{}) + case *service.Patch.Type == JSONMerge: + patchedJSON, err = jsonpatch.MergePatch(originalJSON, service.Patch.Value.Raw) + default: + return nil, fmt.Errorf("unsupported merge type: %s", *service.Patch.Type) + } + if err != nil { + return nil, fmt.Errorf("error applying merge patch: %w", err) + } + + // Deserialize the patched JSON into a new service object + var patchedService corev1.Service + if err := json.Unmarshal(patchedJSON, &patchedService); err != nil { + return nil, fmt.Errorf("error unmarshaling patched service: %w", err) + } + + return &patchedService, nil +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/retry_types.go b/adapter/internal/operator/gateway-api/v1alpha1/retry_types.go new file mode 100644 index 0000000000..2db40aad36 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/retry_types.go @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Retry defines the retry strategy to be applied. +type Retry struct { + // NumRetries is the number of retries to be attempted. Defaults to 2. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=2 + NumRetries *int32 `json:"numRetries,omitempty"` + + // RetryOn specifies the retry trigger condition. + // + // If not specified, the default is to retry on connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes(503). + // +optional + RetryOn *RetryOn `json:"retryOn,omitempty"` + + // PerRetry is the retry policy to be applied per retry attempt. + // + // +optional + PerRetry *PerRetryPolicy `json:"perRetry,omitempty"` +} + +type RetryOn struct { + // Triggers specifies the retry trigger condition(Http/Grpc). + // + // +optional + Triggers []TriggerEnum `json:"triggers,omitempty"` + + // HttpStatusCodes specifies the http status codes to be retried. + // The retriable-status-codes trigger must also be configured for these status codes to trigger a retry. + // + // +optional + HTTPStatusCodes []HTTPStatus `json:"httpStatusCodes,omitempty"` +} + +// TriggerEnum specifies the conditions that trigger retries. +// +kubebuilder:validation:Enum={"5xx","gateway-error","reset","connect-failure","retriable-4xx","refused-stream","retriable-status-codes","cancelled","deadline-exceeded","internal","resource-exhausted","unavailable"} +type TriggerEnum string + +const ( + // HTTP events. + // For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on + + // The upstream server responds with any 5xx response code, or does not respond at all (disconnect/reset/read timeout). + // Includes connect-failure and refused-stream. + Error5XX TriggerEnum = "5xx" + // The response is a gateway error (502,503 or 504). + GatewayError TriggerEnum = "gateway-error" + // The upstream server does not respond at all (disconnect/reset/read timeout.) + Reset TriggerEnum = "reset" + // Connection failure to the upstream server (connect timeout, etc.). (Included in *5xx*) + ConnectFailure TriggerEnum = "connect-failure" + // The upstream server responds with a retriable 4xx response code. + // Currently, the only response code in this category is 409. + Retriable4XX TriggerEnum = "retriable-4xx" + // The upstream server resets the stream with a REFUSED_STREAM error code. + RefusedStream TriggerEnum = "refused-stream" + // The upstream server responds with any response code matching one defined in the RetriableStatusCodes. + RetriableStatusCodes TriggerEnum = "retriable-status-codes" + + // GRPC events, currently only supported for gRPC status codes in response headers. + // For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on + + // The gRPC status code in the response headers is “cancelled”. + Cancelled TriggerEnum = "cancelled" + // The gRPC status code in the response headers is “deadline-exceeded”. + DeadlineExceeded TriggerEnum = "deadline-exceeded" + // The gRPC status code in the response headers is “internal”. + Internal TriggerEnum = "internal" + // The gRPC status code in the response headers is “resource-exhausted”. + ResourceExhausted TriggerEnum = "resource-exhausted" + // The gRPC status code in the response headers is “unavailable”. + Unavailable TriggerEnum = "unavailable" +) + +type PerRetryPolicy struct { + // Timeout is the timeout per retry attempt. + // + // +optional + // +kubebuilder:validation:Format=duration + Timeout *metav1.Duration `json:"timeout,omitempty"` + // Backoff is the backoff policy to be applied per retry attempt. gateway uses a fully jittered exponential + // back-off algorithm for retries. For additional details, + // see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries + // + // +optional + BackOff *BackOffPolicy `json:"backOff,omitempty"` +} + +type BackOffPolicy struct { + // BaseInterval is the base interval between retries. + // + // +kubebuilder:validation:Format=duration + BaseInterval *metav1.Duration `json:"baseInterval,omitempty"` + // MaxInterval is the maximum interval between retries. This parameter is optional, but must be greater than or equal to the base_interval if set. + // The default is 10 times the base_interval + // + // +optional + // +kubebuilder:validation:Format=duration + MaxInterval *metav1.Duration `json:"maxInterval,omitempty"` + // we can add rate limited based backoff config here if we want to. +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/shared_types.go b/adapter/internal/operator/gateway-api/v1alpha1/shared_types.go new file mode 100644 index 0000000000..b3ccdb2842 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/shared_types.go @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + appv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +const ( + // DefaultDeploymentReplicas is the default number of deployment replicas. + DefaultDeploymentReplicas = 1 + // DefaultDeploymentCPUResourceRequests for deployment cpu resource + DefaultDeploymentCPUResourceRequests = "100m" + // DefaultDeploymentMemoryResourceRequests for deployment memory resource + DefaultDeploymentMemoryResourceRequests = "512Mi" + // DefaultEnvoyProxyImage is the default image used by envoyproxy + DefaultEnvoyProxyImage = "envoyproxy/envoy:distroless-v1.29.3" + // DefaultEnforcerImage default image used by enforcer + DefaultEnforcerImage = "wso2/apk-enforcer:1.1.0" + // DefaultShutdownManagerCPUResourceRequests for shutdown manager cpu resource + DefaultShutdownManagerCPUResourceRequests = "10m" + // DefaultShutdownManagerMemoryResourceRequests for shutdown manager memory resource + DefaultShutdownManagerMemoryResourceRequests = "32Mi" + // DefaultShutdownManagerImage is the default image used for the shutdown manager. + DefaultShutdownManagerImage = "envoyproxy/gateway-dev:latest" + // DefaultRateLimitImage is the default image used by ratelimit. + DefaultRateLimitImage = "envoyproxy/ratelimit:19f2079f" + // HTTPProtocol is the common-used http protocol. + HTTPProtocol = "http" + // GRPCProtocol is the common-used grpc protocol. + GRPCProtocol = "grpc" +) + +// GroupVersionKind unambiguously identifies a Kind. +// It can be converted to k8s.io/apimachinery/pkg/runtime/schema.GroupVersionKind +type GroupVersionKind struct { + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` +} + +// ProviderType defines the types of providers supported by Envoy Gateway. +// +// +kubebuilder:validation:Enum=Kubernetes +type ProviderType string + +const ( + // ProviderTypeKubernetes defines the "Kubernetes" provider. + ProviderTypeKubernetes ProviderType = "Kubernetes" + + // ProviderTypeFile defines the "File" provider. This type is not implemented + // until https://github.com/envoyproxy/gateway/issues/1001 is fixed. + ProviderTypeFile ProviderType = "File" +) + +// +k8s:deepcopy-gen=true +// KubernetesDeploymentSpec defines the desired state of the Kubernetes deployment resource. +type KubernetesDeploymentSpec struct { + // Patch defines how to perform the patch operation to deployment + // + // +optional + Patch *KubernetesPatchSpec `json:"patch,omitempty"` + + // Replicas is the number of desired pods. Defaults to 1. + // + // +optional + Replicas *int32 `json:"replicas,omitempty"` + + // The deployment strategy to use to replace existing pods with new ones. + // +optional + Strategy *appv1.DeploymentStrategy `json:"strategy,omitempty"` + + // Pod defines the desired specification of pod. + // + // +optional + Pod *KubernetesPodSpec `json:"pod,omitempty"` + + // EnvoyProxyContainer defines the desired specification of main container. + // + // +optional + EnvoyProxyContainer *KubernetesContainerSpec `json:"envoyProxyContainer,omitempty"` + + // EnforcerContainer defines the desired specification of main container. + // + // +optional + EnforcerContainer *KubernetesContainerSpec `json:"enforcerContainer,omitempty"` + + // List of initialization containers belonging to the pod. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + // + // +optional + InitContainers []corev1.Container `json:"initContainers,omitempty"` + + // TODO: Expose config as use cases are better understood, e.g. labels. +} + +// +k8s:deepcopy-gen=true +// KubernetesPodSpec defines the desired state of the Kubernetes pod resource. +type KubernetesPodSpec struct { + // Annotations are the annotations that should be appended to the pods. + // By default, no pod annotations are appended. + // + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + + // Labels are the additional labels that should be tagged to the pods. + // By default, no additional pod labels are tagged. + // + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // SecurityContext holds pod-level security attributes and common container settings. + // Optional: Defaults to empty. See type description for default values of each field. + // + // +optional + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + + // If specified, the pod's scheduling constraints. + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + + // Volumes that can be mounted by containers belonging to the pod. + // More info: https://kubernetes.io/docs/concepts/storage/volumes + // + // +optional + Volumes []corev1.Volume `json:"volumes,omitempty"` + + // ImagePullSecrets is an optional list of references to secrets + // in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // TopologySpreadConstraints describes how a group of pods ought to spread across topology + // domains. Scheduler will schedule pods in a way which abides by the constraints. + // All topologySpreadConstraints are ANDed. + // + // +optional + TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` +} + +// +k8s:deepcopy-gen=true +// KubernetesContainerSpec defines the desired state of the Kubernetes container resource. +type KubernetesContainerSpec struct { + // List of environment variables to set in the container. + // + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources required by this container. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // + // +optional + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + + // SecurityContext defines the security options the container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + // More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + // + // +optional + SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + + // Image specifies the EnvoyProxy container image to be used, instead of the default image. + // + // +optional + Image *string `json:"image,omitempty"` + + // VolumeMounts are volumes to mount into the container's filesystem. + // Cannot be updated. + // + // +optional + VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ServiceType string describes ingress methods for a service +// +enum +// +kubebuilder:validation:Enum=ClusterIP;LoadBalancer;NodePort +type ServiceType string + +const ( + // ServiceTypeClusterIP means a service will only be accessible inside the + // cluster, via the cluster IP. + ServiceTypeClusterIP ServiceType = "ClusterIP" + + // ServiceTypeLoadBalancer means a service will be exposed via an + // external load balancer (if the cloud provider supports it). + ServiceTypeLoadBalancer ServiceType = "LoadBalancer" + + // ServiceTypeNodePort means a service will be exposed on each Kubernetes Node + // at a static Port, common across all Nodes. + ServiceTypeNodePort ServiceType = "NodePort" +) + +// +k8s:deepcopy-gen=true +// ServiceExternalTrafficPolicy describes how nodes distribute service traffic they +// receive on one of the Service's "externally-facing" addresses (NodePorts, ExternalIPs, +// and LoadBalancer IPs. +// +enum +// +kubebuilder:validation:Enum=Local;Cluster +type ServiceExternalTrafficPolicy string + +const ( + // ServiceExternalTrafficPolicyCluster routes traffic to all endpoints. + ServiceExternalTrafficPolicyCluster ServiceExternalTrafficPolicy = "Cluster" + + // ServiceExternalTrafficPolicyLocal preserves the source IP of the traffic by + // routing only to endpoints on the same node as the traffic was received on + // (dropping the traffic if there are no local endpoints). + ServiceExternalTrafficPolicyLocal ServiceExternalTrafficPolicy = "Local" +) + +// +k8s:deepcopy-gen=true +// KubernetesServiceSpec defines the desired state of the Kubernetes service resource. +// +kubebuilder:validation:XValidation:message="allocateLoadBalancerNodePorts can only be set for LoadBalancer type",rule="!has(self.allocateLoadBalancerNodePorts) || self.type == 'LoadBalancer'" +// +kubebuilder:validation:XValidation:message="loadBalancerIP can only be set for LoadBalancer type",rule="!has(self.loadBalancerIP) || self.type == 'LoadBalancer'" +type KubernetesServiceSpec struct { + // Annotations that should be appended to the service. + // By default, no annotations are appended. + // + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + + // Type determines how the Service is exposed. Defaults to LoadBalancer. + // Valid options are ClusterIP, LoadBalancer and NodePort. + // "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). + // "ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP. + // "NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. + // +kubebuilder:default:="LoadBalancer" + // +optional + Type *ServiceType `json:"type,omitempty"` + + // LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider + // implementation if more than one are available or is otherwise expected to be specified + // +optional + LoadBalancerClass *string `json:"loadBalancerClass,omitempty"` + + // AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for + // services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster + // load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a + // value), those requests will be respected, regardless of this field. This field may only be set for + // services with type LoadBalancer and will be cleared if the type is changed to any other type. + // +optional + AllocateLoadBalancerNodePorts *bool `json:"allocateLoadBalancerNodePorts,omitempty"` + + // LoadBalancerIP defines the IP Address of the underlying load balancer service. This field + // may be ignored if the load balancer provider does not support this feature. + // This field has been deprecated in Kubernetes, but it is still used for setting the IP Address in some cloud + // providers such as GCP. + // + // +kubebuilder:validation:XValidation:message="loadBalancerIP must be a valid IPv4 address",rule="self.matches(r\"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$\")" + // +optional + LoadBalancerIP *string `json:"loadBalancerIP,omitempty"` + + // ExternalTrafficPolicy determines the externalTrafficPolicy for the Envoy Service. Valid options + // are Local and Cluster. Default is "Local". "Local" means traffic will only go to pods on the node + // receiving the traffic. "Cluster" means connections are loadbalanced to all pods in the cluster. + // +kubebuilder:default:="Local" + // +optional + ExternalTrafficPolicy *ServiceExternalTrafficPolicy `json:"externalTrafficPolicy,omitempty"` + + // Patch defines how to perform the patch operation to the service + // + // +optional + Patch *KubernetesPatchSpec `json:"patch,omitempty"` + // TODO: Expose config as use cases are better understood, e.g. labels. +} + +// +k8s:deepcopy-gen=true +// LogLevel defines a log level for Envoy Gateway and EnvoyProxy system logs. +// +kubebuilder:validation:Enum=debug;info;error;warn +type LogLevel string + +const ( + // LogLevelDebug defines the "debug" logging level. + LogLevelDebug LogLevel = "debug" + + // LogLevelInfo defines the "Info" logging level. + LogLevelInfo LogLevel = "info" + + // LogLevelWarn defines the "Warn" logging level. + LogLevelWarn LogLevel = "warn" + + // LogLevelError defines the "Error" logging level. + LogLevelError LogLevel = "error" +) + +// +k8s:deepcopy-gen=true +// XDSTranslatorHook defines the types of hooks that an Envoy Gateway extension may support +// for the xds-translator +// +// +kubebuilder:validation:Enum=VirtualHost;Route;HTTPListener;Translation +type XDSTranslatorHook string + +const ( + XDSVirtualHost XDSTranslatorHook = "VirtualHost" + XDSRoute XDSTranslatorHook = "Route" + XDSHTTPListener XDSTranslatorHook = "HTTPListener" + XDSTranslation XDSTranslatorHook = "Translation" +) + +// +k8s:deepcopy-gen=true +// StringMatch defines how to match any strings. +// This is a general purpose match condition that can be used by other EG APIs +// that need to match against a string. +type StringMatch struct { + // Type specifies how to match against a string. + // + // +optional + // +kubebuilder:default=Exact + Type *StringMatchType `json:"type,omitempty"` + + // Value specifies the string value that the match must have. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=1024 + Value string `json:"value"` +} + +// +k8s:deepcopy-gen=true +// StringMatchType specifies the semantics of how a string value should be compared. +// Valid MatchType values are "Exact", "Prefix", "Suffix", "RegularExpression". +// +// +kubebuilder:validation:Enum=Exact;Prefix;Suffix;RegularExpression +type StringMatchType string + +const ( + // StringMatchExact :the input string must match exactly the match value. + StringMatchExact StringMatchType = "Exact" + + // StringMatchPrefix :the input string must start with the match value. + StringMatchPrefix StringMatchType = "Prefix" + + // StringMatchSuffix :the input string must end with the match value. + StringMatchSuffix StringMatchType = "Suffix" + + // StringMatchRegularExpression :The input string must match the regular expression + // specified in the match value. + // The regex string must adhere to the syntax documented in + // https://github.com/google/re2/wiki/Syntax. + StringMatchRegularExpression StringMatchType = "RegularExpression" +) + +// +k8s:deepcopy-gen=true +// KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment. +// When HPA is enabled, it is recommended that the value in `KubernetesDeploymentSpec.replicas` be removed, otherwise +// Envoy Gateway will revert back to this value every time reconciliation occurs. +// See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec. +// +// +kubebuilder:validation:XValidation:message="maxReplicas cannot be less than minReplicas",rule="!has(self.minReplicas) || self.maxReplicas >= self.minReplicas" +type KubernetesHorizontalPodAutoscalerSpec struct { + // minReplicas is the lower limit for the number of replicas to which the autoscaler + // can scale down. It defaults to 1 replica. + // + // +kubebuilder:validation:XValidation:message="minReplicas must be greater than 0",rule="self > 0" + // +optional + MinReplicas *int32 `json:"minReplicas,omitempty"` + + // maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. + // It cannot be less that minReplicas. + // + // +kubebuilder:validation:XValidation:message="maxReplicas must be greater than 0",rule="self > 0" + MaxReplicas *int32 `json:"maxReplicas"` + + // metrics contains the specifications for which to use to calculate the + // desired replica count (the maximum replica count across all metrics will + // be used). + // If left empty, it defaults to being based on CPU utilization with average on 80% usage. + // + // +optional + Metrics []autoscalingv2.MetricSpec `json:"metrics,omitempty"` + + // behavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. + // + // +optional + Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"` +} + +// +k8s:deepcopy-gen=true +// HTTPStatus defines the http status code. +// +kubebuilder:validation:Minimum=100 +// +kubebuilder:validation:Maximum=600 +// +kubebuilder:validation:ExclusiveMaximum=true +type HTTPStatus int + +// +k8s:deepcopy-gen=true +// MergeType defines the type of merge operation +type MergeType string + +const ( + // StrategicMerge indicates a strategic merge patch type + StrategicMerge MergeType = "StrategicMerge" + // JSONMerge indicates a JSON merge patch type + JSONMerge MergeType = "JSONMerge" +) + +// +k8s:deepcopy-gen=true +// KubernetesPatchSpec defines how to perform the patch operation +type KubernetesPatchSpec struct { + // Type is the type of merge operation to perform + // + // By default, StrategicMerge is used as the patch type. + // +optional + Type *MergeType `json:"type,omitempty"` + + // Object contains the raw configuration for merged object + Value apiextensionsv1.JSON `json:"value"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/timeout_types.go b/adapter/internal/operator/gateway-api/v1alpha1/timeout_types.go new file mode 100644 index 0000000000..d772a0660f --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/timeout_types.go @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + +// +k8s:deepcopy-gen=true +// Timeout defines configuration for timeouts related to connections. +type Timeout struct { + // Timeout settings for TCP. + // + // +optional + TCP *TCPTimeout `json:"tcp,omitempty"` + + // Timeout settings for HTTP. + // + // +optional + HTTP *HTTPTimeout `json:"http,omitempty"` +} + +// +k8s:deepcopy-gen=true +type TCPTimeout struct { + // The timeout for network connection establishment, including TCP and TLS handshakes. + // Default: 10 seconds. + // + // +optional + ConnectTimeout *gwapiv1.Duration `json:"connectTimeout,omitempty"` +} + +// +k8s:deepcopy-gen=true +type HTTPTimeout struct { + // The idle timeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection. + // Default: 1 hour. + // + // +optional + ConnectionIdleTimeout *gwapiv1.Duration `json:"connectionIdleTimeout,omitempty"` + + // The maximum duration of an HTTP connection. + // Default: unlimited. + // + // +optional + MaxConnectionDuration *gwapiv1.Duration `json:"maxConnectionDuration,omitempty"` +} + +// +k8s:deepcopy-gen=true +type ClientTimeout struct { + // Timeout settings for HTTP. + // + // +optional + HTTP *HTTPClientTimeout `json:"http,omitempty"` +} + +// +k8s:deepcopy-gen=true +type HTTPClientTimeout struct { + // The duration envoy waits for the complete request reception. This timer starts upon request + // initiation and stops when either the last byte of the request is sent upstream or when the response begins. + // + // +optional + RequestReceivedTimeout *gwapiv1.Duration `json:"requestReceivedTimeout,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/tls_types.go b/adapter/internal/operator/gateway-api/v1alpha1/tls_types.go new file mode 100644 index 0000000000..0f1a8accb0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/tls_types.go @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// +k8s:deepcopy-gen=true +// +kubebuilder:validation:XValidation:rule="has(self.minVersion) && self.minVersion == '1.3' ? !has(self.ciphers) : true", message="setting ciphers has no effect if the minimum possible TLS version is 1.3" +// +kubebuilder:validation:XValidation:rule="has(self.minVersion) && has(self.maxVersion) ? {\"Auto\":0,\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4}[self.minVersion] <= {\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4,\"Auto\":5}[self.maxVersion] : !has(self.minVersion) && has(self.maxVersion) ? 3 <= {\"1.0\":1,\"1.1\":2,\"1.2\":3,\"1.3\":4,\"Auto\":5}[self.maxVersion] : true", message="minVersion must be smaller or equal to maxVersion" +type TLSSettings struct { + + // Min specifies the minimal TLS protocol version to allow. + // The default is TLS 1.2 if this is not specified. + // + // +optional + MinVersion *TLSVersion `json:"minVersion,omitempty"` + + // Max specifies the maximal TLS protocol version to allow + // The default is TLS 1.3 if this is not specified. + // + // +optional + MaxVersion *TLSVersion `json:"maxVersion,omitempty"` + + // Ciphers specifies the set of cipher suites supported when + // negotiating TLS 1.0 - 1.2. This setting has no effect for TLS 1.3. + // In non-FIPS Envoy Proxy builds the default cipher list is: + // - [ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305] + // - [ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305] + // - ECDHE-ECDSA-AES256-GCM-SHA384 + // - ECDHE-RSA-AES256-GCM-SHA384 + // In builds using BoringSSL FIPS the default cipher list is: + // - ECDHE-ECDSA-AES128-GCM-SHA256 + // - ECDHE-RSA-AES128-GCM-SHA256 + // - ECDHE-ECDSA-AES256-GCM-SHA384 + // - ECDHE-RSA-AES256-GCM-SHA384 + // + // +optional + Ciphers []string `json:"ciphers,omitempty"` + + // ECDHCurves specifies the set of supported ECDH curves. + // In non-FIPS Envoy Proxy builds the default curves are: + // - X25519 + // - P-256 + // In builds using BoringSSL FIPS the default curve is: + // - P-256 + // + // +optional + ECDHCurves []string `json:"ecdhCurves,omitempty"` + + // SignatureAlgorithms specifies which signature algorithms the listener should + // support. + // + // +optional + SignatureAlgorithms []string `json:"signatureAlgorithms,omitempty"` + + // ALPNProtocols supplies the list of ALPN protocols that should be + // exposed by the listener. By default h2 and http/1.1 are enabled. + // Supported values are: + // - http/1.0 + // - http/1.1 + // - h2 + // + // +optional + ALPNProtocols []ALPNProtocol `json:"alpnProtocols,omitempty"` + + // ClientValidation specifies the configuration to validate the client + // initiating the TLS connection to the Gateway listener. + // +optional + ClientValidation *ClientValidationContext `json:"clientValidation,omitempty"` +} + +// +k8s:deepcopy-gen=true +// ALPNProtocol specifies the protocol to be negotiated using ALPN +// +kubebuilder:validation:Enum=http/1.0;http/1.1;h2 +type ALPNProtocol string + +// When adding ALPN constants, they must be values that are defined +// in the IANA registry for ALPN identification sequences +// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids +const ( + // HTTPProtocolVersion1_0 specifies that HTTP/1.0 should be negotiable with ALPN + HTTPProtocolVersion1_0 ALPNProtocol = "http/1.0" + // HTTPProtocolVersion1_1 specifies that HTTP/1.1 should be negotiable with ALPN + HTTPProtocolVersion1_1 ALPNProtocol = "http/1.1" + // HTTPProtocolVersion2 specifies that HTTP/2 should be negotiable with ALPN + HTTPProtocolVersion2 ALPNProtocol = "h2" +) + +// +k8s:deepcopy-gen=true +// TLSVersion specifies the TLS version +// +kubebuilder:validation:Enum=Auto;"1.0";"1.1";"1.2";"1.3" +type TLSVersion string + +const ( + // TLSAuto allows Envoy to choose the optimal TLS Version + TLSAuto TLSVersion = "Auto" + // TLS1.0 specifies TLS version 1.0 + TLSv10 TLSVersion = "1.0" + // TLS1.1 specifies TLS version 1.1 + TLSv11 TLSVersion = "1.1" + // TLSv1.2 specifies TLS version 1.2 + TLSv12 TLSVersion = "1.2" + // TLSv1.3 specifies TLS version 1.3 + TLSv13 TLSVersion = "1.3" +) + +// +k8s:deepcopy-gen=true +// ClientValidationContext holds configuration that can be used to validate the client initiating the TLS connection +// to the Gateway. +// By default, no client specific configuration is validated. +type ClientValidationContext struct { + // CACertificateRefs contains one or more references to + // Kubernetes objects that contain TLS certificates of + // the Certificate Authorities that can be used + // as a trust anchor to validate the certificates presented by the client. + // + // A single reference to a Kubernetes ConfigMap or a Kubernetes Secret, + // with the CA certificate in a key named `ca.crt` is currently supported. + // + // References to a resource in different namespace are invalid UNLESS there + // is a ReferenceGrant in the target namespace that allows the certificate + // to be attached. + // + // +kubebuilder:validation:MaxItems=8 + // +optional + CACertificateRefs []gwapiv1.SecretObjectReference `json:"caCertificateRefs,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/tracing_types.go b/adapter/internal/operator/gateway-api/v1alpha1/tracing_types.go new file mode 100644 index 0000000000..311cd482b0 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/tracing_types.go @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package v1alpha1 + +// +k8s:deepcopy-gen=true +type ProxyTracing struct { + // SamplingRate controls the rate at which traffic will be + // selected for tracing if no prior sampling decision has been made. + // Defaults to 100, valid values [0-100]. 100 indicates 100% sampling. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + // +kubebuilder:default=100 + // +optional + SamplingRate *uint32 `json:"samplingRate,omitempty"` + // CustomTags defines the custom tags to add to each span. + // If provider is kubernetes, pod name and namespace are added by default. + CustomTags map[string]CustomTag `json:"customTags,omitempty"` + // Provider defines the tracing provider. + // Only OpenTelemetry is supported currently. + Provider TracingProvider `json:"provider"` +} + +// +k8s:deepcopy-gen=true +type TracingProviderType string + +const ( + TracingProviderTypeOpenTelemetry TracingProviderType = "OpenTelemetry" +) + +// +k8s:deepcopy-gen=true +type TracingProvider struct { + // Type defines the tracing provider type. + // EG currently only supports OpenTelemetry. + // +kubebuilder:validation:Enum=OpenTelemetry + // +kubebuilder:default=OpenTelemetry + Type TracingProviderType `json:"type"` + // Host define the provider service hostname. + Host string `json:"host"` + // Port defines the port the provider service is exposed on. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=4317 + Port int32 `json:"port,omitempty"` +} + +// +k8s:deepcopy-gen=true +type CustomTagType string + +const ( + // CustomTagTypeLiteral adds hard-coded value to each span. + CustomTagTypeLiteral CustomTagType = "Literal" + // CustomTagTypeEnvironment adds value from environment variable to each span. + CustomTagTypeEnvironment CustomTagType = "Environment" + // CustomTagTypeRequestHeader adds value from request header to each span. + CustomTagTypeRequestHeader CustomTagType = "RequestHeader" +) + +// +k8s:deepcopy-gen=true +type CustomTag struct { + // Type defines the type of custom tag. + // +kubebuilder:validation:Enum=Literal;Environment;RequestHeader + // +unionDiscriminator + // +kubebuilder:default=Literal + Type CustomTagType `json:"type"` + // Literal adds hard-coded value to each span. + // It's required when the type is "Literal". + Literal *LiteralCustomTag `json:"literal,omitempty"` + // Environment adds value from environment variable to each span. + // It's required when the type is "Environment". + Environment *EnvironmentCustomTag `json:"environment,omitempty"` + // RequestHeader adds value from request header to each span. + // It's required when the type is "RequestHeader". + RequestHeader *RequestHeaderCustomTag `json:"requestHeader,omitempty"` + + // TODO: add support for Metadata tags in the future. + // EG currently doesn't support metadata for route or cluster. +} + +// +k8s:deepcopy-gen=true +// LiteralCustomTag adds hard-coded value to each span. +type LiteralCustomTag struct { + // Value defines the hard-coded value to add to each span. + Value string `json:"value"` +} + +// +k8s:deepcopy-gen=true +// EnvironmentCustomTag adds value from environment variable to each span. +type EnvironmentCustomTag struct { + // Name defines the name of the environment variable which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the environment variable is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} + +// +k8s:deepcopy-gen=true +// RequestHeaderCustomTag adds value from request header to each span. +type RequestHeaderCustomTag struct { + // Name defines the name of the request header which to extract the value from. + Name string `json:"name"` + // DefaultValue defines the default value to use if the request header is not set. + // +optional + DefaultValue *string `json:"defaultValue,omitempty"` +} diff --git a/adapter/internal/operator/gateway-api/v1alpha1/zz_generated.deepcopy.go b/adapter/internal/operator/gateway-api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..0e34624ce7 --- /dev/null +++ b/adapter/internal/operator/gateway-api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,1159 @@ +//go:build !ignore_autogenerated + +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + appsv1 "k8s.io/api/apps/v1" + "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + apisv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientTimeout) DeepCopyInto(out *ClientTimeout) { + *out = *in + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPClientTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTimeout. +func (in *ClientTimeout) DeepCopy() *ClientTimeout { + if in == nil { + return nil + } + out := new(ClientTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientValidationContext) DeepCopyInto(out *ClientValidationContext) { + *out = *in + if in.CACertificateRefs != nil { + in, out := &in.CACertificateRefs, &out.CACertificateRefs + *out = make([]apisv1.SecretObjectReference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientValidationContext. +func (in *ClientValidationContext) DeepCopy() *ClientValidationContext { + if in == nil { + return nil + } + out := new(ClientValidationContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomTag) DeepCopyInto(out *CustomTag) { + *out = *in + if in.Literal != nil { + in, out := &in.Literal, &out.Literal + *out = new(LiteralCustomTag) + **out = **in + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = new(EnvironmentCustomTag) + (*in).DeepCopyInto(*out) + } + if in.RequestHeader != nil { + in, out := &in.RequestHeader, &out.RequestHeader + *out = new(RequestHeaderCustomTag) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomTag. +func (in *CustomTag) DeepCopy() *CustomTag { + if in == nil { + return nil + } + out := new(CustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentCustomTag) DeepCopyInto(out *EnvironmentCustomTag) { + *out = *in + if in.DefaultValue != nil { + in, out := &in.DefaultValue, &out.DefaultValue + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentCustomTag. +func (in *EnvironmentCustomTag) DeepCopy() *EnvironmentCustomTag { + if in == nil { + return nil + } + out := new(EnvironmentCustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxy) DeepCopyInto(out *EnvoyProxy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxy. +func (in *EnvoyProxy) DeepCopy() *EnvoyProxy { + if in == nil { + return nil + } + out := new(EnvoyProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyProxy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyKubernetesProvider) DeepCopyInto(out *EnvoyProxyKubernetesProvider) { + *out = *in + if in.EnvoyDeployment != nil { + in, out := &in.EnvoyDeployment, &out.EnvoyDeployment + *out = new(KubernetesDeploymentSpec) + (*in).DeepCopyInto(*out) + } + if in.EnvoyService != nil { + in, out := &in.EnvoyService, &out.EnvoyService + *out = new(KubernetesServiceSpec) + (*in).DeepCopyInto(*out) + } + if in.EnvoyHpa != nil { + in, out := &in.EnvoyHpa, &out.EnvoyHpa + *out = new(KubernetesHorizontalPodAutoscalerSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyKubernetesProvider. +func (in *EnvoyProxyKubernetesProvider) DeepCopy() *EnvoyProxyKubernetesProvider { + if in == nil { + return nil + } + out := new(EnvoyProxyKubernetesProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyList) DeepCopyInto(out *EnvoyProxyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyProxy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyList. +func (in *EnvoyProxyList) DeepCopy() *EnvoyProxyList { + if in == nil { + return nil + } + out := new(EnvoyProxyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyProxyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyProvider) DeepCopyInto(out *EnvoyProxyProvider) { + *out = *in + if in.Kubernetes != nil { + in, out := &in.Kubernetes, &out.Kubernetes + *out = new(EnvoyProxyKubernetesProvider) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyProvider. +func (in *EnvoyProxyProvider) DeepCopy() *EnvoyProxyProvider { + if in == nil { + return nil + } + out := new(EnvoyProxyProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { + *out = *in + if in.Provider != nil { + in, out := &in.Provider, &out.Provider + *out = new(EnvoyProxyProvider) + (*in).DeepCopyInto(*out) + } + in.Logging.DeepCopyInto(&out.Logging) + if in.Telemetry != nil { + in, out := &in.Telemetry, &out.Telemetry + *out = new(ProxyTelemetry) + (*in).DeepCopyInto(*out) + } + if in.Bootstrap != nil { + in, out := &in.Bootstrap, &out.Bootstrap + *out = new(ProxyBootstrap) + (*in).DeepCopyInto(*out) + } + if in.Concurrency != nil { + in, out := &in.Concurrency, &out.Concurrency + *out = new(int32) + **out = **in + } + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MergeGateways != nil { + in, out := &in.MergeGateways, &out.MergeGateways + *out = new(bool) + **out = **in + } + if in.Shutdown != nil { + in, out := &in.Shutdown, &out.Shutdown + *out = new(ShutdownConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. +func (in *EnvoyProxySpec) DeepCopy() *EnvoyProxySpec { + if in == nil { + return nil + } + out := new(EnvoyProxySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyProxyStatus) DeepCopyInto(out *EnvoyProxyStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxyStatus. +func (in *EnvoyProxyStatus) DeepCopy() *EnvoyProxyStatus { + if in == nil { + return nil + } + out := new(EnvoyProxyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileEnvoyProxyAccessLog) DeepCopyInto(out *FileEnvoyProxyAccessLog) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileEnvoyProxyAccessLog. +func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { + if in == nil { + return nil + } + out := new(FileEnvoyProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPClientTimeout) DeepCopyInto(out *HTTPClientTimeout) { + *out = *in + if in.RequestReceivedTimeout != nil { + in, out := &in.RequestReceivedTimeout, &out.RequestReceivedTimeout + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPClientTimeout. +func (in *HTTPClientTimeout) DeepCopy() *HTTPClientTimeout { + if in == nil { + return nil + } + out := new(HTTPClientTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) { + *out = *in + if in.ConnectionIdleTimeout != nil { + in, out := &in.ConnectionIdleTimeout, &out.ConnectionIdleTimeout + *out = new(apisv1.Duration) + **out = **in + } + if in.MaxConnectionDuration != nil { + in, out := &in.MaxConnectionDuration, &out.MaxConnectionDuration + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTimeout. +func (in *HTTPTimeout) DeepCopy() *HTTPTimeout { + if in == nil { + return nil + } + out := new(HTTPTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesContainerSpec) DeepCopyInto(out *KubernetesContainerSpec) { + *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]corev1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesContainerSpec. +func (in *KubernetesContainerSpec) DeepCopy() *KubernetesContainerSpec { + if in == nil { + return nil + } + out := new(KubernetesContainerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesDeploymentSpec) DeepCopyInto(out *KubernetesDeploymentSpec) { + *out = *in + if in.Patch != nil { + in, out := &in.Patch, &out.Patch + *out = new(KubernetesPatchSpec) + (*in).DeepCopyInto(*out) + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Strategy != nil { + in, out := &in.Strategy, &out.Strategy + *out = new(appsv1.DeploymentStrategy) + (*in).DeepCopyInto(*out) + } + if in.Pod != nil { + in, out := &in.Pod, &out.Pod + *out = new(KubernetesPodSpec) + (*in).DeepCopyInto(*out) + } + if in.EnvoyProxyContainer != nil { + in, out := &in.EnvoyProxyContainer, &out.EnvoyProxyContainer + *out = new(KubernetesContainerSpec) + (*in).DeepCopyInto(*out) + } + if in.EnforcerContainer != nil { + in, out := &in.EnforcerContainer, &out.EnforcerContainer + *out = new(KubernetesContainerSpec) + (*in).DeepCopyInto(*out) + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]corev1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesDeploymentSpec. +func (in *KubernetesDeploymentSpec) DeepCopy() *KubernetesDeploymentSpec { + if in == nil { + return nil + } + out := new(KubernetesDeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesHorizontalPodAutoscalerSpec) DeepCopyInto(out *KubernetesHorizontalPodAutoscalerSpec) { + *out = *in + if in.MinReplicas != nil { + in, out := &in.MinReplicas, &out.MinReplicas + *out = new(int32) + **out = **in + } + if in.MaxReplicas != nil { + in, out := &in.MaxReplicas, &out.MaxReplicas + *out = new(int32) + **out = **in + } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = make([]v2.MetricSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Behavior != nil { + in, out := &in.Behavior, &out.Behavior + *out = new(v2.HorizontalPodAutoscalerBehavior) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesHorizontalPodAutoscalerSpec. +func (in *KubernetesHorizontalPodAutoscalerSpec) DeepCopy() *KubernetesHorizontalPodAutoscalerSpec { + if in == nil { + return nil + } + out := new(KubernetesHorizontalPodAutoscalerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesPatchSpec) DeepCopyInto(out *KubernetesPatchSpec) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(MergeType) + **out = **in + } + in.Value.DeepCopyInto(&out.Value) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesPatchSpec. +func (in *KubernetesPatchSpec) DeepCopy() *KubernetesPatchSpec { + if in == nil { + return nil + } + out := new(KubernetesPatchSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesPodSpec) DeepCopyInto(out *KubernetesPodSpec) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(corev1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(corev1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]corev1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]corev1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]corev1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]corev1.TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesPodSpec. +func (in *KubernetesPodSpec) DeepCopy() *KubernetesPodSpec { + if in == nil { + return nil + } + out := new(KubernetesPodSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(ServiceType) + **out = **in + } + if in.LoadBalancerClass != nil { + in, out := &in.LoadBalancerClass, &out.LoadBalancerClass + *out = new(string) + **out = **in + } + if in.AllocateLoadBalancerNodePorts != nil { + in, out := &in.AllocateLoadBalancerNodePorts, &out.AllocateLoadBalancerNodePorts + *out = new(bool) + **out = **in + } + if in.LoadBalancerIP != nil { + in, out := &in.LoadBalancerIP, &out.LoadBalancerIP + *out = new(string) + **out = **in + } + if in.ExternalTrafficPolicy != nil { + in, out := &in.ExternalTrafficPolicy, &out.ExternalTrafficPolicy + *out = new(ServiceExternalTrafficPolicy) + **out = **in + } + if in.Patch != nil { + in, out := &in.Patch, &out.Patch + *out = new(KubernetesPatchSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesServiceSpec. +func (in *KubernetesServiceSpec) DeepCopy() *KubernetesServiceSpec { + if in == nil { + return nil + } + out := new(KubernetesServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LiteralCustomTag) DeepCopyInto(out *LiteralCustomTag) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LiteralCustomTag. +func (in *LiteralCustomTag) DeepCopy() *LiteralCustomTag { + if in == nil { + return nil + } + out := new(LiteralCustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopyInto(out *OpenTelemetryEnvoyProxyAccessLog) { + *out = *in + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryEnvoyProxyAccessLog. +func (in *OpenTelemetryEnvoyProxyAccessLog) DeepCopy() *OpenTelemetryEnvoyProxyAccessLog { + if in == nil { + return nil + } + out := new(OpenTelemetryEnvoyProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) { + *out = *in + if in.Settings != nil { + in, out := &in.Settings, &out.Settings + *out = make([]ProxyAccessLogSetting, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLog. +func (in *ProxyAccessLog) DeepCopy() *ProxyAccessLog { + if in == nil { + return nil + } + out := new(ProxyAccessLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogFormat) DeepCopyInto(out *ProxyAccessLogFormat) { + *out = *in + if in.Text != nil { + in, out := &in.Text, &out.Text + *out = new(string) + **out = **in + } + if in.JSON != nil { + in, out := &in.JSON, &out.JSON + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogFormat. +func (in *ProxyAccessLogFormat) DeepCopy() *ProxyAccessLogFormat { + if in == nil { + return nil + } + out := new(ProxyAccessLogFormat) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogSetting) DeepCopyInto(out *ProxyAccessLogSetting) { + *out = *in + in.Format.DeepCopyInto(&out.Format) + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = make([]ProxyAccessLogSink, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSetting. +func (in *ProxyAccessLogSetting) DeepCopy() *ProxyAccessLogSetting { + if in == nil { + return nil + } + out := new(ProxyAccessLogSetting) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyAccessLogSink) DeepCopyInto(out *ProxyAccessLogSink) { + *out = *in + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileEnvoyProxyAccessLog) + **out = **in + } + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = new(OpenTelemetryEnvoyProxyAccessLog) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyAccessLogSink. +func (in *ProxyAccessLogSink) DeepCopy() *ProxyAccessLogSink { + if in == nil { + return nil + } + out := new(ProxyAccessLogSink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyBootstrap) DeepCopyInto(out *ProxyBootstrap) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(BootstrapType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyBootstrap. +func (in *ProxyBootstrap) DeepCopy() *ProxyBootstrap { + if in == nil { + return nil + } + out := new(ProxyBootstrap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyLogging) DeepCopyInto(out *ProxyLogging) { + *out = *in + if in.Level != nil { + in, out := &in.Level, &out.Level + *out = make(map[ProxyLogComponent]LogLevel, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyLogging. +func (in *ProxyLogging) DeepCopy() *ProxyLogging { + if in == nil { + return nil + } + out := new(ProxyLogging) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyMetricSink) DeepCopyInto(out *ProxyMetricSink) { + *out = *in + if in.OpenTelemetry != nil { + in, out := &in.OpenTelemetry, &out.OpenTelemetry + *out = new(ProxyOpenTelemetrySink) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyMetricSink. +func (in *ProxyMetricSink) DeepCopy() *ProxyMetricSink { + if in == nil { + return nil + } + out := new(ProxyMetricSink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyMetrics) DeepCopyInto(out *ProxyMetrics) { + *out = *in + if in.Prometheus != nil { + in, out := &in.Prometheus, &out.Prometheus + *out = new(ProxyPrometheusProvider) + **out = **in + } + if in.Sinks != nil { + in, out := &in.Sinks, &out.Sinks + *out = make([]ProxyMetricSink, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]StringMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyMetrics. +func (in *ProxyMetrics) DeepCopy() *ProxyMetrics { + if in == nil { + return nil + } + out := new(ProxyMetrics) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyOpenTelemetrySink) DeepCopyInto(out *ProxyOpenTelemetrySink) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyOpenTelemetrySink. +func (in *ProxyOpenTelemetrySink) DeepCopy() *ProxyOpenTelemetrySink { + if in == nil { + return nil + } + out := new(ProxyOpenTelemetrySink) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyPrometheusProvider) DeepCopyInto(out *ProxyPrometheusProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyPrometheusProvider. +func (in *ProxyPrometheusProvider) DeepCopy() *ProxyPrometheusProvider { + if in == nil { + return nil + } + out := new(ProxyPrometheusProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyTelemetry) DeepCopyInto(out *ProxyTelemetry) { + *out = *in + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(ProxyAccessLog) + (*in).DeepCopyInto(*out) + } + if in.Tracing != nil { + in, out := &in.Tracing, &out.Tracing + *out = new(ProxyTracing) + (*in).DeepCopyInto(*out) + } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(ProxyMetrics) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTelemetry. +func (in *ProxyTelemetry) DeepCopy() *ProxyTelemetry { + if in == nil { + return nil + } + out := new(ProxyTelemetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyTracing) DeepCopyInto(out *ProxyTracing) { + *out = *in + if in.SamplingRate != nil { + in, out := &in.SamplingRate, &out.SamplingRate + *out = new(uint32) + **out = **in + } + if in.CustomTags != nil { + in, out := &in.CustomTags, &out.CustomTags + *out = make(map[string]CustomTag, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.Provider = in.Provider +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyTracing. +func (in *ProxyTracing) DeepCopy() *ProxyTracing { + if in == nil { + return nil + } + out := new(ProxyTracing) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestHeaderCustomTag) DeepCopyInto(out *RequestHeaderCustomTag) { + *out = *in + if in.DefaultValue != nil { + in, out := &in.DefaultValue, &out.DefaultValue + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeaderCustomTag. +func (in *RequestHeaderCustomTag) DeepCopy() *RequestHeaderCustomTag { + if in == nil { + return nil + } + out := new(RequestHeaderCustomTag) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ShutdownConfig) DeepCopyInto(out *ShutdownConfig) { + *out = *in + if in.DrainTimeout != nil { + in, out := &in.DrainTimeout, &out.DrainTimeout + *out = new(v1.Duration) + **out = **in + } + if in.MinDrainDuration != nil { + in, out := &in.MinDrainDuration, &out.MinDrainDuration + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShutdownConfig. +func (in *ShutdownConfig) DeepCopy() *ShutdownConfig { + if in == nil { + return nil + } + out := new(ShutdownConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StringMatch) DeepCopyInto(out *StringMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(StringMatchType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. +func (in *StringMatch) DeepCopy() *StringMatch { + if in == nil { + return nil + } + out := new(StringMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPKeepalive) DeepCopyInto(out *TCPKeepalive) { + *out = *in + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(uint32) + **out = **in + } + if in.IdleTime != nil { + in, out := &in.IdleTime, &out.IdleTime + *out = new(apisv1.Duration) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPKeepalive. +func (in *TCPKeepalive) DeepCopy() *TCPKeepalive { + if in == nil { + return nil + } + out := new(TCPKeepalive) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPTimeout) DeepCopyInto(out *TCPTimeout) { + *out = *in + if in.ConnectTimeout != nil { + in, out := &in.ConnectTimeout, &out.ConnectTimeout + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPTimeout. +func (in *TCPTimeout) DeepCopy() *TCPTimeout { + if in == nil { + return nil + } + out := new(TCPTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSettings) DeepCopyInto(out *TLSSettings) { + *out = *in + if in.MinVersion != nil { + in, out := &in.MinVersion, &out.MinVersion + *out = new(TLSVersion) + **out = **in + } + if in.MaxVersion != nil { + in, out := &in.MaxVersion, &out.MaxVersion + *out = new(TLSVersion) + **out = **in + } + if in.Ciphers != nil { + in, out := &in.Ciphers, &out.Ciphers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ECDHCurves != nil { + in, out := &in.ECDHCurves, &out.ECDHCurves + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SignatureAlgorithms != nil { + in, out := &in.SignatureAlgorithms, &out.SignatureAlgorithms + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ALPNProtocols != nil { + in, out := &in.ALPNProtocols, &out.ALPNProtocols + *out = make([]ALPNProtocol, len(*in)) + copy(*out, *in) + } + if in.ClientValidation != nil { + in, out := &in.ClientValidation, &out.ClientValidation + *out = new(ClientValidationContext) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSettings. +func (in *TLSSettings) DeepCopy() *TLSSettings { + if in == nil { + return nil + } + out := new(TLSSettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timeout) DeepCopyInto(out *Timeout) { + *out = *in + if in.TCP != nil { + in, out := &in.TCP, &out.TCP + *out = new(TCPTimeout) + (*in).DeepCopyInto(*out) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timeout. +func (in *Timeout) DeepCopy() *Timeout { + if in == nil { + return nil + } + out := new(Timeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TracingProvider) DeepCopyInto(out *TracingProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TracingProvider. +func (in *TracingProvider) DeepCopy() *TracingProvider { + if in == nil { + return nil + } + out := new(TracingProvider) + in.DeepCopyInto(out) + return out +} diff --git a/adapter/internal/operator/gateway-api/validate.go b/adapter/internal/operator/gateway-api/validate.go new file mode 100644 index 0000000000..56f13a2c54 --- /dev/null +++ b/adapter/internal/operator/gateway-api/validate.go @@ -0,0 +1,916 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package gatewayapi + +import ( + "errors" + "fmt" + "net/netip" + "strings" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, + resources *Resources, backendNamespace string, routeKind gwapiv1.Kind) bool { + if !t.validateBackendRefFilters(backendRefContext, parentRef, route, routeKind) { + return false + } + backendRef := GetBackendRef(backendRefContext) + + if !t.validateBackendRefGroup(backendRef, parentRef, route) { + return false + } + if !t.validateBackendRefKind(backendRef, parentRef, route) { + return false + } + if !t.validateBackendNamespace(backendRef, parentRef, route, resources, routeKind) { + return false + } + if !t.validateBackendPort(backendRef, parentRef, route) { + return false + } + protocol := v1.ProtocolTCP + if routeKind == KindUDPRoute { + protocol = v1.ProtocolUDP + } + backendRefKind := KindDerefOr(backendRef.Kind, KindService) + switch backendRefKind { + case KindService: + if !t.validateBackendService(backendRef, parentRef, resources, backendNamespace, route, protocol) { + return false + } + } + return true +} + +func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { + if backendRef.Group != nil && *backendRef.Group != "" && *backendRef.Group != GroupMultiClusterService { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonInvalidKind, + fmt.Sprintf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) and %s are supported", GroupMultiClusterService), + ) + return false + } + return true +} + +func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { + if backendRef.Kind != nil && *backendRef.Kind != KindService && *backendRef.Kind != KindServiceImport { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonInvalidKind, + "Kind is invalid, only Service and MCS ServiceImport are supported", + ) + return false + } + return true +} + +func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) bool { + var filtersLen int + switch routeKind { + case KindHTTPRoute: + filters := GetFilters(backendRef).([]gwapiv1.HTTPRouteFilter) + filtersLen = len(filters) + case KindGRPCRoute: + filters := GetFilters(backendRef).([]gwapiv1a2.GRPCRouteFilter) + filtersLen = len(filters) + default: + return true + } + + if filtersLen > 0 { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "UnsupportedRefValue", + "The filters field within BackendRef is not supported", + ) + return false + } + + return true +} + +func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, + resources *Resources, routeKind gwapiv1.Kind) bool { + if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && string(*backendRef.Namespace) != route.GetNamespace() { + if !t.validateCrossNamespaceRef( + crossNamespaceFrom{ + group: gwapiv1.GroupName, + kind: string(routeKind), + namespace: route.GetNamespace(), + }, + crossNamespaceTo{ + group: GroupDerefOr(backendRef.Group, ""), + kind: KindDerefOr(backendRef.Kind, KindService), + namespace: string(*backendRef.Namespace), + name: string(backendRef.Name), + }, + resources.ReferenceGrants, + ) { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonRefNotPermitted, + fmt.Sprintf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, KindService), *backendRef.Namespace, backendRef.Name), + ) + return false + } + } + return true +} + +func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) bool { + if backendRef.Port == nil { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "PortNotSpecified", + "A valid port number corresponding to a port on the Service must be specified", + ) + return false + } + return true +} +func (t *Translator) validateBackendService(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *Resources, + serviceNamespace string, route RouteContext, protocol v1.Protocol) bool { + service := resources.GetService(serviceNamespace, string(backendRef.Name)) + if service == nil { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonBackendNotFound, + fmt.Sprintf("Service %s/%s not found", NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()), + string(backendRef.Name)), + ) + return false + } + var portFound bool + for _, port := range service.Spec.Ports { + portProtocol := port.Protocol + if port.Protocol == "" { // Default protocol is TCP + portProtocol = v1.ProtocolTCP + } + if port.Port == int32(*backendRef.Port) && portProtocol == protocol { + portFound = true + break + } + } + + if !portFound { + parentRef.SetCondition(route, + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + "PortNotFound", + fmt.Sprintf(string(protocol)+" Port %d not found on service %s/%s", *backendRef.Port, serviceNamespace, + string(backendRef.Name)), + ) + return false + } + return true +} + +func (t *Translator) validateListenerConditions(listener *ListenerContext) (isReady bool) { + lConditions := listener.GetConditions() + if len(lConditions) == 0 { + listener.SetCondition(gwapiv1.ListenerConditionProgrammed, metav1.ConditionTrue, gwapiv1.ListenerReasonProgrammed, + "Sending translated listener configuration to the data plane") + listener.SetCondition(gwapiv1.ListenerConditionAccepted, metav1.ConditionTrue, gwapiv1.ListenerReasonAccepted, + "Listener has been successfully translated") + listener.SetCondition(gwapiv1.ListenerConditionResolvedRefs, metav1.ConditionTrue, gwapiv1.ListenerReasonResolvedRefs, + "Listener references have been resolved") + return true + } + + // Any condition on the listener apart from Programmed=true indicates an error. + if !(lConditions[0].Type == string(gwapiv1.ListenerConditionProgrammed) && lConditions[0].Status == metav1.ConditionTrue) { + hasProgrammedCond := false + hasRefsCond := false + for _, existing := range lConditions { + if existing.Type == string(gwapiv1.ListenerConditionProgrammed) { + hasProgrammedCond = true + } + if existing.Type == string(gwapiv1.ListenerConditionResolvedRefs) { + hasRefsCond = true + } + } + // set "Programmed: false" if it's not set already. + if !hasProgrammedCond { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + "Listener is invalid, see other Conditions for details.", + ) + } + // set "ResolvedRefs: true" if it's not set already. + if !hasRefsCond { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionTrue, + gwapiv1.ListenerReasonResolvedRefs, + "Listener references have been resolved", + ) + } + // skip computing IR + return false + } + return true +} + +func (t *Translator) validateAllowedNamespaces(listener *ListenerContext) { + if listener.AllowedRoutes != nil && + listener.AllowedRoutes.Namespaces != nil && + listener.AllowedRoutes.Namespaces.From != nil && + *listener.AllowedRoutes.Namespaces.From == gwapiv1.NamespacesFromSelector { + if listener.AllowedRoutes.Namespaces.Selector == nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + "The allowedRoutes.namespaces.selector field must be specified when allowedRoutes.namespaces.from is set to \"Selector\".", + ) + } else { + selector, err := metav1.LabelSelectorAsSelector(listener.AllowedRoutes.Namespaces.Selector) + if err != nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + fmt.Sprintf("The allowedRoutes.namespaces.selector could not be parsed: %v.", err), + ) + } + + listener.namespaceSelector = selector + } + } +} + +func (t *Translator) validateTerminateModeAndGetTLSSecrets(listener *ListenerContext, resources *Resources) []*v1.Secret { + if len(listener.TLS.CertificateRefs) == 0 { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + "Listener must have at least 1 TLS certificate ref", + ) + return nil + } + + secrets := make([]*v1.Secret, 0) + for _, certificateRef := range listener.TLS.CertificateRefs { + // TODO zhaohuabing: reuse validateSecretRef + if certificateRef.Group != nil && string(*certificateRef.Group) != "" { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + "Listener's TLS certificate ref group must be unspecified/empty.", + ) + break + } + + if certificateRef.Kind != nil && string(*certificateRef.Kind) != KindSecret { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Listener's TLS certificate ref kind must be %s.", KindSecret), + ) + break + } + + secretNamespace := listener.gateway.Namespace + + if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != "" && string(*certificateRef.Namespace) != listener.gateway.Namespace { + if !t.validateCrossNamespaceRef( + crossNamespaceFrom{ + group: gwapiv1.GroupName, + kind: KindGateway, + namespace: listener.gateway.Namespace, + }, + crossNamespaceTo{ + group: "", + kind: KindSecret, + namespace: string(*certificateRef.Namespace), + name: string(certificateRef.Name), + }, + resources.ReferenceGrants, + ) { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonRefNotPermitted, + fmt.Sprintf("Certificate ref to secret %s/%s not permitted by any ReferenceGrant.", *certificateRef.Namespace, certificateRef.Name), + ) + break + } + + secretNamespace = string(*certificateRef.Namespace) + } + + secret := resources.GetSecret(secretNamespace, string(certificateRef.Name)) + if secret == nil { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s does not exist.", listener.gateway.Namespace, certificateRef.Name), + ) + break + } + + if secret.Type != v1.SecretTypeTLS { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s must be of type %s.", listener.gateway.Namespace, certificateRef.Name, v1.SecretTypeTLS), + ) + break + } + + if len(secret.Data[v1.TLSCertKey]) == 0 || len(secret.Data[v1.TLSPrivateKeyKey]) == 0 { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s/%s must contain %s and %s.", listener.gateway.Namespace, certificateRef.Name, v1.TLSCertKey, v1.TLSPrivateKeyKey), + ) + break + } + + secrets = append(secrets, secret) + } + + err := validateTLSSecretsData(secrets, listener.Hostname) + if err != nil { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidCertificateRef, + fmt.Sprintf("Secret %s.", err.Error()), + ) + } + + return secrets +} + +func (t *Translator) validateTLSConfiguration(listener *ListenerContext, resources *Resources) { + switch listener.Protocol { + case gwapiv1.HTTPProtocolType, gwapiv1.UDPProtocolType, gwapiv1.TCPProtocolType: + if listener.TLS != nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + fmt.Sprintf("Listener must not have TLS set when protocol is %s.", listener.Protocol), + ) + } + case gwapiv1.HTTPSProtocolType: + if listener.TLS == nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), + ) + break + } + + if listener.TLS.Mode != nil && *listener.TLS.Mode != gwapiv1.TLSModeTerminate { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + "UnsupportedTLSMode", + fmt.Sprintf("TLS %s mode is not supported, TLS mode must be Terminate.", *listener.TLS.Mode), + ) + break + } + + secrets := t.validateTerminateModeAndGetTLSSecrets(listener, resources) + listener.SetTLSSecrets(secrets) + + case gwapiv1.TLSProtocolType: + if listener.TLS == nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + fmt.Sprintf("Listener must have TLS set when protocol is %s.", listener.Protocol), + ) + break + } + + if listener.TLS.Mode != nil && *listener.TLS.Mode == gwapiv1.TLSModePassthrough { + if len(listener.TLS.CertificateRefs) > 0 { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + "Listener must not have TLS certificate refs set for TLS mode Passthrough.", + ) + break + } + } + + if listener.TLS.Mode != nil && *listener.TLS.Mode == gwapiv1.TLSModeTerminate { + if len(listener.TLS.CertificateRefs) == 0 { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + "Listener must have TLS certificate refs set for TLS mode Terminate.", + ) + break + } + secrets := t.validateTerminateModeAndGetTLSSecrets(listener, resources) + listener.SetTLSSecrets(secrets) + } + } +} + +func (t *Translator) validateHostName(listener *ListenerContext) { + if listener.Protocol == gwapiv1.UDPProtocolType || listener.Protocol == gwapiv1.TCPProtocolType { + if listener.Hostname != nil { + listener.SetCondition( + gwapiv1.ListenerConditionProgrammed, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalid, + fmt.Sprintf("Listener must not have hostname set when protocol is %s.", listener.Protocol), + ) + } + } +} + +func (t *Translator) validateAllowedRoutes(listener *ListenerContext, routeKinds ...gwapiv1.Kind) { + canSupportKinds := make([]gwapiv1.RouteGroupKind, len(routeKinds)) + for i, routeKind := range routeKinds { + canSupportKinds[i] = gwapiv1.RouteGroupKind{Group: GroupPtr(gwapiv1.GroupName), Kind: routeKind} + } + if listener.AllowedRoutes == nil || len(listener.AllowedRoutes.Kinds) == 0 { + listener.SetSupportedKinds(canSupportKinds...) + return + } + + supportedRouteKinds := make([]gwapiv1.Kind, 0) + supportedKinds := make([]gwapiv1.RouteGroupKind, 0) + unSupportedKinds := make([]gwapiv1.RouteGroupKind, 0) + + for _, kind := range listener.AllowedRoutes.Kinds { + + // if there is a group it must match `gateway.networking.k8s.io` + if kind.Group != nil && string(*kind.Group) != gwapiv1.GroupName { + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidRouteKinds, + fmt.Sprintf("Group is not supported, group must be %s", gwapiv1.GroupName), + ) + continue + } + + found := false + for _, routeKind := range routeKinds { + if kind.Kind == routeKind { + supportedKinds = append(supportedKinds, kind) + supportedRouteKinds = append(supportedRouteKinds, kind.Kind) + found = true + break + } + } + + if !found { + unSupportedKinds = append(unSupportedKinds, kind) + } + } + + for _, kind := range unSupportedKinds { + var printRouteKinds []gwapiv1.Kind + if len(supportedKinds) == 0 { + printRouteKinds = routeKinds + } else { + printRouteKinds = supportedRouteKinds + } + listener.SetCondition( + gwapiv1.ListenerConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.ListenerReasonInvalidRouteKinds, + fmt.Sprintf("%s is not supported, kind must be one of %v", string(kind.Kind), printRouteKinds), + ) + } + + listener.SetSupportedKinds(supportedKinds...) +} + +type portListeners struct { + listeners []*ListenerContext + protocols sets.Set[string] + hostnames map[string]int +} + +// Port, protocol and hostname tuple should be unique across all listeners on merged Gateways. +func (t *Translator) validateConflictedMergedListeners(gateways []*GatewayContext) { + listenerSets := sets.Set[string]{} + for _, gateway := range gateways { + for _, listener := range gateway.listeners { + hostname := new(gwapiv1.Hostname) + if listener.Hostname != nil { + hostname = listener.Hostname + } + portProtocolHostname := fmt.Sprintf("%s:%s:%d", listener.Protocol, *hostname, listener.Port) + if listenerSets.Has(portProtocolHostname) { + listener.SetCondition( + gwapiv1.ListenerConditionConflicted, + metav1.ConditionTrue, + gwapiv1.ListenerReasonHostnameConflict, + "Port, protocol and hostname tuple must be unique for every listener", + ) + } + listenerSets.Insert(portProtocolHostname) + } + } +} + +func (t *Translator) validateConflictedLayer7Listeners(gateways []*GatewayContext) { + // Iterate through all layer-7 (HTTP, HTTPS, TLS) listeners and collect info about protocols + // and hostnames per port. + for _, gateway := range gateways { + portListenerInfo := map[gwapiv1.PortNumber]*portListeners{} + for _, listener := range gateway.listeners { + if listener.Protocol == gwapiv1.UDPProtocolType || listener.Protocol == gwapiv1.TCPProtocolType { + continue + } + if portListenerInfo[listener.Port] == nil { + portListenerInfo[listener.Port] = &portListeners{ + protocols: sets.Set[string]{}, + hostnames: map[string]int{}, + } + } + + portListenerInfo[listener.Port].listeners = append(portListenerInfo[listener.Port].listeners, listener) + + var protocol string + switch listener.Protocol { + // HTTPS and TLS can co-exist on the same port + case gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType: + protocol = "https/tls" + default: + protocol = string(listener.Protocol) + } + portListenerInfo[listener.Port].protocols.Insert(protocol) + + var hostname string + if listener.Hostname != nil { + hostname = string(*listener.Hostname) + } + + portListenerInfo[listener.Port].hostnames[hostname]++ + } + + // Set Conflicted conditions for any listeners with conflicting specs. + for _, info := range portListenerInfo { + for _, listener := range info.listeners { + if len(info.protocols) > 1 { + listener.SetCondition( + gwapiv1.ListenerConditionConflicted, + metav1.ConditionTrue, + gwapiv1.ListenerReasonProtocolConflict, + "All listeners for a given port must use a compatible protocol", + ) + } + + var hostname string + if listener.Hostname != nil { + hostname = string(*listener.Hostname) + } + + if info.hostnames[hostname] > 1 { + listener.SetCondition( + gwapiv1.ListenerConditionConflicted, + metav1.ConditionTrue, + gwapiv1.ListenerReasonHostnameConflict, + "All listeners for a given port must use a unique hostname", + ) + } + } + } + } +} + +func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocols ...gwapiv1.ProtocolType) { + // Iterate through all layer-4(TCP UDP) listeners and check if there are more than one listener on the same port + for _, gateway := range gateways { + portListenerInfo := map[gwapiv1.PortNumber]*portListeners{} + for _, listener := range gateway.listeners { + for _, protocol := range protocols { + if listener.Protocol == protocol { + if portListenerInfo[listener.Port] == nil { + portListenerInfo[listener.Port] = &portListeners{} + } + portListenerInfo[listener.Port].listeners = append(portListenerInfo[listener.Port].listeners, listener) + } + } + } + + // Leave the first one and set Conflicted conditions for all other listeners with conflicting specs. + for _, info := range portListenerInfo { + if len(info.listeners) > 1 { + for i := 1; i < len(info.listeners); i++ { + info.listeners[i].SetCondition( + gwapiv1.ListenerConditionConflicted, + metav1.ConditionTrue, + gwapiv1.ListenerReasonProtocolConflict, + fmt.Sprintf("Only one %s listener is allowed in a given port", strings.Join(protocolSliceToStringSlice(protocols), "/")), + ) + } + } + } + } +} + +func (t *Translator) validateCrossNamespaceRef(from crossNamespaceFrom, to crossNamespaceTo, referenceGrants []*gwapiv1b1.ReferenceGrant) bool { + for _, referenceGrant := range referenceGrants { + // The ReferenceGrant must be defined in the namespace of + // the "to" (the referent). + if referenceGrant.Namespace != to.namespace { + continue + } + + // Check if the ReferenceGrant has a matching "from". + var fromAllowed bool + for _, refGrantFrom := range referenceGrant.Spec.From { + if string(refGrantFrom.Namespace) == from.namespace && string(refGrantFrom.Group) == from.group && string(refGrantFrom.Kind) == from.kind { + fromAllowed = true + break + } + } + if !fromAllowed { + continue + } + + // Check if the ReferenceGrant has a matching "to". + var toAllowed bool + for _, refGrantTo := range referenceGrant.Spec.To { + if string(refGrantTo.Group) == to.group && string(refGrantTo.Kind) == to.kind && (refGrantTo.Name == nil || *refGrantTo.Name == "" || string(*refGrantTo.Name) == to.name) { + toAllowed = true + break + } + } + if !toAllowed { + continue + } + + // If we got here, both the "from" and the "to" were allowed by this + // reference grant. + return true + } + + // If we got here, no reference policy or reference grant allowed both the "from" and "to". + return false +} + +// Checks if a hostname is valid according to RFC 1123 and gateway API's requirement that it not be an IP address +func (t *Translator) validateHostname(hostname string) error { + if errs := validation.IsDNS1123Subdomain(hostname); errs != nil { + return fmt.Errorf("hostname %q is invalid: %v", hostname, errs) + } + + // IP addresses are not allowed so parsing the hostname as an address needs to fail + if _, err := netip.ParseAddr(hostname); err == nil { + return fmt.Errorf("hostname: %q cannot be an ip address", hostname) + } + + labelIdx := 0 + for i := range hostname { + if hostname[i] == '.' { + + if i-labelIdx > 63 { + return fmt.Errorf("label: %q in hostname %q cannot exceed 63 characters", hostname[labelIdx:i], hostname) + } + labelIdx = i + 1 + } + } + // Check the last label + if len(hostname)-labelIdx > 63 { + return fmt.Errorf("label: %q in hostname %q cannot exceed 63 characters", hostname[labelIdx:], hostname) + } + + return nil +} + +// validateSecretRef checks three things: +// 1. Does the secret reference have a valid Group and kind +// 2. If the secret reference is a cross-namespace reference, +// is it permitted by any ReferenceGrant +// 3. Does the secret exist +func (t *Translator) validateSecretRef( + allowCrossNamespace bool, + from crossNamespaceFrom, + secretObjRef gwapiv1b1.SecretObjectReference, + resources *Resources) (*v1.Secret, error) { + + if err := t.validateSecretObjectRef(allowCrossNamespace, from, secretObjRef, resources); err != nil { + return nil, err + } + + secretNamespace := from.namespace + if secretObjRef.Namespace != nil { + secretNamespace = string(*secretObjRef.Namespace) + } + secret := resources.GetSecret(secretNamespace, string(secretObjRef.Name)) + + if secret == nil { + return nil, fmt.Errorf( + "secret %s/%s does not exist", secretNamespace, secretObjRef.Name) + } + + return secret, nil +} + +func (t *Translator) validateConfigMapRef( + allowCrossNamespace bool, + from crossNamespaceFrom, + secretObjRef gwapiv1b1.SecretObjectReference, + resources *Resources) (*v1.ConfigMap, error) { + + if err := t.validateSecretObjectRef(allowCrossNamespace, from, secretObjRef, resources); err != nil { + return nil, err + } + + configMapNamespace := from.namespace + if secretObjRef.Namespace != nil { + configMapNamespace = string(*secretObjRef.Namespace) + } + configMap := resources.GetConfigMap(configMapNamespace, string(secretObjRef.Name)) + + if configMap == nil { + return nil, fmt.Errorf( + "configmap %s/%s does not exist", configMapNamespace, secretObjRef.Name) + } + + return configMap, nil +} + +func (t *Translator) validateSecretObjectRef( + allowCrossNamespace bool, + from crossNamespaceFrom, + secretRef gwapiv1b1.SecretObjectReference, + resources *Resources) error { + var kind string + if secretRef.Group != nil && string(*secretRef.Group) != "" { + return errors.New("secret ref group must be unspecified/empty") + } + + if secretRef.Kind == nil { // nolint + kind = KindSecret + } else if string(*secretRef.Kind) == KindSecret { + kind = KindSecret + } else if string(*secretRef.Kind) == KindConfigMap { + kind = KindConfigMap + } else { + return fmt.Errorf("secret ref kind must be %s", KindSecret) + } + + if secretRef.Namespace != nil && + string(*secretRef.Namespace) != "" && + string(*secretRef.Namespace) != from.namespace { + if !allowCrossNamespace { + return fmt.Errorf( + "secret ref namespace must be unspecified/empty or %s", + from.namespace) + } + + if !t.validateCrossNamespaceRef( + from, + crossNamespaceTo{ + group: "", + kind: kind, + namespace: string(*secretRef.Namespace), + name: string(secretRef.Name), + }, + resources.ReferenceGrants, + ) { + return fmt.Errorf( + "certificate ref to secret %s/%s not permitted by any ReferenceGrant", + *secretRef.Namespace, secretRef.Name) + } + + } + + return nil +} + +// TODO: zhaohuabing combine this function with the one in the route translator +// validateExtServiceBackendReference validates the backend reference for an +// external service referenced by an EG policy. +// This can also be used for the other external services deployed in the cluster, +// such as the external processing filter, gRPC Access Log Service, etc. +// It checks: +// 1. The group is nil or empty, indicating the core API group. +// 2. The kind is Service. +// 3. The port is specified. +// 4. The service exists and the specified port is found. +// 5. The cross-namespace reference is permitted by the ReferenceGrants if the +// namespace is different from the policy's namespace. +func (t *Translator) validateExtServiceBackendReference( + backendRef *gwapiv1.BackendObjectReference, + ownerNamespace string, + resources *Resources) error { + + // These are sanity checks, they should never happen because the API server + // should have caught them + if backendRef.Group != nil && *backendRef.Group != "" { + return errors.New( + "group is invalid, only the core API group (specified by omitting" + + " the group field or setting it to an empty string) is supported") + } + if backendRef.Kind != nil && *backendRef.Kind != KindService { + return errors.New("kind is invalid, only Service (specified by omitting " + + "the kind field or setting it to 'Service') is supported") + } + if backendRef.Port == nil { + return errors.New("a valid port number corresponding to a port on the Service must be specified") + } + + // check if the service is valid + serviceNamespace := NamespaceDerefOr(backendRef.Namespace, ownerNamespace) + service := resources.GetService(serviceNamespace, string(backendRef.Name)) + if service == nil { + return fmt.Errorf("service %s/%s not found", serviceNamespace, backendRef.Name) + } + var portFound bool + for _, port := range service.Spec.Ports { + portProtocol := port.Protocol + if port.Protocol == "" { // Default protocol is TCP + portProtocol = v1.ProtocolTCP + } + // currently only HTTP and GRPC are supported, both of which are TCP + if port.Port == int32(*backendRef.Port) && portProtocol == v1.ProtocolTCP { + portFound = true + break + } + } + + if !portFound { + return fmt.Errorf( + "TCP Port %d not found on service %s/%s", + *backendRef.Port, serviceNamespace, string(backendRef.Name), + ) + } + + // check if the cross-namespace reference is permitted + // if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && + // string(*backendRef.Namespace) != ownerNamespace { + // if !t.validateCrossNamespaceRef( + // crossNamespaceFrom{ + // group: egv1a1.GroupName, + // kind: KindSecurityPolicy, + // namespace: ownerNamespace, + // }, + // crossNamespaceTo{ + // group: GroupDerefOr(backendRef.Group, ""), + // kind: KindDerefOr(backendRef.Kind, KindService), + // namespace: string(*backendRef.Namespace), + // name: string(backendRef.Name), + // }, + // resources.ReferenceGrants, + // ) { + // return fmt.Errorf( + // "backend ref to %s %s/%s not permitted by any ReferenceGrant", + // KindService, *backendRef.Namespace, backendRef.Name) + // } + // } + return nil +} diff --git a/adapter/internal/operator/gateway-api/version/version.go b/adapter/internal/operator/gateway-api/version/version.go new file mode 100644 index 0000000000..64440dd3ab --- /dev/null +++ b/adapter/internal/operator/gateway-api/version/version.go @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package version + +import ( + "encoding/json" + "fmt" + "io" + "runtime/debug" + "strings" + + "github.com/wso2/apk/adapter/internal/operator/gateway-api/v1alpha1" + "sigs.k8s.io/yaml" +) + +type Info struct { + EnvoyGatewayVersion string `json:"envoyGatewayVersion"` + GatewayAPIVersion string `json:"gatewayAPIVersion"` + EnvoyProxyVersion string `json:"envoyProxyVersion"` + EnforcerVersion string `json:"enforcerVersion"` + ShutdownManagerVersion string `json:"shutdownManagerVersion"` + GitCommitID string `json:"gitCommitID"` +} + +func Get() Info { + return Info{ + EnvoyGatewayVersion: envoyGatewayVersion, + GatewayAPIVersion: gatewayAPIVersion, + EnvoyProxyVersion: envoyProxyVersion, + EnforcerVersion: enforcerVersion, + ShutdownManagerVersion: shutdownManagerVersion, + GitCommitID: gitCommitID, + } +} + +var ( + envoyGatewayVersion string + gatewayAPIVersion string + envoyProxyVersion = strings.Split(v1alpha1.DefaultEnvoyProxyImage, ":")[1] + enforcerVersion = strings.Split(v1alpha1.DefaultEnforcerImage, ":")[1] + shutdownManagerVersion string + gitCommitID string +) + +func init() { + bi, ok := debug.ReadBuildInfo() + if ok { + for _, dep := range bi.Deps { + if dep.Path == "sigs.k8s.io/gateway-api" { + gatewayAPIVersion = dep.Version + } + } + } +} + +// Print shows the versions of the Envoy Gateway. +func Print(w io.Writer, format string) error { + v := Get() + switch format { + case "json": + if marshalled, err := json.MarshalIndent(v, "", " "); err == nil { + _, _ = fmt.Fprintln(w, string(marshalled)) + } + case "yaml": + if marshalled, err := yaml.Marshal(v); err == nil { + _, _ = fmt.Fprintln(w, string(marshalled)) + } + default: + _, _ = fmt.Fprintf(w, "ENVOY_GATEWAY_VERSION: %s\n", v.EnvoyGatewayVersion) + _, _ = fmt.Fprintf(w, "ENVOY_PROXY_VERSION: %s\n", v.EnvoyProxyVersion) + _, _ = fmt.Fprintf(w, "GATEWAYAPI_VERSION: %s\n", v.GatewayAPIVersion) + _, _ = fmt.Fprintf(w, "SHUTDOWN_MANAGER_VERSION: %s\n", v.ShutdownManagerVersion) + _, _ = fmt.Fprintf(w, "GIT_COMMIT_ID: %s\n", v.GitCommitID) + } + + return nil +} diff --git a/adapter/internal/operator/gateway-api/xds/cache/snapshotcache.go b/adapter/internal/operator/gateway-api/xds/cache/snapshotcache.go new file mode 100644 index 0000000000..c9b217ae5e --- /dev/null +++ b/adapter/internal/operator/gateway-api/xds/cache/snapshotcache.go @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +// This file contains code derived from Contour, +// https://github.com/projectcontour/contour +// from the source file +// https://github.com/projectcontour/contour/blob/main/internal/xds/v3/snapshotter.go +// and is provided here subject to the following: +// Copyright Project Contour Authors +// SPDX-License-Identifier: Apache-2.0 + +package cache + +import ( + "context" + "fmt" + "math" + "strconv" + "sync" + + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" + cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3" + serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3" + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/types" +) + +var Hash = cachev3.IDHash{} + +// SnapshotCacheWithCallbacks uses the go-control-plane SimpleCache to store snapshots of +// Envoy resources, sliced by Node ID so that we can do incremental xDS properly. +// It does this by also implementing callbacks to make sure that the cache is kept +// up to date for each new node. +// +// Having the cache also implement the callbacks is a little bit hacky, but it makes sure +// that all the required bookkeeping happens. +// TODO(youngnick): Talk to the go-control-plane maintainers and see if we can upstream +// this in a better way. +type SnapshotCacheWithCallbacks interface { + cachev3.SnapshotCache + serverv3.Callbacks + GenerateNewSnapshot(string, types.XdsResources) error +} + +type snapshotMap map[string]*cachev3.Snapshot + +type nodeInfoMap map[int64]*corev3.Node + +type snapshotCache struct { + cachev3.SnapshotCache + streamIDNodeInfo nodeInfoMap + snapshotVersion int64 + lastSnapshot snapshotMap + mu sync.Mutex +} + +// GenerateNewSnapshot takes a table of resources (the output from the IR->xDS +// translator) and updates the snapshot version. +func (s *snapshotCache) GenerateNewSnapshot(irKey string, resources types.XdsResources) error { + s.mu.Lock() + defer s.mu.Unlock() + + version := s.newSnapshotVersion() + + // Create a snapshot with all xDS resources. + snapshot, err := cachev3.NewSnapshot( + version, + resources, + ) + if err != nil { + return err + } + + s.lastSnapshot[irKey] = snapshot + + for _, node := range s.getNodeIDs(irKey) { + loggers.LoggerAPKOperator.Debugf("Generating a snapshot with Node %s", node) + err := s.SetSnapshot(context.TODO(), node, snapshot) + if err != nil { + return err + } + } + + return nil +} + +// newSnapshotVersion increments the current snapshotVersion +// and returns as a string. +func (s *snapshotCache) newSnapshotVersion() string { + // Reset the snapshotVersion if it ever hits max size. + if s.snapshotVersion == math.MaxInt64 { + s.snapshotVersion = 0 + } + + // Increment the snapshot version & return as string. + s.snapshotVersion++ + return strconv.FormatInt(s.snapshotVersion, 10) +} + +// NewSnapshotCache gives you a fresh SnapshotCache. +// It needs a logger that supports the go-control-plane +// required interface (Debugf, Infof, Warnf, and Errorf). +func NewSnapshotCache(ads bool) SnapshotCacheWithCallbacks { + // // Set up the nasty wrapper hack. + // wrappedLogger := logger.Sugar() + //todo(amali) check the logger passing + return &snapshotCache{ + SnapshotCache: cachev3.NewSnapshotCache(ads, &Hash, loggers.LoggerAPKOperator), + lastSnapshot: make(snapshotMap), + streamIDNodeInfo: make(nodeInfoMap), + } +} + +// getNodeIDs retrieves the node ids from the node info map whose +// cluster field matches the ir key +func (s *snapshotCache) getNodeIDs(irKey string) []string { + var nodeIDs []string + for _, node := range s.streamIDNodeInfo { + if node != nil && node.Cluster == irKey { + nodeIDs = append(nodeIDs, node.Id) + } + } + + return nodeIDs +} + +// OnStreamOpen and the other OnStream* functions implement the callbacks for the +// state-of-the-world stream types. +func (s *snapshotCache) OnStreamOpen(_ context.Context, streamID int64, _ string) error { + s.mu.Lock() + defer s.mu.Unlock() + s.streamIDNodeInfo[streamID] = nil + + return nil +} + +func (s *snapshotCache) OnStreamClosed(streamID int64, _ *corev3.Node) { + // TODO: something with the node? + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.streamIDNodeInfo, streamID) +} + +func (s *snapshotCache) OnStreamRequest(streamID int64, req *discoveryv3.DiscoveryRequest) error { + s.mu.Lock() + // We could do this a little earlier than the defer, since the last half of this func is only logging + // but that seemed like a premature optimization. + defer s.mu.Unlock() + + // It's possible that only the first discovery request will have a node ID set. + // We also need to save the node ID to the node list anyway. + // So check if we have a nodeID for this stream already, then set it if not. + if s.streamIDNodeInfo[streamID] == nil { + if req.Node.Id == "" { + return fmt.Errorf("couldn't get the node ID from the first discovery request on stream %d", streamID) + } + loggers.LoggerAPKOperator.Debugf("First discovery request on stream %d, got nodeID %s", streamID, req.Node.Id) + s.streamIDNodeInfo[streamID] = req.Node + } + nodeID := s.streamIDNodeInfo[streamID].Id + cluster := s.streamIDNodeInfo[streamID].Cluster + + var nodeVersion string + + var errorCode int32 + var errorMessage string + + // If no snapshot has been generated yet, we can't do anything, so don't mess with this request. + // go-control-plane will respond with an empty response, then send an update when a snapshot is generated. + if s.lastSnapshot[cluster] == nil { + return nil + } + + _, err := s.GetSnapshot(nodeID) + if err != nil { + err = s.SetSnapshot(context.TODO(), nodeID, s.lastSnapshot[cluster]) + if err != nil { + return err + } + } + + if req.Node != nil { + if bv := req.Node.GetUserAgentBuildVersion(); bv != nil && bv.Version != nil { + nodeVersion = fmt.Sprintf("v%d.%d.%d", bv.Version.MajorNumber, bv.Version.MinorNumber, bv.Version.Patch) + } + } + + loggers.LoggerAPKOperator.Debugf("Got a new request, version_info %s, response_nonce %s, nodeID %s, node_version %s", req.VersionInfo, req.ResponseNonce, nodeID, nodeVersion) + + if status := req.ErrorDetail; status != nil { + // if Envoy rejected the last update log the details here. + // TODO(youngnick): Handle NACK properly + errorCode = status.Code + errorMessage = status.Message + } + + loggers.LoggerAPKOperator.Debugf("handling v3 xDS resource request, version_info %s, response_nonce %s, nodeID %s, node_version %s, resource_names %v, type_url %s, errorCode %d, errorMessage %s", + req.VersionInfo, req.ResponseNonce, + nodeID, nodeVersion, req.ResourceNames, req.GetTypeUrl(), + errorCode, errorMessage) + + return nil +} + +func (s *snapshotCache) OnStreamResponse(_ context.Context, streamID int64, _ *discoveryv3.DiscoveryRequest, _ *discoveryv3.DiscoveryResponse) { + // No mutex lock required here because no writing to the cache. + node := s.streamIDNodeInfo[streamID] + if node == nil { + loggers.LoggerAPKOperator.Errorf("Tried to send a response to a node we haven't seen yet on stream %d", streamID) + } else { + loggers.LoggerAPKOperator.Debugf("Sending Response on stream %d to node %s", streamID, node.Id) + } +} + +// OnDeltaStreamOpen and the other OnDeltaStream*/OnStreamDelta* functions implement +// the callbacks for the incremental xDS versions. +// Yes, the different ordering in the name is part of the go-control-plane interface. +func (s *snapshotCache) OnDeltaStreamOpen(_ context.Context, streamID int64, _ string) error { + s.mu.Lock() + defer s.mu.Unlock() + // Ensure that we're adding the streamID to the Node ID list. + s.streamIDNodeInfo[streamID] = nil + + return nil +} + +func (s *snapshotCache) OnDeltaStreamClosed(streamID int64, _ *corev3.Node) { + // TODO: something with the node? + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.streamIDNodeInfo, streamID) +} + +func (s *snapshotCache) OnStreamDeltaRequest(streamID int64, req *discoveryv3.DeltaDiscoveryRequest) error { + s.mu.Lock() + // We could do this a little earlier than with a defer, since the last half of this func is logging + // but that seemed like a premature optimization. + defer s.mu.Unlock() + + var nodeVersion string + var errorCode int32 + var errorMessage string + // It's possible that only the first incremental discovery request will have a node ID set. + // We also need to save the node ID to the node list anyway. + // So check if we have a nodeID for this stream already, then set it if not. + node := s.streamIDNodeInfo[streamID] + if node == nil { + if req.Node.Id == "" { + return fmt.Errorf("couldn't get the node ID from the first incremental discovery request on stream %d", streamID) + } + loggers.LoggerAPKOperator.Debugf("First incremental discovery request on stream %d, got nodeID %s", streamID, req.Node.Id) + s.streamIDNodeInfo[streamID] = req.Node + } + nodeID := s.streamIDNodeInfo[streamID].Id + cluster := s.streamIDNodeInfo[streamID].Cluster + + // If no snapshot has been written into the snapshotCache yet, we can't do anything, so don't mess with + // this request. go-control-plane will respond with an empty response, then send an update when a + // snapshot is generated. + if s.lastSnapshot[cluster] == nil { + return nil + } + + _, err := s.GetSnapshot(nodeID) + if err != nil { + err = s.SetSnapshot(context.TODO(), nodeID, s.lastSnapshot[cluster]) + if err != nil { + return err + } + } + + if req.Node != nil { + if bv := req.Node.GetUserAgentBuildVersion(); bv != nil && bv.Version != nil { + nodeVersion = fmt.Sprintf("v%d.%d.%d", bv.Version.MajorNumber, bv.Version.MinorNumber, bv.Version.Patch) + } + } + + loggers.LoggerAPKOperator.Debugf("Got a new request, response_nonce %s, nodeID %s, node_version %s", + req.ResponseNonce, nodeID, nodeVersion) + if status := req.ErrorDetail; status != nil { + // if Envoy rejected the last update log the details here. + // TODO(youngnick): Handle NACK properly + errorCode = status.Code + errorMessage = status.Message + } + loggers.LoggerAPKOperator.Debugf("handling v3 xDS resource request, response_nonce %s, nodeID %s, node_version %s, resource_names_subscribe %v, resource_names_unsubscribe %v, type_url %s, errorCode %d, errorMessage %s", + req.ResponseNonce, + nodeID, nodeVersion, + req.ResourceNamesSubscribe, req.ResourceNamesUnsubscribe, + req.GetTypeUrl(), + errorCode, errorMessage) + + return nil +} + +func (s *snapshotCache) OnStreamDeltaResponse(streamID int64, _ *discoveryv3.DeltaDiscoveryRequest, _ *discoveryv3.DeltaDiscoveryResponse) { + // No mutex lock required here because no writing to the cache. + node := s.streamIDNodeInfo[streamID] + if node == nil { + loggers.LoggerAPKOperator.Errorf("Tried to send a response to a node we haven't seen yet on stream %d", streamID) + } else { + loggers.LoggerAPKOperator.Debugf("Sending Incremental Response on stream %d to node %s", streamID, node.Id) + } +} + +func (s *snapshotCache) OnFetchRequest(_ context.Context, _ *discoveryv3.DiscoveryRequest) error { + return nil +} + +func (s *snapshotCache) OnFetchResponse(_ *discoveryv3.DiscoveryRequest, _ *discoveryv3.DiscoveryResponse) { +} diff --git a/adapter/internal/operator/gateway-api/xds/filters/wellknown.go b/adapter/internal/operator/gateway-api/xds/filters/wellknown.go new file mode 100644 index 0000000000..215f0769b4 --- /dev/null +++ b/adapter/internal/operator/gateway-api/xds/filters/wellknown.go @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package filters + +import ( + grpcstats "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" + grpcweb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3" + httprouter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" + hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "github.com/wso2/apk/adapter/pkg/utils/protocov" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var ( + GRPCWeb = &hcm.HttpFilter{ + Name: wellknown.GRPCWeb, + ConfigType: &hcm.HttpFilter_TypedConfig{ + TypedConfig: protocov.ToAny(&grpcweb.GrpcWeb{}), + }, + } + GRPCStats = &hcm.HttpFilter{ + Name: wellknown.HTTPGRPCStats, + ConfigType: &hcm.HttpFilter_TypedConfig{ + TypedConfig: protocov.ToAny(&grpcstats.FilterConfig{ + EmitFilterState: true, + PerMethodStatSpecifier: &grpcstats.FilterConfig_StatsForAllMethods{ + StatsForAllMethods: &wrapperspb.BoolValue{Value: true}, + }, + }), + }, + } +) + +func GenerateRouterFilter(enableEnvoyHeaders bool) *hcm.HttpFilter { + return &hcm.HttpFilter{ + Name: wellknown.Router, + ConfigType: &hcm.HttpFilter_TypedConfig{ + TypedConfig: protocov.ToAny(&httprouter.Router{ + SuppressEnvoyHeaders: !enableEnvoyHeaders, + }), + }, + } +} diff --git a/adapter/internal/operator/gateway-api/xds/runner/runner.go b/adapter/internal/operator/gateway-api/xds/runner/runner.go new file mode 100644 index 0000000000..b782074447 --- /dev/null +++ b/adapter/internal/operator/gateway-api/xds/runner/runner.go @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "time" + + "google.golang.org/grpc/keepalive" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3" + discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/service/route/v3" + runtimev3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" + secretv3 "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" + serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3" + "github.com/wso2/apk/adapter/config" + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/xds/cache" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/types" + "github.com/wso2/apk/adapter/pkg/utils/tlsutils" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +const ( + // XdsServerAddress is the listening address of the xds-server. + XdsServerAddress = "0.0.0.0" + // xdsTLSCertFilename is the fully qualified path of the file containing the + // xDS server TLS certificate. + // xdsTLSCertFilename = "/home/wso2/security/keystore/adapter.crt" + // // xdsTLSKeyFilename is the fully qualified path of the file containing the + // // xDS server TLS key. + // xdsTLSKeyFilename = "/home/wso2/security/keystore/adapter.key" + // xdsTLSCaFilename is the fully qualified path of the file containing the + // xDS server trusted CA certificate. + // xdsTLSCaFilename = "/home/wso2/security/truststore/adapter.crt" +) + +type Config struct { + Xds *message.Xds + grpc *grpc.Server + cache cache.SnapshotCacheWithCallbacks +} + +type Runner struct { + Config +} + +func New(cfg *Config) *Runner { + return &Runner{Config: *cfg} +} + +func (r *Runner) Name() string { + return string("xds-server") +} + +// Start starts the xds-server runner +func (r *Runner) Start(ctx context.Context) (err error) { + + // Set up the gRPC server and register the xDS handler. + // Create SnapshotCache before start subscribeAndTranslate, + // prevent panics in case cache is nil. + cfg := r.tlsConfig(tlsutils.GetKeyLocations()) + r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(cfg)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + MinTime: 15 * time.Second, + PermitWithoutStream: true, + })) + + r.cache = cache.NewSnapshotCache(true) + registerServer(serverv3.NewServer(ctx, r.cache, r.cache), r.grpc) + + // Start and listen xDS gRPC Server. + go r.serveXdsServer(ctx) + + // Start message Subscription. + go r.subscribeAndTranslate(ctx) + loggers.LoggerAPKOperator.Info("started xds runner") + return +} + +func (r *Runner) serveXdsServer(ctx context.Context) { + conf := config.ReadConfigs() + l, err := net.Listen("tcp", fmt.Sprintf(":%s", conf.Deployment.Gateway.AdapterXDSPort)) + if err != nil { + loggers.LoggerAPKOperator.Error(err, "failed to listen on port", "address", conf.Deployment.Gateway.AdapterXDSPort) + return + } + + go func() { + <-ctx.Done() + loggers.LoggerAPKOperator.Info("grpc server shutting down") + // We don't use GracefulStop here because envoy + // has long-lived hanging xDS requests. There's no + // mechanism to make those pending requests fail, + // so we forcibly terminate the TCP sessions. + r.grpc.Stop() + }() + + if err = r.grpc.Serve(l); err != nil { + loggers.LoggerAPKOperator.Error(err, "failed to start grpc based xds server") + } +} + +// registerServer registers the given xDS protocol Server with the gRPC +// runtime. +func registerServer(srv serverv3.Server, g *grpc.Server) { + // register services + discoveryv3.RegisterAggregatedDiscoveryServiceServer(g, srv) + secretv3.RegisterSecretDiscoveryServiceServer(g, srv) + clusterv3.RegisterClusterDiscoveryServiceServer(g, srv) + endpointv3.RegisterEndpointDiscoveryServiceServer(g, srv) + listenerv3.RegisterListenerDiscoveryServiceServer(g, srv) + routev3.RegisterRouteDiscoveryServiceServer(g, srv) + runtimev3.RegisterRuntimeDiscoveryServiceServer(g, srv) +} + +func (r *Runner) subscribeAndTranslate(ctx context.Context) { + // Subscribe to resources + message.HandleSubscription(message.Metadata{Runner: string("xds-server"), Message: "xds"}, r.Xds.Subscribe(ctx), + func(update message.Update[string, *types.ResourceVersionTable], errChan chan error) { + key := update.Key + val := update.Value + + loggers.LoggerAPKOperator.Info("Received an update in xds server") + var err error + if update.Delete { + err = r.cache.GenerateNewSnapshot(key, nil) + } else if val != nil && val.XdsResources != nil { + if r.cache == nil { + loggers.LoggerAPKOperator.Error("Failed to init snapshot cache ", err) + errChan <- err + } else { + // Update snapshot cache + err = r.cache.GenerateNewSnapshot(key, val.XdsResources) + } + } + if err != nil { + loggers.LoggerAPKOperator.Error("Failed to generate a snapshot ", err) + errChan <- err + } + }, + ) + + loggers.LoggerAPKOperator.Info("subscriber shutting down") +} + +func (r *Runner) tlsConfig(cert, key, ca string) *tls.Config { + extCert, err := tlsutils.GetServerCertificate(cert, key) + if err != nil { + loggers.LoggerAPKOperator.Error("failed to parse CA certificate") + return nil + } + caCertPool := tlsutils.GetTrustedCertPool(ca) + return &tls.Config{ + Certificates: []tls.Certificate{extCert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: caCertPool, + MinVersion: tls.VersionTLS13, + } +} diff --git a/adapter/internal/operator/gateway-api/xds/runner/runner_test.go b/adapter/internal/operator/gateway-api/xds/runner/runner_test.go new file mode 100644 index 0000000000..ea2929d53b --- /dev/null +++ b/adapter/internal/operator/gateway-api/xds/runner/runner_test.go @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "net" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/wso2/apk/adapter/config" +) + +// func TestTLSConfig(t *testing.T) { +// // Create trusted CA, server and client certs. +// trustedCACert := certyaml.Certificate{ +// Subject: "cn=trusted-ca", +// } +// egCertBeforeRotation := certyaml.Certificate{ +// Subject: "cn=eg-before-rotation", +// SubjectAltNames: []string{"DNS:localhost"}, +// Issuer: &trustedCACert, +// } +// egCertAfterRotation := certyaml.Certificate{ +// Subject: "cn=eg-after-rotation", +// SubjectAltNames: []string{"DNS:localhost"}, +// Issuer: &trustedCACert, +// } +// trustedEnvoyCert := certyaml.Certificate{ +// Subject: "cn=trusted-envoy", +// Issuer: &trustedCACert, +// } + +// // Create another CA and a client cert to test that untrusted clients are denied. +// untrustedCACert := certyaml.Certificate{ +// Subject: "cn=untrusted-ca", +// } +// untrustedClientCert := certyaml.Certificate{ +// Subject: "cn=untrusted-client", +// Issuer: &untrustedCACert, +// } + +// caCertPool := x509.NewCertPool() +// ca, err := trustedCACert.X509Certificate() +// require.NoError(t, err) +// caCertPool.AddCert(&ca) + +// tests := map[string]struct { +// serverCredentials *certyaml.Certificate +// clientCredentials *certyaml.Certificate +// expectError bool +// }{ +// "successful TLS connection established": { +// serverCredentials: &egCertBeforeRotation, +// clientCredentials: &trustedEnvoyCert, +// expectError: false, +// }, +// "rotating server credentials returns new server cert": { +// serverCredentials: &egCertAfterRotation, +// clientCredentials: &trustedEnvoyCert, +// expectError: false, +// }, +// "rotating server credentials again to ensure rotation can be repeated": { +// serverCredentials: &egCertBeforeRotation, +// clientCredentials: &trustedEnvoyCert, +// expectError: false, +// }, +// "fail to connect with client certificate which is not signed by correct CA": { +// serverCredentials: &egCertBeforeRotation, +// clientCredentials: &untrustedClientCert, +// expectError: true, +// }, +// } + +// // Create temporary directory to store certificates and key for the server. +// configDir, err := os.MkdirTemp("", "eg-testdata-") +// require.NoError(t, err) +// defer os.RemoveAll(configDir) + +// caFile := filepath.Join(configDir, "ca.crt") +// certFile := filepath.Join(configDir, "tls.crt") +// keyFile := filepath.Join(configDir, "tls.key") + +// // Initial set of credentials must be written into temp directory before +// // starting the tests to avoid error at server startup. +// err = trustedCACert.WritePEM(caFile, keyFile) +// require.NoError(t, err) +// err = egCertBeforeRotation.WritePEM(certFile, keyFile) +// require.NoError(t, err) + +// r := New(&Config{}) +// g := grpc.NewServer(grpc.Creds(credentials.NewTLS(r.tlsConfig(certFile, keyFile, caFile)))) +// if g == nil { +// t.Error("failed to create server") +// } + +// address := "localhost:8001" +// l, err := net.Listen("tcp", address) +// require.NoError(t, err) + +// go func() { +// err := g.Serve(l) +// require.NoError(t, err) +// }() +// defer g.GracefulStop() + +// for name, tc := range tests { +// tc := tc +// t.Run(name, func(t *testing.T) { +// // Store certificate and key to temp dir used by serveContext. +// err = tc.serverCredentials.WritePEM(certFile, keyFile) +// require.NoError(t, err) +// clientCert, _ := tc.clientCredentials.TLSCertificate() +// receivedCert, err := tryConnect(address, clientCert, caCertPool) +// gotError := err != nil +// if gotError != tc.expectError { +// t.Errorf("Unexpected result when connecting to the server: %s", err) +// } +// if err == nil { +// expectedCert, _ := tc.serverCredentials.X509Certificate() +// assert.Equal(t, &expectedCert, receivedCert) +// } +// }) +// } +// } + +// tryConnect tries to establish TLS connection to the server. +// If successful, return the server certificate. +func tryConnect(address string, clientCert tls.Certificate, caCertPool *x509.CertPool) (*x509.Certificate, error) { + clientConfig := &tls.Config{ + ServerName: "localhost", + MinVersion: tls.VersionTLS13, + Certificates: []tls.Certificate{clientCert}, + RootCAs: caCertPool, + } + conn, err := tls.Dial("tcp", address, clientConfig) + if err != nil { + return nil, err + } + defer conn.Close() + + err = peekError(conn) + if err != nil { + return nil, err + } + + return conn.ConnectionState().PeerCertificates[0], nil +} + +// peekError is a workaround for TLS 1.3: due to shortened handshake, TLS alert +// from server is received at first read from the socket. To receive alert for +// bad certificate, this function tries to read one byte. +// Adapted from https://golang.org/src/crypto/tls/handshake_client_test.go +func peekError(conn net.Conn) error { + _ = conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + _, err := conn.Read(make([]byte, 1)) + if err != nil { + var netErr net.Error + if !errors.As(netErr, &netErr) || !netErr.Timeout() { + return err + } + } + return nil +} + +func TestServeXdsServerListenFailed(t *testing.T) { + conf := config.ReadConfigs() + // Occupy the address to make listening failed + l, err := net.Listen("tcp", fmt.Sprintf(":%s", conf.Deployment.Gateway.AdapterXDSPort)) + require.NoError(t, err) + defer l.Close() + + r := New(&Config{}) + // Don't crash in this function + r.serveXdsServer(context.Background()) +} diff --git a/adapter/internal/operator/gateway-api/xds/utils/utils.go b/adapter/internal/operator/gateway-api/xds/utils/utils.go new file mode 100644 index 0000000000..e6f44d5c5a --- /dev/null +++ b/adapter/internal/operator/gateway-api/xds/utils/utils.go @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package utils + +import ( + "bytes" + + "github.com/envoyproxy/go-control-plane/pkg/cache/types" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "sigs.k8s.io/yaml" +) + +func MarshalResourcesToJSON(resources []types.Resource) ([]byte, error) { + msgs := make([]proto.Message, 0) + for _, resource := range resources { + msgs = append(msgs, resource.(proto.Message)) + } + var buffer bytes.Buffer + buffer.WriteByte('[') + for idx, msg := range msgs { + if idx != 0 { + buffer.WriteByte(',') + } + b, err := protojson.Marshal(msg) + if err != nil { + return nil, err + } + buffer.Write(b) + } + buffer.WriteByte(']') + return buffer.Bytes(), nil +} + +// ResourcesToYAMLString converts xDS Resource types into YAML string +func ResourcesToYAMLString(resources []types.Resource) (string, error) { + jsonBytes, err := MarshalResourcesToJSON(resources) + if err != nil { + return "", err + } + data, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + return "", err + } + return string(data), nil +} diff --git a/adapter/internal/operator/gateway-api/zz_generated.deepcopy.go b/adapter/internal/operator/gateway-api/zz_generated.deepcopy.go new file mode 100644 index 0000000000..6463a14e3d --- /dev/null +++ b/adapter/internal/operator/gateway-api/zz_generated.deepcopy.go @@ -0,0 +1,213 @@ +//go:build !ignore_autogenerated + +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package gatewayapi + +import ( + "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + if in.GatewayClass != nil { + in, out := &in.GatewayClass, &out.GatewayClass + *out = new(v1.GatewayClass) + (*in).DeepCopyInto(*out) + } + if in.Gateways != nil { + in, out := &in.Gateways, &out.Gateways + *out = make([]*v1.Gateway, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1.Gateway) + (*in).DeepCopyInto(*out) + } + } + } + if in.HTTPRoutes != nil { + in, out := &in.HTTPRoutes, &out.HTTPRoutes + *out = make([]*v1.HTTPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1.HTTPRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.GRPCRoutes != nil { + in, out := &in.GRPCRoutes, &out.GRPCRoutes + *out = make([]*v1alpha2.GRPCRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha2.GRPCRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.TLSRoutes != nil { + in, out := &in.TLSRoutes, &out.TLSRoutes + *out = make([]*v1alpha2.TLSRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha2.TLSRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.TCPRoutes != nil { + in, out := &in.TCPRoutes, &out.TCPRoutes + *out = make([]*v1alpha2.TCPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha2.TCPRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.UDPRoutes != nil { + in, out := &in.UDPRoutes, &out.UDPRoutes + *out = make([]*v1alpha2.UDPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha2.UDPRoute) + (*in).DeepCopyInto(*out) + } + } + } + if in.ReferenceGrants != nil { + in, out := &in.ReferenceGrants, &out.ReferenceGrants + *out = make([]*v1beta1.ReferenceGrant, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1beta1.ReferenceGrant) + (*in).DeepCopyInto(*out) + } + } + } + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]*corev1.Namespace, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1.Namespace) + (*in).DeepCopyInto(*out) + } + } + } + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]*corev1.Service, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1.Service) + (*in).DeepCopyInto(*out) + } + } + } + if in.Backends != nil { + in, out := &in.Backends, &out.Backends + *out = make([]*v1alpha1.Backend, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha1.Backend) + (*in).DeepCopyInto(*out) + } + } + } + if in.EndpointSlices != nil { + in, out := &in.EndpointSlices, &out.EndpointSlices + *out = make([]*discoveryv1.EndpointSlice, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(discoveryv1.EndpointSlice) + (*in).DeepCopyInto(*out) + } + } + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]*corev1.Secret, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1.Secret) + (*in).DeepCopyInto(*out) + } + } + } + if in.ConfigMaps != nil { + in, out := &in.ConfigMaps, &out.ConfigMaps + *out = make([]*corev1.ConfigMap, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1.ConfigMap) + (*in).DeepCopyInto(*out) + } + } + } + if in.ExtensionRefFilters != nil { + in, out := &in.ExtensionRefFilters, &out.ExtensionRefFilters + *out = make([]unstructured.Unstructured, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.BackendTLSPolicies != nil { + in, out := &in.BackendTLSPolicies, &out.BackendTLSPolicies + *out = make([]*v1alpha2.BackendTLSPolicy, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1alpha2.BackendTLSPolicy) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} diff --git a/adapter/internal/operator/infrastructure/runner/runner.go b/adapter/internal/operator/infrastructure/runner/runner.go new file mode 100644 index 0000000000..f4f9b4010a --- /dev/null +++ b/adapter/internal/operator/infrastructure/runner/runner.go @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/infrastructure" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + "github.com/wso2/apk/adapter/internal/operator/message" +) + +type Config struct { + InfraIR *message.InfraIR +} + +type Runner struct { + Config + mgr infrastructure.Manager +} + +func (r *Runner) Name() string { + return string("infrastructure") +} + +func New(cfg *Config) *Runner { + return &Runner{Config: *cfg} +} + +// Start starts the infrastructure runner +func (r *Runner) Start(ctx context.Context) (err error) { + + r.mgr, err = infrastructure.NewManager() + if err != nil { + loggers.LoggerAPKOperator.Error(err, "failed to create new manager") + return err + } + go r.subscribeToProxyInfraIR(ctx) + + // Enable global ratelimit if it has been configured. + // if r.EnvoyGateway.RateLimit != nil { + // go r.enableRateLimitInfra(ctx) + // } + + loggers.LoggerAPKOperator.Info("started") + return +} + +func (r *Runner) subscribeToProxyInfraIR(ctx context.Context) { + // Subscribe to resources + message.HandleSubscription(message.Metadata{Runner: string("infrastructure"), Message: "infra-ir"}, r.InfraIR.Subscribe(ctx), + func(update message.Update[string, *ir.Infra], errChan chan error) { + loggers.LoggerAPKOperator.Info("Received an update in infrastructure provider ...") + val := update.Value + + if update.Delete { + if err := r.mgr.DeleteProxyInfra(ctx, val); err != nil { + loggers.LoggerAPKOperator.Error(err, "failed to delete infra") + errChan <- err + } + } else { + // Manage the proxy infra. + if len(val.Proxy.Listeners) == 0 { + loggers.LoggerAPKOperator.Info("Infra IR was updated, but no listeners were found. Skipping infra creation.") + return + } + + if err := r.mgr.CreateOrUpdateProxyInfra(ctx, val); err != nil { + loggers.LoggerAPKOperator.Error("Failed to create new infra ", err) + errChan <- err + } + } + }, + ) + loggers.LoggerAPKOperator.Info("infra subscriber shutting down") +} + +// func (r *Runner) enableRateLimitInfra(ctx context.Context) { +// if err := r.mgr.CreateOrUpdateRateLimitInfra(ctx); err != nil { +// loggers.LoggerAPKOperator.Error(err, "failed to create ratelimit infra") +// } +// } diff --git a/adapter/internal/operator/message/types.go b/adapter/internal/operator/message/types.go new file mode 100644 index 0000000000..a5e22562cd --- /dev/null +++ b/adapter/internal/operator/message/types.go @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package message + +import ( + "github.com/telepresenceio/watchable" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/gateway-api/ir" + xdstypes "github.com/wso2/apk/adapter/internal/types" + "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// ProviderResources message +type ProviderResources struct { + // GatewayAPIResources is a map from a GatewayClass name to + // a group of gateway API and other related resources. + GatewayAPIResources watchable.Map[string, *gatewayapi.ControllerResources] + + // GatewayAPIStatuses is a group of gateway api + // resource statuses maps. + GatewayAPIStatuses +} + +func (p *ProviderResources) GetResources() []*gatewayapi.Resources { + if p.GatewayAPIResources.Len() == 0 { + return nil + } + + for _, v := range p.GatewayAPIResources.LoadAll() { + return *v + } + + return nil +} + +func (p *ProviderResources) GetResourcesByGatewayClass(name string) *gatewayapi.Resources { + for _, r := range p.GetResources() { + if r != nil && r.GatewayClass != nil && r.GatewayClass.Name == name { + return r + } + } + + return nil +} + +func (p *ProviderResources) GetResourcesKey() string { + if p.GatewayAPIResources.Len() == 0 { + return "" + } + for k := range p.GatewayAPIResources.LoadAll() { + return k + } + return "" +} + +func (p *ProviderResources) Close() { + p.GatewayAPIResources.Close() + p.GatewayAPIStatuses.Close() +} + +// GatewayAPIStatuses contains gateway API resources statuses +type GatewayAPIStatuses struct { + GatewayStatuses watchable.Map[types.NamespacedName, *gwapiv1.GatewayStatus] + HTTPRouteStatuses watchable.Map[types.NamespacedName, *gwapiv1.HTTPRouteStatus] +} + +func (s *GatewayAPIStatuses) Close() { + s.GatewayStatuses.Close() + s.HTTPRouteStatuses.Close() +} + +// XdsIR message +type XdsIR struct { + watchable.Map[string, *ir.Xds] +} + +// InfraIR message +type InfraIR struct { + watchable.Map[string, *ir.Infra] +} + +// Xds message +type Xds struct { + watchable.Map[string, *xdstypes.ResourceVersionTable] +} diff --git a/adapter/internal/operator/message/watchutil.go b/adapter/internal/operator/message/watchutil.go new file mode 100644 index 0000000000..f72ed19275 --- /dev/null +++ b/adapter/internal/operator/message/watchutil.go @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package message + +import ( + "github.com/telepresenceio/watchable" + "github.com/wso2/apk/adapter/internal/loggers" +) + +type Update[K comparable, V any] watchable.Update[K, V] + +type Metadata struct { + Runner string + Message string +} + +// HandleSubscription takes a channel returned by +// watchable.Map.Subscribe() (or .SubscribeSubset()), and calls the +// given function for each initial value in the map, and for any +// updates. +// +// This is better than simply iterating over snapshot.Updates because +// it handles the case where the watchable.Map already contains +// entries before .Subscribe is called. +func HandleSubscription[K comparable, V any]( + meta Metadata, + subscription <-chan watchable.Snapshot[K, V], + handle func(updateFunc Update[K, V], errChans chan error), +) { + //TODO: find a suitable value + errChans := make(chan error, 10) + go func() { + for err := range errChans { + loggers.LoggerAPKOperator.Error(err, "observed an error") + } + }() + + if snapshot, ok := <-subscription; ok { + for k, v := range snapshot.State { + handle(Update[K, V]{ + Key: k, + Value: v, + }, errChans) + } + } + for snapshot := range subscription { + for _, update := range snapshot.Updates { + handle(Update[K, V](update), errChans) + } + } +} diff --git a/adapter/internal/operator/message/watchutil_test.go b/adapter/internal/operator/message/watchutil_test.go new file mode 100644 index 0000000000..2206b25d41 --- /dev/null +++ b/adapter/internal/operator/message/watchutil_test.go @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package message_test + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/telepresenceio/watchable" + "github.com/wso2/apk/adapter/internal/operator/message" +) + +func TestHandleSubscriptionAlreadyClosed(t *testing.T) { + ch := make(chan watchable.Snapshot[string, any]) + close(ch) + + var calls int + message.HandleSubscription[string, any]( + message.Metadata{Runner: "demo", Message: "demo"}, + ch, + func(update message.Update[string, any], errChans chan error) { calls++ }, + ) + assert.Equal(t, 0, calls) +} + +func TestHandleSubscriptionAlreadyInitialized(t *testing.T) { + var m watchable.Map[string, any] + m.Store("foo", "bar") + + endCtx, end := context.WithCancel(context.Background()) + go func() { + <-endCtx.Done() + m.Store("baz", "qux") + m.Delete("qux") // no-op + m.Store("foo", "bar") // no-op + m.Delete("baz") + time.Sleep(100 * time.Millisecond) + m.Close() + }() + + var storeCalls int + var deleteCalls int + message.HandleSubscription[string, any]( + message.Metadata{Runner: "demo", Message: "demo"}, + m.Subscribe(context.Background()), + func(update message.Update[string, any], errChans chan error) { + end() + if update.Delete { + deleteCalls++ + } else { + storeCalls++ + } + }, + ) + assert.Equal(t, 2, storeCalls) + assert.Equal(t, 1, deleteCalls) +} diff --git a/adapter/internal/operator/provider-resources/runner/runner.go b/adapter/internal/operator/provider-resources/runner/runner.go new file mode 100644 index 0000000000..78982e9c67 --- /dev/null +++ b/adapter/internal/operator/provider-resources/runner/runner.go @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + + "github.com/wso2/apk/adapter/internal/loggers" + gatewayapi "github.com/wso2/apk/adapter/internal/operator/gateway-api" + "github.com/wso2/apk/adapter/internal/operator/message" + "github.com/wso2/apk/adapter/internal/operator/utils" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +type Config struct { + ProviderResources *message.ProviderResources + XdsIR *message.XdsIR + InfraIR *message.InfraIR +} + +type Runner struct { + Config +} + +func New(cfg *Config) *Runner { + return &Runner{Config: *cfg} +} + +func (r *Runner) Name() string { + return "gateway-api" +} + +// Start starts the gateway-api translator runner +func (r *Runner) Start(ctx context.Context) (err error) { + go r.subscribeAndTranslate(ctx) + return +} + +func (r *Runner) subscribeAndTranslate(ctx context.Context) { + message.HandleSubscription(message.Metadata{Runner: "gateway-api", Message: "provider-resources"}, r.ProviderResources.GatewayAPIResources.Subscribe(ctx), + func(update message.Update[string, *gatewayapi.ControllerResources], errChan chan error) { + val := update.Value + loggers.LoggerAPKOperator.Info("Received an update in provider resources ...") + // There is only 1 key which is the controller name + // so when a delete is triggered, delete all IR keys + if update.Delete || val == nil { + r.deleteAllIRKeys() + r.deleteAllStatusKeys() + return + } + + // IR keys for watchable + var curIRKeys, newIRKeys []string + + // Get current IR keys + for key := range r.InfraIR.LoadAll() { + curIRKeys = append(curIRKeys, key) + } + + // Get all status keys from watchable and save them in this StatusesToDelete structure. + // Iterating through the controller resources, any valid keys will be removed from statusesToDelete. + // Remaining keys will be deleted from watchable before we exit this function. + statusesToDelete := r.getAllStatuses() + + for _, resources := range *val { + // Translate and publish IRs. + t := &gatewayapi.Translator{ + GatewayControllerName: "wso2.com/apk-gateway-default", + GatewayClassName: v1.ObjectName(resources.GatewayClass.Name), + } + + // Translate to IR + result := t.Translate(resources) + + // Publish the IRs. + // Also validate the ir before sending it. + for key, val := range result.InfraIR { + if err := val.Validate(); err != nil { + loggers.LoggerAPKOperator.Error(err, "unable to validate infra ir, skipped sending it") + errChan <- err + } else { + r.InfraIR.Store(key, val) + newIRKeys = append(newIRKeys, key) + } + } + + for key, val := range result.XdsIR { + if err := val.Validate(); err != nil { + loggers.LoggerAPKOperator.Error(err, "unable to validate xds ir, skipped sending it") + errChan <- err + } else { + r.XdsIR.Store(key, val) + } + } + + // Update Status + for _, gateway := range result.Gateways { + gateway := gateway + key := utils.NamespacedName(gateway) + r.ProviderResources.GatewayStatuses.Store(key, &gateway.Status) + delete(statusesToDelete.GatewayStatusKeys, key) + } + for _, httpRoute := range result.HTTPRoutes { + httpRoute := httpRoute + key := utils.NamespacedName(httpRoute) + r.ProviderResources.HTTPRouteStatuses.Store(key, &httpRoute.Status) + delete(statusesToDelete.HTTPRouteStatusKeys, key) + } + // for _, grpcRoute := range result.GRPCRoutes { + // grpcRoute := grpcRoute + // key := utils.NamespacedName(grpcRoute) + // r.ProviderResources.GRPCRouteStatuses.Store(key, &grpcRoute.Status) + // delete(statusesToDelete.GRPCRouteStatusKeys, key) + // } + // for _, tlsRoute := range result.TLSRoutes { + // tlsRoute := tlsRoute + // key := utils.NamespacedName(tlsRoute) + // r.ProviderResources.TLSRouteStatuses.Store(key, &tlsRoute.Status) + // delete(statusesToDelete.TLSRouteStatusKeys, key) + // } + // for _, tcpRoute := range result.TCPRoutes { + // tcpRoute := tcpRoute + // key := utils.NamespacedName(tcpRoute) + // r.ProviderResources.TCPRouteStatuses.Store(key, &tcpRoute.Status) + // delete(statusesToDelete.TCPRouteStatusKeys, key) + // } + // for _, udpRoute := range result.UDPRoutes { + // udpRoute := udpRoute + // key := utils.NamespacedName(udpRoute) + // r.ProviderResources.UDPRouteStatuses.Store(key, &udpRoute.Status) + // delete(statusesToDelete.UDPRouteStatusKeys, key) + // } + + // // Skip updating status for policies with empty status + // // They may have been skipped in this translation because + // // their target is not found (not relevant) + + // for _, backendTLSPolicy := range result.BackendTLSPolicies { + // backendTLSPolicy := backendTLSPolicy + // key := utils.NamespacedName(backendTLSPolicy) + // if !(reflect.ValueOf(backendTLSPolicy.Status).IsZero()) { + // r.ProviderResources.BackendTLSPolicyStatuses.Store(key, &backendTLSPolicy.Status) + // } + // delete(statusesToDelete.BackendTLSPolicyStatusKeys, key) + // } + + } + + // Delete IR keys + // There is a 1:1 mapping between infra and xds IR keys + delKeys := getIRKeysToDelete(curIRKeys, newIRKeys) + for _, key := range delKeys { + r.InfraIR.Delete(key) + r.XdsIR.Delete(key) + } + + // Delete status keys + r.deleteStatusKeys(statusesToDelete) + }, + ) + loggers.LoggerAPKOperator.Info("shutting down") +} + +type StatusesToDelete struct { + GatewayStatusKeys map[types.NamespacedName]bool + HTTPRouteStatusKeys map[types.NamespacedName]bool +} + +func (r *Runner) getAllStatuses() *StatusesToDelete { + // Maps storing status keys to be deleted + ds := &StatusesToDelete{ + GatewayStatusKeys: make(map[types.NamespacedName]bool), + HTTPRouteStatusKeys: make(map[types.NamespacedName]bool), + } + + // Get current status keys + for key := range r.ProviderResources.GatewayStatuses.LoadAll() { + ds.GatewayStatusKeys[key] = true + } + for key := range r.ProviderResources.HTTPRouteStatuses.LoadAll() { + ds.HTTPRouteStatusKeys[key] = true + } + + return ds +} + +func (r *Runner) deleteStatusKeys(ds *StatusesToDelete) { + for key := range ds.GatewayStatusKeys { + r.ProviderResources.GatewayStatuses.Delete(key) + delete(ds.GatewayStatusKeys, key) + } + for key := range ds.HTTPRouteStatusKeys { + r.ProviderResources.HTTPRouteStatuses.Delete(key) + delete(ds.HTTPRouteStatusKeys, key) + } +} + +// deleteAllIRKeys deletes all XdsIR and InfraIR +func (r *Runner) deleteAllIRKeys() { + for key := range r.InfraIR.LoadAll() { + r.InfraIR.Delete(key) + r.XdsIR.Delete(key) + } +} + +// deleteAllStatusKeys deletes all status keys stored by the subscriber. +func (r *Runner) deleteAllStatusKeys() { + // Fields of GatewayAPIStatuses + for key := range r.ProviderResources.GatewayStatuses.LoadAll() { + r.ProviderResources.GatewayStatuses.Delete(key) + } + for key := range r.ProviderResources.HTTPRouteStatuses.LoadAll() { + r.ProviderResources.HTTPRouteStatuses.Delete(key) + } +} + +// getIRKeysToDelete returns the list of IR keys to delete +// based on the difference between the current keys and the +// new keys parameters passed to the function. +func getIRKeysToDelete(curKeys, newKeys []string) []string { + curSet := sets.NewString(curKeys...) + newSet := sets.NewString(newKeys...) + + delSet := curSet.Difference(newSet) + + return delSet.List() +} diff --git a/adapter/internal/operator/provider/runner/provider_runner.go b/adapter/internal/operator/provider/runner/provider_runner.go new file mode 100644 index 0000000000..d241b67e68 --- /dev/null +++ b/adapter/internal/operator/provider/runner/provider_runner.go @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package runner + +import ( + "context" + "fmt" + + "github.com/wso2/apk/adapter/internal/loggers" + provider "github.com/wso2/apk/adapter/internal/operator/gateway-api/provider/kubernetes" + "github.com/wso2/apk/adapter/internal/operator/message" + ctrl "sigs.k8s.io/controller-runtime" +) + +type Config struct { + ProviderResources *message.ProviderResources +} + +type Runner struct { + Config +} + +func New(cfg *Config) *Runner { + return &Runner{Config: *cfg} +} + +func (r *Runner) Name() string { + return string("provider") +} + +// Start the provider runner +func (r *Runner) Start(ctx context.Context) (err error) { + loggers.LoggerAPKOperator.Info("Started Runner Kubernetes operator...") + cfg, err := ctrl.GetConfig() + if err != nil { + return fmt.Errorf("failed to get kubeconfig: %w", err) + } + p, err := provider.New(cfg, r.ProviderResources) + if err != nil { + return fmt.Errorf("failed to create provider %s: %w", "Kubernetes", err) + } + go func() { + err := p.Start(ctx) + if err != nil { + loggers.LoggerAPKOperator.Error("Unable to start kubernetes operator provider", err) + } + }() + return nil +} diff --git a/adapter/internal/operator/status/conditions.go b/adapter/internal/operator/status/conditions.go new file mode 100644 index 0000000000..0ef2b69b3e --- /dev/null +++ b/adapter/internal/operator/status/conditions.go @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +// This file contains code derived from Contour, +// https://github.com/projectcontour/contour +// from the source file +// https://github.com/projectcontour/contour/blob/main/internal/status/gatewayclassconditions.go +// and is provided here subject to the following: +// Copyright Project Contour Authors +// SPDX-License-Identifier: Apache-2.0 + +package status + +import ( + "fmt" + "time" + "unicode" + + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +const ( + ReasonOlderGatewayClassExists gwapiv1.GatewayClassConditionReason = "OlderGatewayClassExists" + + MsgOlderGatewayClassExists = "Invalid GatewayClass: another older GatewayClass with the same Spec.Controller exists" + MsgValidGatewayClass = "Valid GatewayClass" + MsgGatewayClassInvalidParams = "Invalid parametersRef" +) + +// computeGatewayClassAcceptedCondition computes the GatewayClass Accepted status condition. +func computeGatewayClassAcceptedCondition(gatewayClass *gwapiv1.GatewayClass, + accepted bool, + reason, msg string) metav1.Condition { + switch accepted { + case true: + return metav1.Condition{ + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), + Status: metav1.ConditionTrue, + Reason: reason, + Message: msg, + ObservedGeneration: gatewayClass.Generation, + LastTransitionTime: metav1.NewTime(time.Now()), + } + default: + return metav1.Condition{ + Type: string(gwapiv1.GatewayClassConditionStatusAccepted), + Status: metav1.ConditionFalse, + Reason: reason, + Message: msg, + ObservedGeneration: gatewayClass.Generation, + LastTransitionTime: metav1.NewTime(time.Now()), + } + } +} + +// computeGatewayAcceptedCondition computes the Gateway Accepted status condition. +func computeGatewayAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) metav1.Condition { + switch accepted { + case true: + return newCondition(string(gwapiv1.GatewayReasonAccepted), metav1.ConditionTrue, + string(gwapiv1.GatewayReasonAccepted), + "The Gateway has been scheduled by Envoy Gateway", time.Now(), gw.Generation) + default: + return newCondition(string(gwapiv1.GatewayReasonAccepted), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonAccepted), + "The Gateway has not been scheduled by Envoy Gateway", time.Now(), gw.Generation) + } +} + +// computeGatewayProgrammedCondition computes the Gateway Programmed status condition. +// Programmed condition surfaces true when the Envoy Deployment status is ready. +func computeGatewayProgrammedCondition(gw *gwapiv1.Gateway, deployment *appsv1.Deployment) metav1.Condition { + if len(gw.Status.Addresses) == 0 { + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonAddressNotAssigned), + "No addresses have been assigned to the Gateway", time.Now(), gw.Generation) + } + + // If there are no available replicas for the Envoy Deployment, don't + // mark the Gateway as ready yet. + + if deployment == nil || deployment.Status.AvailableReplicas == 0 { + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, + string(gwapiv1.GatewayReasonNoResources), + "Deployment replicas unavailable", time.Now(), gw.Generation) + } + + message := fmt.Sprintf("Address assigned to the Gateway, %d/%d envoy Deployment replicas available", + deployment.Status.AvailableReplicas, deployment.Status.Replicas) + return newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionTrue, + string(gwapiv1.GatewayConditionProgrammed), message, time.Now(), gw.Generation) +} + +// MergeConditions adds or updates matching conditions, and updates the transition +// time if details of a condition have changed. Returns the updated condition array. +func MergeConditions(conditions []metav1.Condition, updates ...metav1.Condition) []metav1.Condition { + var additions []metav1.Condition + for i, update := range updates { + add := true + for j, cond := range conditions { + if cond.Type == update.Type { + add = false + if conditionChanged(cond, update) { + conditions[j].Status = update.Status + conditions[j].Reason = update.Reason + conditions[j].Message = update.Message + conditions[j].ObservedGeneration = update.ObservedGeneration + conditions[j].LastTransitionTime = update.LastTransitionTime + break + } + } + } + if add { + additions = append(additions, updates[i]) + } + } + conditions = append(conditions, additions...) + return conditions +} + +func newCondition(t string, status metav1.ConditionStatus, reason, msg string, lt time.Time, og int64) metav1.Condition { + return metav1.Condition{ + Type: t, + Status: status, + Reason: reason, + Message: msg, + LastTransitionTime: metav1.NewTime(lt), + ObservedGeneration: og, + } +} + +func conditionChanged(a, b metav1.Condition) bool { + return (a.Status != b.Status) || + (a.Reason != b.Reason) || + (a.Message != b.Message) || + (a.ObservedGeneration != b.ObservedGeneration) +} + +// Error2ConditionMsg format the error string to a Status condition message. +// * Convert the first letter to capital +// * Append "." to the string if it doesn't exit +func Error2ConditionMsg(err error) string { + if err == nil { + return "" + } + + message := err.Error() + if message == "" { + return message + } + + // Convert the string to a rune slice for easier manipulation + runes := []rune(message) + + // Check if the first rune is a letter and convert it to uppercase + if unicode.IsLetter(runes[0]) { + runes[0] = unicode.ToUpper(runes[0]) + } + + // Convert the rune slice back to a string + return string(runes) +} diff --git a/adapter/internal/operator/status/gateway.go b/adapter/internal/operator/status/gateway.go new file mode 100644 index 0000000000..475cf07725 --- /dev/null +++ b/adapter/internal/operator/status/gateway.go @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package status + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// UpdateGatewayStatusAcceptedCondition updates the status condition for the provided Gateway based on the accepted state. +func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) *gwapiv1.Gateway { + gw.Status.Conditions = MergeConditions(gw.Status.Conditions, computeGatewayAcceptedCondition(gw, accepted)) + return gw +} + +// UpdateGatewayStatusProgrammedCondition updates the status addresses for the provided gateway +// based on the status IP/Hostname of svc and updates the Programmed condition based on the +// service and deployment state. +func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1.Gateway, svc *corev1.Service, deployment *appsv1.Deployment, nodeAddresses ...string) { + var addresses, hostnames []string + // Update the status addresses field. + if svc != nil { + // If the addresses is explicitly set in the Gateway spec by the user, use it + // to populate the Status + if len(gw.Spec.Addresses) > 0 { + // Make sure the addresses have been populated into ExternalIPs/ClusterIPs + // and use that value + if len(svc.Spec.ExternalIPs) > 0 { + addresses = append(addresses, svc.Spec.ExternalIPs...) + } else if len(svc.Spec.ClusterIPs) > 0 { + addresses = append(addresses, svc.Spec.ClusterIPs...) + } + } else { + if svc.Spec.Type == corev1.ServiceTypeLoadBalancer { + for i := range svc.Status.LoadBalancer.Ingress { + switch { + case len(svc.Status.LoadBalancer.Ingress[i].IP) > 0: + addresses = append(addresses, svc.Status.LoadBalancer.Ingress[i].IP) + case len(svc.Status.LoadBalancer.Ingress[i].Hostname) > 0: + // Remove when the following supports the hostname address type: + // https://github.com/kubernetes-sigs/gateway-api/blob/v0.5.0/conformance/utils/kubernetes/helpers.go#L201-L207 + if svc.Status.LoadBalancer.Ingress[i].Hostname == "localhost" { + addresses = append(addresses, "0.0.0.0") + } + hostnames = append(hostnames, svc.Status.LoadBalancer.Ingress[i].Hostname) + } + } + } + + if svc.Spec.Type == corev1.ServiceTypeClusterIP { + for i := range svc.Spec.ClusterIPs { + if svc.Spec.ClusterIPs[i] != "" { + addresses = append(addresses, svc.Spec.ClusterIPs[i]) + } + } + } + + if svc.Spec.Type == corev1.ServiceTypeNodePort { + addresses = nodeAddresses + } + } + + var gwAddresses []gwapiv1.GatewayStatusAddress + for i := range addresses { + addr := gwapiv1.GatewayStatusAddress{ + Type: ptr.To(gwapiv1.IPAddressType), + Value: addresses[i], + } + gwAddresses = append(gwAddresses, addr) + } + + for i := range hostnames { + addr := gwapiv1.GatewayStatusAddress{ + Type: ptr.To(gwapiv1.HostnameAddressType), + Value: hostnames[i], + } + gwAddresses = append(gwAddresses, addr) + } + + gw.Status.Addresses = gwAddresses + } else { + gw.Status.Addresses = nil + } + // Update the programmed condition. + gw.Status.Conditions = MergeConditions(gw.Status.Conditions, computeGatewayProgrammedCondition(gw, deployment)) +} diff --git a/adapter/internal/operator/status/gatewayclass.go b/adapter/internal/operator/status/gatewayclass.go new file mode 100644 index 0000000000..ca5461fb15 --- /dev/null +++ b/adapter/internal/operator/status/gatewayclass.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +// This file contains code derived from Contour, +// https://github.com/projectcontour/contour +// from the source file +// https://github.com/projectcontour/contour/blob/main/internal/status/gatewayclass.go +// and is provided here subject to the following: +// Copyright Project Contour Authors +// SPDX-License-Identifier: Apache-2.0 + +package status + +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// SetGatewayClassAccepted inserts or updates the Accepted condition +// for the provided GatewayClass. +func SetGatewayClassAccepted(gc *gwapiv1.GatewayClass, accepted bool, reason, msg string) *gwapiv1.GatewayClass { + gc.Status.Conditions = MergeConditions(gc.Status.Conditions, computeGatewayClassAcceptedCondition(gc, accepted, reason, msg)) + return gc +} diff --git a/adapter/internal/operator/status/status.go b/adapter/internal/operator/status/status.go index c08b7df837..0ca1b4f172 100644 --- a/adapter/internal/operator/status/status.go +++ b/adapter/internal/operator/status/status.go @@ -65,7 +65,8 @@ func (updateHandler *UpdateHandler) applyUpdate(update Update) { }) if err != nil { - loggers.LoggerAPKOperator.Errorf("Unable to update status for %s, Error : %v ", update.NamespacedName.String(), err) + loggers.LoggerAPKOperator.Errorf("Unable to update status for %s, Resource Kind : %+v, Error : %v ", + update.NamespacedName.String(), update.Resource.GetObjectKind(), err) } } diff --git a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go index 2016851957..15146d4176 100644 --- a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go +++ b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. @@ -150,7 +149,8 @@ func (in *GQLRouteState) DeepCopyInto(out *GQLRouteState) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1alpha2.GQLRoute) (*in).DeepCopyInto(*out) } @@ -165,7 +165,8 @@ func (in *GQLRouteState) DeepCopyInto(out *GQLRouteState) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1alpha1.ResolvedBackend) (*in).DeepCopyInto(*out) } @@ -227,14 +228,16 @@ func (in *GatewayStateData) DeepCopyInto(out *GatewayStateData) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = make(map[string][]byte, len(*in)) for key, val := range *in { var outVal []byte if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = make([]byte, len(*in)) copy(*out, *in) } @@ -259,7 +262,8 @@ func (in *GatewayStateData) DeepCopyInto(out *GatewayStateData) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1alpha1.ResolvedBackend) (*in).DeepCopyInto(*out) } @@ -281,7 +285,8 @@ func (in *GatewayStateData) DeepCopyInto(out *GatewayStateData) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1alpha1.RateLimitPolicy) (*in).DeepCopyInto(*out) } @@ -316,7 +321,8 @@ func (in *HTTPRouteState) DeepCopyInto(out *HTTPRouteState) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1.HTTPRoute) (*in).DeepCopyInto(*out) } @@ -331,7 +337,8 @@ func (in *HTTPRouteState) DeepCopyInto(out *HTTPRouteState) { if val == nil { (*out)[key] = nil } else { - in, out := &val, &outVal + inVal := (*in)[key] + in, out := &inVal, &outVal *out = new(v1alpha1.ResolvedBackend) (*in).DeepCopyInto(*out) } diff --git a/adapter/internal/types/resourceversiontable.go b/adapter/internal/types/resourceversiontable.go new file mode 100644 index 0000000000..b22a534683 --- /dev/null +++ b/adapter/internal/types/resourceversiontable.go @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package types + +import ( + "fmt" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + "github.com/envoyproxy/go-control-plane/pkg/cache/types" + resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "google.golang.org/protobuf/proto" +) + +// XdsResources represents all the xds resources +type XdsResources = map[resourcev3.Type][]types.Resource + +// ResourceVersionTable holds all the translated xds resources +type ResourceVersionTable struct { + XdsResources +} + +// DeepCopyInto copies the contents into the output object +// This was generated by controller-gen, moved from +// zz_generated.deepcopy.go and updated to use proto.Clone +// to deep copy the proto.Message +func (t *ResourceVersionTable) DeepCopyInto(out *ResourceVersionTable) { + *out = *t + if t.XdsResources != nil { + in, out := &t.XdsResources, &out.XdsResources + *out = make(map[string][]types.Resource, len(*in)) + for key, val := range *in { + var outVal []types.Resource + if val == nil { + (*out)[key] = nil + } else { + // Snippet was generated by controller-gen + //G601: Implicit memory aliasing in for loop. + in, out := &val, &outVal //nolint:gosec,scopelint + *out = make([]types.Resource, len(*in)) + for i := range *in { + (*out)[i] = proto.Clone((*in)[i]) + } + } + (*out)[key] = outVal + } + } +} + +// DeepCopy generates a deep copy of the ResourceVersionTable object. +// This was generated by controller-gen and moved over from +// zz_generated.deepcopy.go to this file. +func (t *ResourceVersionTable) DeepCopy() *ResourceVersionTable { + if t == nil { + return nil + } + out := new(ResourceVersionTable) + t.DeepCopyInto(out) + return out +} + +// GetXdsResources retrieves the translated xds resources saved in the translator context. +func (t *ResourceVersionTable) GetXdsResources() XdsResources { + return t.XdsResources +} + +func (t *ResourceVersionTable) AddXdsResource(rType resourcev3.Type, xdsResource types.Resource) error { + // It's a sanity check to make sure the xdsResource is not nil + if xdsResource == nil { + return fmt.Errorf("xds resource is nil") + } + + // Perform type switch to handle different types of xdsResource + switch rType { + case resourcev3.ListenerType: + // Handle Type specific operations + if resourceOfType, ok := xdsResource.(*listenerv3.Listener); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err: %w", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Listener type", xdsResource) + } + case resourcev3.RouteType: + // Handle Type specific operations + if resourceOfType, ok := xdsResource.(*routev3.RouteConfiguration); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err: %w", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to RouteConfiguration type", xdsResource) + } + + case resourcev3.SecretType: + // Handle specific operations + if resourceOfType, ok := xdsResource.(*tlsv3.Secret); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err: %w", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Secret type", xdsResource) + } + + case resourcev3.EndpointType: + if resourceOfType, ok := xdsResource.(*endpointv3.ClusterLoadAssignment); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err: %w", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to ClusterLoadAssignment type", xdsResource) + } + + case resourcev3.ClusterType: + // Handle specific operations + if resourceOfType, ok := xdsResource.(*clusterv3.Cluster); ok { + if err := resourceOfType.ValidateAll(); err != nil { + return fmt.Errorf("validation failed for xds resource %+v, err: %w", xdsResource, err) + } + } else { + return fmt.Errorf("failed to cast xds resource %+v to Cluster type", xdsResource) + } + case resourcev3.RateLimitConfigType: + // Handle specific operations + // cfg resource from runner.go is the RateLimitConfig type from "github.com/envoyproxy/go-control-plane/ratelimit/config/ratelimit/v3", which does have validate function. + + // Add more cases for other types as needed + default: + // Handle the case when the type is not recognized or supported + } + + if t.XdsResources == nil { + t.XdsResources = make(XdsResources) + } + if t.XdsResources[rType] == nil { + t.XdsResources[rType] = make([]types.Resource, 0, 1) + } + + t.XdsResources[rType] = append(t.XdsResources[rType], xdsResource) + return nil +} + +// AddOrReplaceXdsResource will update an existing resource of rType according to matchFunc or add as a new resource +// if none satisfy the match criteria. It will only update the first match it finds, regardless +// if multiple resources satisfy the match criteria. +func (t *ResourceVersionTable) AddOrReplaceXdsResource(rType resourcev3.Type, resource types.Resource, matchFunc func(existing types.Resource, new types.Resource) bool) error { + if t.XdsResources == nil || t.XdsResources[rType] == nil { + return t.AddXdsResource(rType, resource) + } + + var found bool + for i, r := range t.XdsResources[rType] { + if matchFunc(r, resource) { + t.XdsResources[rType][i] = resource + found = true + break + } + } + if !found { + if err := t.AddXdsResource(rType, resource); err != nil { + return err + } + } + return nil +} + +// SetResources will update an entire entry of the XdsResources for a certain type to the provided resources +func (t *ResourceVersionTable) SetResources(rType resourcev3.Type, xdsResources []types.Resource) { + if t.XdsResources == nil { + t.XdsResources = make(XdsResources) + } + + t.XdsResources[rType] = xdsResources +} diff --git a/adapter/internal/types/resourceversiontable_test.go b/adapter/internal/types/resourceversiontable_test.go new file mode 100644 index 0000000000..d734dea702 --- /dev/null +++ b/adapter/internal/types/resourceversiontable_test.go @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package types + +import ( + "testing" + + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" + listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + "github.com/envoyproxy/go-control-plane/pkg/cache/types" + resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" +) + +var ( + testListener = &listenerv3.Listener{ + Name: "test-listener", + } + testSecret = &tlsv3.Secret{ + Name: "test-secret", + } +) + +func TestDeepCopy(t *testing.T) { + testCases := []struct { + name string + in *ResourceVersionTable + out *ResourceVersionTable + }{ + { + name: "nil", + in: nil, + out: nil, + }, + { + name: "listener", + in: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + out: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + }, + { + name: "kitchen-sink", + in: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + resourcev3.SecretType: []types.Resource{testSecret}, + }, + }, + out: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + resourcev3.SecretType: []types.Resource{testSecret}, + }, + }, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if tc.out == nil { + require.Nil(t, tc.in.DeepCopy()) + } else { + diff := cmp.Diff(tc.out, tc.in.DeepCopy(), protocmp.Transform()) + require.Empty(t, diff) + } + }) + } +} + +func TestAddOrReplaceXdsResource(t *testing.T) { + testListener := &listenerv3.Listener{ + Name: "test-listener", + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "exampleservice.examplenamespace.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + } + updatedListener := &listenerv3.Listener{ + Name: "test-listener", + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "newsvc.newns.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 8000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + } + testCluster := &clusterv3.Cluster{ + Name: "test-cluster", + LoadAssignment: &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "exampleservice.examplenamespace.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + updatedCluster := &clusterv3.Cluster{ + Name: "test-cluster", + LoadAssignment: &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "modified.example.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + testEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "exampleservice.examplenamespace.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + updatedEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "modified.example.svc.cluster.local", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + testRouteConfig := &routev3.RouteConfiguration{ + Name: "test-route-config", + VirtualHosts: []*routev3.VirtualHost{ + { + Name: "test-virtual-host", + Domains: []string{"test.example.com"}, + Routes: []*routev3.Route{ + { + Match: &routev3.RouteMatch{ + PathSpecifier: &routev3.RouteMatch_Prefix{ + Prefix: "/", + }, + }, + Action: &routev3.Route_Route{ + Route: &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: "test-cluster", + }, + }, + }, + }, + }, + }, + }, + } + testSecret := &tlsv3.Secret{ + Name: "example-secret", + Type: &tlsv3.Secret_TlsCertificate{ + TlsCertificate: &tlsv3.TlsCertificate{ + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN CERTIFICATE-----\n... Your certificate data ... \n-----END CERTIFICATE-----"), + }, + }, + PrivateKey: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN PRIVATE KEY-----\n... Your private key data ... \n-----END PRIVATE KEY-----"), + }, + }, + }, + }, + // Add other fields for the secret as needed. + } + + testCases := []struct { + name string + tableIn *ResourceVersionTable + typeIn resourcev3.Type + resourceIn types.Resource + funcIn func(existing types.Resource, new types.Resource) bool + tableOut *ResourceVersionTable + }{ + { + name: "inject-new-cluster", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: testCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{testCluster}, + }, + }, + }, + { + name: "replace-cluster", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{testCluster}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: updatedCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{updatedCluster}, + }, + }, + }, + { + name: "inject-new-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: testEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{testEndpoint}, + }, + }, + }, + { + name: "replace-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{testEndpoint}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: updatedEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{updatedEndpoint}, + }, + }, + }, + { + name: "inject-new-listener", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: testListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + }, + { + name: "replace-listener", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: updatedListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{updatedListener}, + }, + }, + }, + { + name: "inject-nil-resourcetype", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{}, + }, + typeIn: resourcev3.ClusterType, + resourceIn: testCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{testCluster}, + }, + }, + }, + { + name: "inject-nil resources", + tableIn: &ResourceVersionTable{}, + typeIn: resourcev3.ClusterType, + resourceIn: testCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{testCluster}, + }, + }, + }, + { + name: "inject-route-config", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: testRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{testRouteConfig}, + }, + }, + }, + { + name: "new-secret", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: testSecret, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{testSecret}, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.tableIn.AddOrReplaceXdsResource(tc.typeIn, tc.resourceIn, tc.funcIn) + require.NoError(t, err) + diff := cmp.Diff(tc.tableOut, tc.tableIn.DeepCopy(), protocmp.Transform()) + require.Empty(t, diff) + }) + } +} + +func TestInvalidAddXdsResource(t *testing.T) { + invalidListener := &listenerv3.Listener{ + Name: "invalid-listener", + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + } + invalidRouteConfig := &routev3.RouteConfiguration{ + Name: "test-route-config", + VirtualHosts: []*routev3.VirtualHost{ + { + Name: "", // missing name + Domains: []string{"test.example.com"}, + Routes: []*routev3.Route{ + { + Match: &routev3.RouteMatch{ + PathSpecifier: &routev3.RouteMatch_Prefix{ + Prefix: "/", + }, + }, + Action: &routev3.Route_Route{ + Route: &routev3.RouteAction{ + ClusterSpecifier: &routev3.RouteAction_Cluster{ + Cluster: "test-cluster", + }, + }, + }, + }, + }, + }, + }, + } + + invalidCluster := &clusterv3.Cluster{ + Name: "test-cluster", + LoadAssignment: &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + invalidEndpoint := &endpointv3.ClusterLoadAssignment{ + ClusterName: "test-cluster", + Endpoints: []*endpointv3.LocalityLbEndpoints{ + { + LbEndpoints: []*endpointv3.LbEndpoint{ + { + HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ + Endpoint: &endpointv3.Endpoint{ + Address: &corev3.Address{ + Address: &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Address: "", + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: 5000, + }, + Protocol: corev3.SocketAddress_TCP, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + invalidSecret := &tlsv3.Secret{ + Name: "=*&", + Type: &tlsv3.Secret_TlsCertificate{ + TlsCertificate: &tlsv3.TlsCertificate{ + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineBytes{ + InlineBytes: []byte("-----BEGIN CERTIFICATE-----\n... Your certificate data ... \n-----END CERTIFICATE-----"), + }, + }, + PrivateKey: &corev3.DataSource{}, + }, + }, + // Add other fields for the secret as needed. + } + + testCases := []struct { + name string + tableIn *ResourceVersionTable + typeIn resourcev3.Type + resourceIn types.Resource + funcIn func(existing types.Resource, new types.Resource) bool + tableOut *ResourceVersionTable + }{ + { + name: "inject-invalid-listener", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{testListener}, + }, + }, + }, + { + name: "inject-invalid-route-config", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: invalidRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "inject-invalid-cluster", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-cluster-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ClusterType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ClusterType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldCluster := existing.(*clusterv3.Cluster) + newCluster := new.(*clusterv3.Cluster) + if newCluster == nil || oldCluster == nil { + return false + } + if oldCluster.Name == newCluster.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-listener-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.ListenerType: []types.Resource{}, + }, + }, + typeIn: resourcev3.ListenerType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-route-config-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.RouteType: []types.Resource{}, + }, + }, + typeIn: resourcev3.RouteType, + resourceIn: invalidCluster, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-secret-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: invalidRouteConfig, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "invalid-secret", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.SecretType: []types.Resource{}, + }, + }, + typeIn: resourcev3.SecretType, + resourceIn: invalidSecret, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldListener := existing.(*listenerv3.Listener) + newListener := new.(*listenerv3.Listener) + if newListener == nil || oldListener == nil { + return false + } + if oldListener.Name == newListener.Name { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "inject-invalid-endpoint", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: invalidEndpoint, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: nil, + }, + { + name: "cast-endpoint-type", + tableIn: &ResourceVersionTable{ + XdsResources: XdsResources{ + resourcev3.EndpointType: []types.Resource{}, + }, + }, + typeIn: resourcev3.EndpointType, + resourceIn: invalidListener, + funcIn: func(existing types.Resource, new types.Resource) bool { + oldEndpoint := existing.(*endpointv3.ClusterLoadAssignment) + newEndpoint := new.(*endpointv3.ClusterLoadAssignment) + if newEndpoint == nil || oldEndpoint == nil { + return false + } + if oldEndpoint.ClusterName == newEndpoint.ClusterName { + return true + } + return false + }, + tableOut: nil, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.tableIn.AddOrReplaceXdsResource(tc.typeIn, tc.resourceIn, tc.funcIn) + require.Error(t, err) + }) + } +} diff --git a/adapter/pkg/health/health.go b/adapter/pkg/health/health.go index 4f65c16dd9..9edaad386b 100644 --- a/adapter/pkg/health/health.go +++ b/adapter/pkg/health/health.go @@ -58,8 +58,8 @@ type Server struct { // Check responds the health check client with health status of the Adapter func (s Server) Check(ctx context.Context, request *healthservice.HealthCheckRequest) (*healthservice.HealthCheckResponse, error) { - logger.LoggerHealth.Debugf("Querying health state for Adapter service \"%s\"", request.Service) - logger.LoggerHealth.Debugf("Internal health state map: %v", serviceHealthStatus) + // logger.LoggerHealth.Debugf("Querying health state for Adapter service \"%s\"", request.Service) + // logger.LoggerHealth.Debugf("Internal health state map: %v", serviceHealthStatus) if request.Service == "" { // overall health of the server diff --git a/adapter/pkg/utils/file/file.go b/adapter/pkg/utils/file/file.go new file mode 100644 index 0000000000..51740db978 --- /dev/null +++ b/adapter/pkg/utils/file/file.go @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package file + +import ( + "bufio" + "os" +) + +func Write(data string, filepath string) error { + file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + defer file.Close() + write := bufio.NewWriter(file) + _, err = write.WriteString(data) + if err != nil { + return err + } + write.Flush() + + return nil +} diff --git a/adapter/pkg/utils/misc/misc.go b/adapter/pkg/utils/misc/misc.go new file mode 100644 index 0000000000..f4080c5f06 --- /dev/null +++ b/adapter/pkg/utils/misc/misc.go @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package utils + +import ( + "crypto/sha256" + "fmt" + "hash/fnv" + "strings" + + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// NamespacedName creates and returns object's NamespacedName. +func NamespacedName(obj client.Object) types.NamespacedName { + return types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + } +} + +// GetHashedName returns a partially hashed name for the string including up to the given length of the original name characters before the hash. +// Input `nsName` should be formatted as `{Namespace}/{ResourceName}`. +func GetHashedName(nsName string, length int) string { + hashedName := Digest256(nsName) + // replace `/` with `-` to create a valid K8s resource name + resourceName := strings.ReplaceAll(nsName, "/", "-") + if length > 0 && len(resourceName) > length { + // resource name needs to be trimmed, as container port name must not contain consecutive hyphens + trimmedName := strings.TrimSuffix(resourceName[0:length], "-") + return fmt.Sprintf("%s-%s", trimmedName, hashedName[0:8]) + } + // Ideally we should use 32-bit hash instead of 64-bit hash and return the first 8 characters of the hash. + // However, we are using 64-bit hash to maintain backward compatibility. + return fmt.Sprintf("%s-%s", resourceName, hashedName[0:8]) +} + +// Digest256 returns a sha256 hash of the input string. +// The hash is represented as a hexadecimal string of length 64. +func Digest256(str string) string { + h := sha256.New() // Using sha256 instead of sha1 due to Blocklisted import crypto/sha1: weak cryptographic primitive (gosec) + h.Write([]byte(str)) + return strings.ToLower(fmt.Sprintf("%x", h.Sum(nil))) +} + +// Digest32 returns a 32-bit hash of the input string. +// The hash is represented as a hexadecimal string of length 8. +func Digest32(str string) string { + h := fnv.New32a() + _, _ = h.Write([]byte(str)) + return fmt.Sprintf("%x", h.Sum32()) +} diff --git a/adapter/pkg/utils/misc/misc_test.go b/adapter/pkg/utils/misc/misc_test.go new file mode 100644 index 0000000000..e6cf51a30a --- /dev/null +++ b/adapter/pkg/utils/misc/misc_test.go @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetHashedName(t *testing.T) { + testCases := []struct { + name string + nsName string + length int + expected string + }{ + {"test default name", "http", 6, "http-e0603c49"}, + {"test removing trailing slash", "namespace/name", 10, "namespace-18a6500f"}, + {"test removing trailing hyphen", "apk/eg/http", 6, "apk-eg-9df93c35"}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + result := GetHashedName(tc.nsName, tc.length) + require.Equal(t, tc.expected, result, "Result does not match expected string") + }) + } +} diff --git a/adapter/pkg/utils/protocov/protocov.go b/adapter/pkg/utils/protocov/protocov.go new file mode 100644 index 0000000000..6040be9e91 --- /dev/null +++ b/adapter/pkg/utils/protocov/protocov.go @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package protocov + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + APIPrefix = "type.googleapis.com/" +) + +var ( + marshalOpts = proto.MarshalOptions{} +) + +func ToAnyWithError(msg proto.Message) (*anypb.Any, error) { + b, err := marshalOpts.Marshal(msg) + if err != nil { + return nil, err + } + return &anypb.Any{ + TypeUrl: APIPrefix + string(msg.ProtoReflect().Descriptor().FullName()), + Value: b, + }, nil +} + +func ToAny(msg proto.Message) *anypb.Any { + res, err := ToAnyWithError(msg) + if err != nil { + return nil + } + return res +} diff --git a/adapter/pkg/utils/regex/regex.go b/adapter/pkg/utils/regex/regex.go new file mode 100644 index 0000000000..5bd9d95777 --- /dev/null +++ b/adapter/pkg/utils/regex/regex.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package regex + +import ( + "fmt" + "regexp" +) + +// Validate validates a regex string. +func Validate(regex string) error { + if _, err := regexp.Compile(regex); err != nil { + return fmt.Errorf("regex %q is invalid: %w", regex, err) + } + return nil +} diff --git a/adapter/pkg/utils/regex/regex_test.go b/adapter/pkg/utils/regex/regex_test.go new file mode 100644 index 0000000000..129ac7254b --- /dev/null +++ b/adapter/pkg/utils/regex/regex_test.go @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + * This file contains code derived from Envoy Gateway, + * https://github.com/envoyproxy/gateway + * and is provided here subject to the following: + * Copyright Project Envoy Gateway Authors + * + */ + +package regex + +import "testing" + +func TestValidate(t *testing.T) { + tests := []struct { + name string + regex string + wantErr bool + }{ + { + name: "Valid regex", + regex: "^[a-z0-9-]+$", + wantErr: false, + }, + { + name: "Invalid regex", + regex: "^[a-z0-9-++$", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Validate(tt.regex); (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/adapter/pkg/utils/stringutils/string_utils.go b/adapter/pkg/utils/stringutils/string_utils.go index 829f02bddc..b8394f0db9 100644 --- a/adapter/pkg/utils/stringutils/string_utils.go +++ b/adapter/pkg/utils/stringutils/string_utils.go @@ -67,3 +67,16 @@ func ToStringArray[T any](array []T) []string { } return vHosts } + +// RemoveString returns a newly created []string that contains all items from slice that +// are not equal to s. +func RemoveString(slice []string, s string) []string { + var newSlice []string + for _, item := range slice { + if item == s { + continue + } + newSlice = append(newSlice, item) + } + return newSlice +} diff --git a/adapter/pkg/utils/stringutils/string_utils_test.go b/adapter/pkg/utils/stringutils/string_utils_test.go index ae67132572..7fa7368411 100644 --- a/adapter/pkg/utils/stringutils/string_utils_test.go +++ b/adapter/pkg/utils/stringutils/string_utils_test.go @@ -17,6 +17,7 @@ package stringutils import ( + "reflect" "testing" "github.com/stretchr/testify/assert" @@ -95,3 +96,42 @@ func TestMaskString(t *testing.T) { assert.Equal(t, item.output, result, item.message, i) } } + +func TestRemoveString(t *testing.T) { + tests := []struct { + testName string + input []string + remove string + want []string + }{ + { + testName: "Nil input slice", + input: nil, + remove: "", + want: nil, + }, + { + testName: "Remove a string from input slice", + input: []string{"a", "ab", "cdef"}, + remove: "ab", + want: []string{"a", "cdef"}, + }, + { + testName: "Slice doesn't contain the string", + input: []string{"a", "ab", "cdef"}, + remove: "NotPresentInSlice", + want: []string{"a", "ab", "cdef"}, + }, + { + testName: "All strings removed, result is nil", + input: []string{"a"}, + remove: "a", + want: nil, + }, + } + for _, tt := range tests { + if got := RemoveString(tt.input, tt.remove); !reflect.DeepEqual(got, tt.want) { + t.Errorf("%v: RemoveString(%v, %q) = %v WANT %v", tt.testName, tt.input, tt.remove, got, tt.want) + } + } +} diff --git a/adapter/resources/check_health.sh b/adapter/resources/check_health.sh index 8d5e18abd5..93c56cad5d 100755 --- a/adapter/resources/check_health.sh +++ b/adapter/resources/check_health.sh @@ -15,7 +15,7 @@ # limitations under the License. # ----------------------------------------------------------------------- -ADAPTER_XDS_PORT="${ADAPTER_XDS_PORT:-18000}" +ADAPTER_XDS_PORT="${ADAPTER_XDS_PORT:-18001}" ADAPTER_SERVER_NAME="${ADAPTER_SERVER_NAME:-adapter}" grpc_health_probe -addr "127.0.0.1:${ADAPTER_XDS_PORT}" \ -tls \ diff --git a/adapter/revive.toml b/adapter/revive.toml index 0024ff213b..80be43d1fb 100644 --- a/adapter/revive.toml +++ b/adapter/revive.toml @@ -12,7 +12,7 @@ exclude = [ "internal/operator/controllers/cp/suite_test.go","internal/operator/ [rule.error-return] [rule.error-strings] [rule.error-naming] -[rule.exported] +# [rule.exported] [rule.if-return] [rule.increment-decrement] [rule.var-naming] diff --git a/gateway/enforcer/Dockerfile b/gateway/enforcer/Dockerfile index 62ed13dfb7..59739b9c84 100644 --- a/gateway/enforcer/Dockerfile +++ b/gateway/enforcer/Dockerfile @@ -75,18 +75,6 @@ ENV JAVA_OPTS="" ENV ENFORCER_HOME=${APK_USER_HOME} ARG CHECKSUM_AMD64="7e564681110ee4563637457b91e42f62f96b79618a835bb05ae2305acdcc3db0" ARG CHECKSUM_ARM64="3759148e22a494149a4abae269adee0d20c428b966683426e2319f9047da521d" -ENV ENFORCER_PRIVATE_KEY_PATH=/home/wso2/security/keystore/mg.key -ENV ENFORCER_PUBLIC_CERT_PATH=/home/wso2/security/keystore/mg.pem -ENV TRUSTED_CA_CERTS_PATH=/home/wso2/security/truststore -ENV ADAPTER_HOST_NAME=adapter -ENV ADAPTER_HOST=adapter -ENV ADAPTER_XDS_PORT=18000 -ENV COMMON_CONTROLLER_HOST_NAME=common-controller -ENV COMMON_CONTROLLER_HOST=common-controller -ENV COMMON_CONTROLLER_XDS_PORT=18002 -ENV ENFORCER_LABEL="default" -ENV XDS_MAX_MSG_SIZE=4194304 -ENV XDS_MAX_RETRIES=3 #todo update the connection string ENV APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=;IngestionEndpoint=https://westus2-2.in.applicationinsights.azure.com/ diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/config/EnvVarConfig.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/config/EnvVarConfig.java index a3b31bce7a..e9c484dbcf 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/config/EnvVarConfig.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/config/EnvVarConfig.java @@ -68,7 +68,7 @@ public class EnvVarConfig { private static final String DEFAULT_ENFORCER_PUBLIC_CERT_PATH = "/home/wso2/security/keystore/mg.pem"; private static final String DEFAULT_ENFORCER_REGION_ID = "UNKNOWN"; private static final String DEFAULT_ADAPTER_HOST = "adapter"; - private static final String DEFAULT_ADAPTER_XDS_PORT = "18000"; + private static final String DEFAULT_ADAPTER_XDS_PORT = "18001"; private static final String DEFAULT_COMMON_CONTROLLER_HOST = "common-controller"; private static final String DEFAULT_COMMON_CONTROLLER_XDS_PORT = "18002"; private static final String DEFAULT_COMMON_CONTROLLER_REST_PORT = "18003"; diff --git a/helm-charts/Chart.lock b/helm-charts/Chart.lock index ffcc399714..9ce49a7eb6 100644 --- a/helm-charts/Chart.lock +++ b/helm-charts/Chart.lock @@ -7,6 +7,6 @@ dependencies: version: 17.8.0 - name: cert-manager repository: https://charts.jetstack.io - version: v1.10.1 -digest: sha256:0991f0f8b1c4652afcde362a3359f514796b41ac6f43039712fe67df616e5eca -generated: "2023-03-13T18:09:21.400318+05:30" + version: v1.10.2 +digest: sha256:c73e2790cdd6cef996f40aecb00224e74e8aae146a7d1c4afb528720b404f76c +generated: "2024-06-13T16:05:27.648453+05:30" diff --git a/helm-charts/Chart.yaml b/helm-charts/Chart.yaml index 1a794cac65..1e7fb59571 100644 --- a/helm-charts/Chart.yaml +++ b/helm-charts/Chart.yaml @@ -14,6 +14,6 @@ dependencies: repository: "https://charts.bitnami.com/bitnami" condition: redis.enabled - name: cert-manager - version: "v1.10.1" + version: "v1.10.2" repository: "https://charts.jetstack.io" condition: certmanager.enabled diff --git a/helm-charts/templates/cert-manager/certificates/envoy-certificate.yaml b/helm-charts/templates/cert-manager/certificates/envoy-certificate.yaml new file mode 100644 index 0000000000..b154b3eee1 --- /dev/null +++ b/helm-charts/templates/cert-manager/certificates/envoy-certificate.yaml @@ -0,0 +1,43 @@ +# Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. +# +# WSO2 LLC. 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. + +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io + +{{- if .Values.wso2.apk.dp.enabled }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: envoy-cert + namespace: {{ .Release.Namespace }} +spec: + commonName: envoy-gateway + privateKey: + algorithm: RSA + encoding: PKCS8 + size: 2048 + dnsNames: + - \*.{{ .Release.Namespace }}.svc + - \*.{{ .Release.Namespace }}.svc.cluster.local + issuerRef: + kind: {{ .Values.certmanager.issuerKind | default "ClusterIssuer" }} + {{ if .Values.certmanager.servers -}} + name: {{ .Values.certmanager.servers.issuerName | default "selfsigned-issuer" }} + {{- else -}} + name: {{ template "apk-helm.resource.prefix" . }}-selfsigned-issuer + {{- end }} + secretName: envoy-cert +{{- end -}} diff --git a/helm-charts/templates/data-plane/gateway-components/adapter/adapter-deployment.yaml b/helm-charts/templates/data-plane/gateway-components/adapter/adapter-deployment.yaml index 9167c587dc..3416b7cdde 100644 --- a/helm-charts/templates/data-plane/gateway-components/adapter/adapter-deployment.yaml +++ b/helm-charts/templates/data-plane/gateway-components/adapter/adapter-deployment.yaml @@ -88,7 +88,7 @@ spec: {{- else }} subPath: ca.crt {{- end }} - - name: router-truststore-secret-volume + - name: envoy-truststore-secret-volume mountPath: /home/wso2/security/truststore/router.crt {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.certFilename | default "tls.crt" }} @@ -158,13 +158,9 @@ spec: # secret: # secretName: {{ template "apk-helm.resource.prefix" . }}-webhook-server-cert # defaultMode: 420 - - name: router-truststore-secret-volume + - name: envoy-truststore-secret-volume secret: - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} - secretName: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.secretName | default (printf "%s-gateway-server-cert" (include "apk-helm.resource.prefix" .)) }} - {{- else }} - secretName: {{ template "apk-helm.resource.prefix" . }}-gateway-server-cert - {{- end }} + secretName: envoy-cert - name: enforcer-truststore-secret-volume secret: {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} diff --git a/helm-charts/templates/data-plane/gateway-components/adapter/adapter-grpc-probe-script-conf.yaml b/helm-charts/templates/data-plane/gateway-components/adapter/adapter-grpc-probe-script-conf.yaml index 9d1d9bde92..abecda2fab 100644 --- a/helm-charts/templates/data-plane/gateway-components/adapter/adapter-grpc-probe-script-conf.yaml +++ b/helm-charts/templates/data-plane/gateway-components/adapter/adapter-grpc-probe-script-conf.yaml @@ -38,7 +38,7 @@ data: # limitations under the License. # ----------------------------------------------------------------------- - ADAPTER_XDS_PORT="${ADAPTER_XDS_PORT:-18000}" + ADAPTER_XDS_PORT="${ADAPTER_XDS_PORT:-18001}" grpc_health_probe -addr "127.0.0.1:${ADAPTER_XDS_PORT}" \ -tls \ -tls-ca-cert "${ADAPTER_PUBLIC_CERT_PATH}" \ diff --git a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-autoscaller.yaml b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-autoscaller.yaml deleted file mode 100644 index b9a5a8c594..0000000000 --- a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-autoscaller.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# -------------------------------------------------------------------- -# Copyright (c) 2022, WSO2 LLC. (http://wso2.com) All Rights Reserved. -# -# Licensed 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. -# ----------------------------------------------------------------------- - -{{- if .Values.wso2.apk.dp.enabled }} -{{- if and .Values.wso2.apk.dp.gateway.autoscaling .Values.wso2.apk.dp.gateway.autoscaling.enabled -}} - -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: {{ template "apk-helm.resource.prefix" . }}-gateway-runtime-autoscaller - namespace: {{ .Release.Namespace }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ template "apk-helm.resource.prefix" . }}-gateway-runtime-deployment - minReplicas: {{ .Values.wso2.apk.dp.gateway.autoscaling.minReplicas }} - maxReplicas: {{ .Values.wso2.apk.dp.gateway.autoscaling.maxReplicas }} - metrics: - {{- if .Values.wso2.apk.dp.gateway.autoscaling.targetMemory }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ .Values.wso2.apk.dp.gateway.autoscaling.targetMemory }} - {{- end }} - {{- if .Values.wso2.apk.dp.gateway.autoscaling.targetCPU }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ .Values.wso2.apk.dp.gateway.autoscaling.targetCPU }} - {{- end }} -{{- end }} -{{- end}} \ No newline at end of file diff --git a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-deployment.yaml b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-deployment.yaml deleted file mode 100644 index 3d6fc60622..0000000000 --- a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-runtime-deployment.yaml +++ /dev/null @@ -1,476 +0,0 @@ -# Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. -# -# WSO2 LLC. 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. - -{{- if .Values.wso2.apk.dp.enabled }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "apk-helm.resource.prefix" . }}-gateway-runtime-deployment - namespace: {{ .Release.Namespace }} -spec: - replicas: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.replicas }} - strategy: - type: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.strategy }} - selector: - matchLabels: -{{ include "apk-helm.pod.selectorLabels" (dict "root" . "app" "gateway" ) | indent 6}} - template: - metadata: - labels: -{{ include "apk-helm.pod.selectorLabels" (dict "root" . "app" "gateway" ) | indent 8}} - annotations: - checksum/config: {{ include (print $.Template.BasePath "/data-plane/gateway-components/log-conf.yaml") . | sha256sum }} - spec: - affinity: {{- include "apk-helm.deployment.affinity" ( dict "value" .Values.wso2.apk.dp.gatewayRuntime.deployment.affinity "app" "gateway-runtime" "context" $) | nindent 8 }} - {{- if .Values.wso2.apk.dp.gatewayRuntime.deployment.nodeSelector }} - nodeSelector: {{- include "apk-helm.deployment.nodeSelector" ( dict "value" .Values.wso2.apk.dp.gatewayRuntime.deployment.nodeSelector "context" $) | nindent 8 }} - {{- end }} - automountServiceAccountToken: false - containers: - - name: enforcer - image: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.image }} - imagePullPolicy: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.imagePullPolicy }} - ports: - - containerPort: 8081 - protocol: "TCP" - - containerPort: 9001 - protocol: "TCP" - - containerPort: 5006 - protocol: "TCP" - - containerPort: 8084 - protocol: "TCP" - - containerPort: 9092 - protocol: "TCP" - - containerPort: 18002 - protocol: "TCP" - {{- if and .Values.wso2.apk.metrics .Values.wso2.apk.metrics.enabled}} - - containerPort: 18006 - protocol: "TCP" - {{- end }} -{{ include "apk-helm.deployment.resources" .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.resources | indent 10 }} -{{ include "apk-helm.deployment.env" .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.env | indent 10 }} - - name: ADAPTER_HOST_NAME - value: {{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc - - name: ADAPTER_HOST - value: {{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc - - name: COMMON_CONTROLLER_HOST_NAME - value: {{ template "apk-helm.resource.prefix" . }}-common-controller-service.{{ .Release.Namespace }}.svc - - name: COMMON_CONTROLLER_HOST - value: {{ template "apk-helm.resource.prefix" . }}-common-controller-service.{{ .Release.Namespace }}.svc - - name: ENFORCER_PRIVATE_KEY_PATH - value: /home/wso2/security/keystore/enforcer.key - - name: ENFORCER_PUBLIC_CERT_PATH - value: /home/wso2/security/keystore/enforcer.crt - - name: ENFORCER_SERVER_NAME - value: {{ template "apk-helm.resource.prefix" . }}-enforcer-service.{{ .Release.Namespace }}.svc - - name: TRUSTED_CA_CERTS_PATH - value: "/home/wso2/security/truststore" - - name: ADAPTER_XDS_PORT - value : "18000" - - name: COMMON_CONTROLLER_XDS_PORT - value : "18002" - - name: COMMON_CONTROLLER_REST_PORT - value : "18003" - - name: ENFORCER_LABEL - value : {{ .Values.wso2.apk.dp.gateway.name | default "wso2-apk-default" }} - - name: ENFORCER_REGION - value: UNKNOWN - - name: XDS_MAX_MSG_SIZE - value: "4194304" - - name: XDS_MAX_RETRIES - value: "3" - - name: enforcer_admin_pwd - value: admin - - name: JAVA_OPTS - {{- if and .Values.wso2.apk.metrics .Values.wso2.apk.metrics.enabled }} - value: -Dhttpclient.hostnameVerifier=AllowAll -Xms512m -Xmx512m -XX:MaxRAMFraction=2 -Dapk.jmx.metrics.enabled=true -javaagent:/home/wso2/lib/jmx_prometheus_javaagent-0.20.0.jar=18006:/tmp/metrics/prometheus-jmx-config-enforcer.yml - {{- else }} - value: -Dhttpclient.hostnameVerifier=AllowAll -Xms512m -Xmx512m -XX:MaxRAMFraction=2 - {{- end }} - {{- if and .Values.wso2.apk.dp.gatewayRuntime.analytics .Values.wso2.apk.dp.gatewayRuntime.analytics.publishers }} - {{- $defaultPublisherSecretName := "" }} - {{- $moesifPublisherSecretName := "" }} - {{- range .Values.wso2.apk.dp.gatewayRuntime.analytics.publishers }} - {{- if eq .type "default" }} - {{- $defaultPublisherSecretName = .secretName }} - {{- end }} - {{- if eq .type "moesif" }} - {{- $moesifPublisherSecretName = .secretName }} - {{- end }} - {{- end }} - {{- if $defaultPublisherSecretName }} - - name: CHOREO_ANALYTICS_AUTH_TOKEN - valueFrom: - secretKeyRef: - name: {{ $defaultPublisherSecretName }} - key: "authToken" - - name: CHOREO_ANALYTICS_AUTH_URL - valueFrom: - secretKeyRef: - name: {{ $defaultPublisherSecretName }} - key: "authURL" - {{- end }} - {{- if $moesifPublisherSecretName }} - - name: MOESIF_TOKEN - valueFrom: - secretKeyRef: - name: {{ $moesifPublisherSecretName }} - key: "moesifToken" - {{- end }} - {{- end }} - {{- if .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis }} - - name: REDIS_USERNAME - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.username | default "default" }} - - name: REDIS_PASSWORD - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.password | default "" }} - - name: REDIS_HOST - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.host | default "redis-master" }} - - name: REDIS_PORT - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.port | default "6379" }} - - name: IS_REDIS_TLS_ENABLED - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.tlsEnabled | default "false" }} - - name: REDIS_REVOKED_TOKENS_CHANNEL - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.channelName | default "wso2-apk-revoked-tokens-channel" }} - - name: REDIS_KEY_FILE - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.userKeyPath | default "/home/wso2/security/redis/redis.key" }} - - name: REDIS_CERT_FILE - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.userCertPath | default "/home/wso2/security/redis/redis.crt" }} - - name: REDIS_CA_CERT_FILE - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.redisCaCertPath | default "/home/wso2/security/redis/redis-ca.key" }} - - name: REVOKED_TOKEN_CLEANUP_INTERVAL - value: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.redis.tokenCleanupInterval | default "3600" }} - {{- else }} - - name: REDIS_USERNAME - value: "default" - - name: REDIS_PASSWORD - value: "" - - name: REDIS_HOST - value: "redis-master" - - name: REDIS_PORT - value: "6379" - - name: IS_REDIS_TLS_ENABLED - value: "false" - - name: REDIS_REVOKED_TOKENS_CHANNEL - value: "wso2-apk-revoked-tokens-channel" - - name: REDIS_KEY_FILE - value: "/home/wso2/security/redis/redis.key" - - name: REDIS_CERT_FILE - value: "/home/wso2/security/redis/redis.crt" - - name: REDIS_CA_CERT_FILE - value: "/home/wso2/security/redis/redis-ca.key" - - name: REVOKED_TOKEN_CLEANUP_INTERVAL - value: "3600" - {{- end }} - volumeMounts: - - name: tmp - mountPath: /tmp - - name: enforcer-keystore-secret-volume - mountPath: /home/wso2/security/keystore/enforcer.key - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.certKeyFilename | default "tls.key" }} - {{- else }} - subPath: tls.key - {{- end }} - {{- if and .Values.wso2.apk.metrics .Values.wso2.apk.metrics.enabled }} - - name: prometheus-jmx-config-volume - mountPath: /tmp/metrics/prometheus-jmx-config-enforcer.yml - subPath: prometheus-jmx-config-enforcer.yml - {{- end }} - - name: enforcer-keystore-secret-volume - mountPath: /home/wso2/security/keystore/enforcer.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: enforcer-keystore-secret-volume - mountPath: /home/wso2/security/truststore/apk.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.caCertFileName | default "ca.crt" }} - {{- else }} - subPath: ca.crt - {{- end }} - - name: enforcer-keystore-secret-volume - mountPath: /home/wso2/security/truststore/enforcer.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: adapter-truststore-secret-volume - mountPath: /home/wso2/security/truststore/adapter.crt - {{- if and .Values.wso2.apk.dp.adapter.deployment.configs .Values.wso2.apk.dp.adapter.deployment.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.adapter.deployment.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - # - name: common-controller-truststore-secret-volume - # mountPath: /home/wso2/security/truststore/adapter.pem - - name: router-keystore-secret-volume - mountPath: /home/wso2/security/truststore/router.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: log-conf-volume - mountPath: /home/wso2/conf/ - - name: enforcer-jwt-secret-volume - mountPath: /home/wso2/security/keystore/mg.pem - subPath: mg.pem - - name: enforcer-jwt-secret-volume - mountPath: /home/wso2/security/truststore/mg.pem - subPath: mg.pem - - name: enforcer-jwt-secret-volume - mountPath: /home/wso2/security/keystore/mg.key - subPath: mg.key - - name: enforcer-trusted-certs - mountPath: /home/wso2/security/truststore/wso2carbon.pem - subPath: wso2carbon.pem - - name: enforcer-apikey-cert - mountPath: /home/wso2/security/truststore/wso2-apim-carbon.pem - subPath: wso2-apim-carbon.pem - - name: idp-certificate-secret-volume - mountPath: /home/wso2/security/truststore/idp.pem - {{ if and .Values.wso2.apk.idp.signing .Values.wso2.apk.idp.signing.fileName }} - subPath: {{ .Values.wso2.apk.idp.signing.fileName }} - {{ else }} - subPath: wso2carbon.pem - {{ end }} - {{ if and .Values.wso2.apk.idp.tls .Values.wso2.apk.idp.tls.fileName }} - - name: idp-tls-certificate-secret-volume - mountPath: /home/wso2/security/truststore/idp-tls.pem - subPath: {{ .Values.wso2.apk.idp.tls.fileName }} - {{ end }} - readinessProbe: - exec: - command: [ "sh", "check_health.sh" ] - initialDelaySeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.readinessProbe.periodSeconds }} - failureThreshold: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.readinessProbe.failureThreshold }} - livenessProbe: - exec: - command: [ "sh", "check_health.sh" ] - initialDelaySeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.livenessProbe.periodSeconds }} - failureThreshold: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.livenessProbe.failureThreshold }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - readOnlyRootFilesystem: true - runAsNonRoot: true - - name: router - image: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.image }} - imagePullPolicy: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.imagePullPolicy }} - ports: - {{ if and .Values.wso2.apk.dp.gateway.httpListener .Values.wso2.apk.dp.gateway.httpListener.enabled }} - - containerPort: {{ .Values.wso2.apk.dp.gateway.httpListener.port | default 9080}} - protocol: "TCP" - {{ end }} - - containerPort: 9095 - protocol: "TCP" - - containerPort: 9090 - protocol: "TCP" - - containerPort: 9091 - protocol: "TCP" - - containerPort: 9000 - protocol: "TCP" -{{ include "apk-helm.deployment.resources" .Values.wso2.apk.dp.gatewayRuntime.deployment.router.resources | indent 10 }} -{{ include "apk-helm.deployment.env" .Values.wso2.apk.dp.gatewayRuntime.deployment.router.env | indent 10 }} - - name: ADAPTER_HOST_NAME - value: {{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc - - name: ADAPTER_HOST - value: {{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc - - name: ENFORCER_HOST - value: "127.0.0.1" - - name: ENFORCER_ANALYTICS_HOST - value: "127.0.0.1" - - name: ROUTER_ADMIN_HOST - value: "0.0.0.0" - - name: ROUTER_ADMIN_PORT - value: "9000" - - name: ROUTER_PORT - value: "9095" - - name: ROUTER_CLUSTER - value: "apk_router_cluster" - - name: ROUTER_LABEL - value: {{ .Values.wso2.apk.dp.gateway.name | default "wso2-apk-default" }} - - name: ROUTER_PRIVATE_KEY_PATH - value: "/home/wso2/security/keystore/router.key" - - name: ROUTER_PUBLIC_CERT_PATH - value: "/home/wso2/security/keystore/router.crt" - - name: ADAPTER_PORT - value: "18000" - - name: ADAPTER_CA_CERT_PATH - value: "/home/wso2/security/truststore/adapter.crt" - - name: ENFORCER_PORT - value: "8081" - - name: ENFORCER_ANALYTICS_RECEIVER_PORT - value: "18090" - - name: ENFORCER_CA_CERT_PATH - value: "/home/wso2/security/truststore/enforcer.crt" - - name: CONCURRENCY - value: "2" - volumeMounts: - - name: router-keystore-secret-volume - mountPath: /home/wso2/security/keystore/router.key - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.certKeyFilename | default "tls.key" }} - {{- else }} - subPath: tls.key - {{- end }} - - name: router-keystore-secret-volume - mountPath: /home/wso2/security/keystore/router.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: adapter-truststore-secret-volume - mountPath: /home/wso2/security/truststore/adapter.crt - {{- if and .Values.wso2.apk.dp.adapter.deployment.configs .Values.wso2.apk.dp.adapter.deployment.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.adapter.deployment.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: enforcer-keystore-secret-volume - mountPath: /home/wso2/security/truststore/enforcer.crt - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: log-conf-volume - mountPath: /home/wso2/conf/ - {{ if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.ratelimiter.enabled }} - - name: ratelimiter-truststore-secret-volume - mountPath: /home/wso2/security/truststore/ratelimiter.crt - {{- if and .Values.wso2.apk.dp.ratelimiter.deployment.configs .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls.certFilename | default "tls.crt" }} - {{- else }} - subPath: tls.crt - {{- end }} - - name: ratelimiter-truststore-secret-volume - mountPath: /home/wso2/security/truststore/ratelimiter-ca.crt - {{- if and .Values.wso2.apk.dp.ratelimiter.deployment.configs .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls }} - subPath: {{ .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls.certCAFilename | default "ca.crt" }} - {{- else }} - subPath: ca.crt - {{- end }} - {{ end }} - livenessProbe: - exec: - command: [ "sh", "router_check_health.sh", "health" ] - initialDelaySeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.livenessProbe.periodSeconds }} - failureThreshold: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.livenessProbe.failureThreshold }} - readinessProbe: - exec: - command: [ "sh", "router_check_health.sh", "ready" ] - initialDelaySeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.periodSeconds }} - failureThreshold: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.failureThreshold }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - readOnlyRootFilesystem: true - runAsNonRoot: true - startupProbe: - exec: - command: [ "sh", "router_check_health.sh", "ready" ] - initialDelaySeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.periodSeconds }} - failureThreshold: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.readinessProbe.failureThreshold }} - {{- if and .Values.wso2.subscription .Values.wso2.subscription.imagePullSecrets}} - imagePullSecrets: - - name: {{ .Values.wso2.subscription.imagePullSecrets }} - {{ end }} - securityContext: - seccompProfile: - type: "RuntimeDefault" - volumes: - {{ if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.ratelimiter.enabled }} - - name: ratelimiter-truststore-secret-volume - secret: - {{- if and .Values.wso2.apk.dp.ratelimiter.deployment.configs .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls }} - secretName: {{ .Values.wso2.apk.dp.ratelimiter.deployment.configs.tls.certificatesSecret | default (printf "%s-ratelimiter-server-cert" (include "apk-helm.resource.prefix" .)) }} - {{- else }} - secretName: {{ template "apk-helm.resource.prefix" . }}-ratelimiter-server-cert - {{- end }} - {{ end }} - # - name: common-controller-truststore-secret-volume - # secret: - # {{- if and .Values.wso2.apk.dp.commonController.configs .Values.wso2.apk.dp.commonController.configs.tls }} - # secretName: {{ .Values.wso2.apk.dp.commonController.configs.tls.secretName | default (printf "%s-common-controller-server-cert" (include "apk-helm.resource.prefix" .)) }} - # {{- else }} - # secretName: {{ template "apk-helm.resource.prefix" . }}-common-controller-server-cert - # {{- end }} - # defaultMode: 420 - {{- if and .Values.wso2.apk.metrics .Values.wso2.apk.metrics.enabled }} - - name: prometheus-jmx-config-volume - configMap: - name: prometheus-jmx-config-enforcer - {{- end }} - - name: enforcer-keystore-secret-volume - secret: - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls }} - secretName: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.tls.certificatesSecret | default (printf "%s-enforcer-server-cert" (include "apk-helm.resource.prefix" .)) }} - {{- else }} - secretName: {{ template "apk-helm.resource.prefix" . }}-enforcer-server-cert - {{- end }} - - name: log-conf-volume - configMap: - name: {{ template "apk-helm.resource.prefix" . }}-log-conf - - name: router-keystore-secret-volume - secret: - {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls }} - secretName: {{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.tls.certificatesSecret | default (printf "%s-gateway-server-cert" (include "apk-helm.resource.prefix" .)) }} - {{- else }} - secretName: {{ template "apk-helm.resource.prefix" . }}-gateway-server-cert - {{- end }} - - name: adapter-truststore-secret-volume - secret: - {{- if and .Values.wso2.apk.dp.adapter.deployment.configs .Values.wso2.apk.dp.adapter.deployment.configs.tls }} - secretName: {{ .Values.wso2.apk.dp.adapter.deployment.configs.tls.secretName | default (printf "%s-adapter-server-cert" (include "apk-helm.resource.prefix" .)) }} - {{- else }} - secretName: {{ template "apk-helm.resource.prefix" . }}-adapter-server-cert - {{- end }} - - name: enforcer-jwt-secret-volume - secret: - secretName: {{ template "apk-helm.resource.prefix" . }}-enforcer-keystore-secret - - name: enforcer-trusted-certs - secret: - secretName: {{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret - - name: enforcer-apikey-cert - secret: - secretName: {{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret - - name: idp-certificate-secret-volume - secret: - {{ if and .Values.wso2.apk.idp.signing .Values.wso2.apk.idp.signing.secretName }} - secretName: {{ .Values.wso2.apk.idp.signing.secretName }} - {{else}} - secretName: {{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret - {{ end}} - {{ if and .Values.wso2.apk.idp.tls .Values.wso2.apk.idp.tls.secretName }} - - name: idp-tls-certificate-secret-volume - secret: - secretName: {{ .Values.wso2.apk.idp.tls.secretName }} - {{ end }} - - name: tmp - emptyDir: {} - {{end}} diff --git a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-service.yaml b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-service.yaml deleted file mode 100644 index 0173aed7a2..0000000000 --- a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/gateway-service.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. -# -# WSO2 LLC. 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. - -{{- if .Values.wso2.apk.dp.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "apk-helm.resource.prefix" . }}-gateway-service - namespace : {{ .Release.Namespace }} -{{if and .Values.wso2.apk.dp.gatewayRuntime.service .Values.wso2.apk.dp.gatewayRuntime.service.annotations }} - annotations: -{{ toYaml .Values.wso2.apk.dp.gatewayRuntime.service.annotations | indent 4 }} -{{ end }} -spec: - type: {{ .Values.wso2.apk.dp.gateway.service.type | default "LoadBalancer" }} - # label keys and values that must match in order to receive traffic for this service - selector: -{{ include "apk-helm.pod.selectorLabels" (dict "root" . "app" "gateway" ) | indent 4}} - ports: -{{ if and .Values.wso2.apk.dp.gateway.httpListener .Values.wso2.apk.dp.gateway.httpListener.enabled }} - - name: "http-endpoint" - protocol: TCP - port: {{ .Values.wso2.apk.dp.gateway.httpListener.port | default 9080 }} -{{ end }} - - name: "https-endpoint" - protocol: TCP - port: 9095 -{{- end -}} diff --git a/helm-charts/templates/data-plane/gateway-components/log-conf.yaml b/helm-charts/templates/data-plane/gateway-components/log-conf.yaml index f034acbe53..c04aa0e7ab 100644 --- a/helm-charts/templates/data-plane/gateway-components/log-conf.yaml +++ b/helm-charts/templates/data-plane/gateway-components/log-conf.yaml @@ -6,6 +6,27 @@ metadata: namespace: {{ .Release.Namespace }} data: config.toml: | + [deployment.gateway] + namespace = "{{ .Release.Namespace }}" + adapterHostName = "{{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc" + adapterHost = "{{ template "apk-helm.resource.prefix" . }}-adapter-service.{{ .Release.Namespace }}.svc" + commonControllerHostName = "{{ template "apk-helm.resource.prefix" . }}-common-controller-service.{{ .Release.Namespace }}.svc" + commonControllerHost = "{{ template "apk-helm.resource.prefix" . }}-common-controller-service.{{ .Release.Namespace }}.svc" + enforcerServerName = "{{ template "apk-helm.resource.prefix" . }}-enforcer-service.{{ .Release.Namespace }}.svc" + {{- if and .Values.wso2.apk.dp.gatewayRuntime.deployment .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.javaOpts }} + javaOpts = "{{ .Values.wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.javaOpts }}" + {{- end }} + + [deployment.gateway.volumes] + ratelimiterTruststoreSecretVolume = "{{ template "apk-helm.resource.prefix" . }}-ratelimiter-server-cert" + enforcerKeystoreSecretVolume = "{{ template "apk-helm.resource.prefix" . }}-enforcer-server-cert" + routerKeystoreSecretVolume = "envoy-cert" + adapterTruststoreSecretVolume = "{{ template "apk-helm.resource.prefix" . }}-adapter-server-cert" + enforcerJwtSecretVolume = "{{ template "apk-helm.resource.prefix" . }}-enforcer-keystore-secret" + enforcerTrustedCerts = "{{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret" + enforcerApikeyCert = "{{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret" + iDPCertificateSecretVolume = "{{ template "apk-helm.resource.prefix" . }}-enforcer-truststore-secret" + [adapter] {{- if and .Values.wso2.apk.dp.environment .Values.wso2.apk.dp.environment.name }} environment = "{{ .Values.wso2.apk.dp.environment.name }}" @@ -32,6 +53,7 @@ data: {{ if and .Values.wso2.apk.dp.gatewayRuntime.deployment .Values.wso2.apk.dp.gatewayRuntime.deployment.router .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs }} [router] + namespace = "{{ .Release.Namespace }}" {{ if .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.systemHost }} systemHost = "{{ .Values.wso2.apk.dp.gatewayRuntime.deployment.router.configs.systemHost }}" {{ end }} diff --git a/helm-charts/templates/serviceAccount/apk-cluster-role.yaml b/helm-charts/templates/serviceAccount/apk-cluster-role.yaml index ccbb849a5d..8b090ca0ee 100644 --- a/helm-charts/templates/serviceAccount/apk-cluster-role.yaml +++ b/helm-charts/templates/serviceAccount/apk-cluster-role.yaml @@ -20,12 +20,21 @@ kind: ClusterRole metadata: name: {{ .Values.wso2.apk.auth.roleName }} rules: + - apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["get","list","watch","update","delete","create"] - apiGroups: [""] - resources: ["services","configmaps","secrets"] + resources: ["services","configmaps","secrets", "namespaces", "serviceaccounts", "events", "nodes"] verbs: ["get","list","watch","update","delete","create"] + - apiGroups: ["autoscaling"] + resources: ["horizontalpodautoscalers"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["gateway.networking.k8s.io"] - resources: ["httproutes","gateways","gatewayclasses"] - verbs: ["get","list","watch","update","delete","create"] + resources: ["httproutes","gateways","gatewayclasses", "referencegrants"] + verbs: ["get","list","watch","update","delete","create", "patch"] - apiGroups: [ "gateway.networking.k8s.io" ] resources: [ "gateways/status","gatewayclasses/status","httproutes/status" ] verbs: [ "get","patch","update" ] diff --git a/helm-charts/values.yaml b/helm-charts/values.yaml index 79d83dd5c8..fa8671daa0 100644 --- a/helm-charts/values.yaml +++ b/helm-charts/values.yaml @@ -59,7 +59,8 @@ wso2: gateway: listener: hostname: "gw.wso2.com" - service: {} + service: + type: "NodePort" # secretName: "idp-tls" # partitionServer: # enabled: false @@ -124,8 +125,8 @@ wso2: image: apk-adapter:1.1.0-SNAPSHOT security: sslHostname: "adapter" - # logging: - # level: "INFO" # LogLevels can be "DEBG", "FATL", "ERRO", "WARN", "INFO", "PANC" + logging: + level: "DEBG" # LogLevels can be "DEBG", "FATL", "ERRO", "WARN", "INFO", "PANC" # # logFormat: "TEXT" # Values can be "JSON", "TEXT" # configs: # apiNamespaces: @@ -154,7 +155,7 @@ wso2: strategy: RollingUpdate replicas: 1 imagePullPolicy: IfNotPresent - image: apk-common-controller:1.1.0-SNAPSHOT + image: wso2/apk-common-controller:1.1.0 security: sslHostname: "commoncontroller" # configs: @@ -247,7 +248,8 @@ wso2: sslHostname: "enforcer" # logging: # level: DEBUG -# configs: + configs: + javaOpts: "-Dhttpclient.hostnameVerifier=AllowAll -Xms512m -Xmx512m -XX:MaxRAMFraction=2" # tls: # secretName: "router-cert" # certKeyFilename: ""