diff --git a/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_externalratelimits.yaml b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_externalratelimits.yaml index b7bf1ebe4..2e263a885 100644 --- a/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_externalratelimits.yaml +++ b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_externalratelimits.yaml @@ -62,6 +62,10 @@ spec: throttleHost: description: ThrottleHosts is the list of hosts to be throttled items: + description: HostPort is a host name with optional port number + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*(:[0-9]{1,5})?$ type: string maxItems: 32 minItems: 1 diff --git a/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml index cbc53ba5b..84b812862 100644 --- a/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml +++ b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml @@ -103,12 +103,14 @@ spec: is POST enum: - GET + - HEAD - POST - PUT - DELETE - - PATCH - - HEAD + - CONNECT - OPTIONS + - TRACE + - PATCH type: string target: description: Target is the URL of the HTTPLog service diff --git a/pkg/announcements/types.go b/pkg/announcements/types.go index 2dd678f37..16fd189c7 100644 --- a/pkg/announcements/types.go +++ b/pkg/announcements/types.go @@ -676,6 +676,50 @@ const ( // GatewayProxyTagUpdated is the type of announcement emitted when we observe an update to proxytags.extension.gateway.flomesh.io GatewayProxyTagUpdated Kind = "gatewayproxytag-updated" + + // --- + + // GatewayIPRestrictionAdded is the type of announcement emitted when we observe an addition of iprestrictions.extension.gateway.flomesh.io + GatewayIPRestrictionAdded Kind = "gatewayiprestriction-added" + + // GatewayIPRestrictionDeleted the type of announcement emitted when we observe a deletion of iprestrictions.extension.gateway.flomesh.io + GatewayIPRestrictionDeleted Kind = "gatewayiprestriction-deleted" + + // GatewayIPRestrictionUpdated is the type of announcement emitted when we observe an update to iprestrictions.extension.gateway.flomesh.io + GatewayIPRestrictionUpdated Kind = "gatewayiprestriction-updated" + + // --- + + // GatewayConcurrencyLimitAdded is the type of announcement emitted when we observe an addition of concurrencylimits.extension.gateway.flomesh.io + GatewayConcurrencyLimitAdded Kind = "gatewayconcurrencylimit-added" + + // GatewayConcurrencyLimitDeleted the type of announcement emitted when we observe a deletion of concurrencylimits.extension.gateway.flomesh.io + GatewayConcurrencyLimitDeleted Kind = "gatewayconcurrencylimit-deleted" + + // GatewayConcurrencyLimitUpdated is the type of announcement emitted when we observe an update to concurrencylimits.extension.gateway.flomesh.io + GatewayConcurrencyLimitUpdated Kind = "gatewayconcurrencylimit-updated" + + // --- + + // GatewayExternalRateLimitAdded is the type of announcement emitted when we observe an addition of externalratelimits.extension.gateway.flomesh.io + GatewayExternalRateLimitAdded Kind = "gatewayexternalratelimit-added" + + // GatewayExternalRateLimitDeleted the type of announcement emitted when we observe a deletion of externalratelimits.extension.gateway.flomesh.io + GatewayExternalRateLimitDeleted Kind = "gatewayexternalratelimit-deleted" + + // GatewayExternalRateLimitUpdated is the type of announcement emitted when we observe an update to externalratelimits.extension.gateway.flomesh.io + GatewayExternalRateLimitUpdated Kind = "gatewayexternalratelimit-updated" + + // --- + + // GatewayRequestTerminationAdded is the type of announcement emitted when we observe an addition of requestterminations.extension.gateway.flomesh.io + GatewayRequestTerminationAdded Kind = "gatewayrequesttermination-added" + + // GatewayRequestTerminationDeleted the type of announcement emitted when we observe a deletion of requestterminations.extension.gateway.flomesh.io + GatewayRequestTerminationDeleted Kind = "gatewayrequesttermination-deleted" + + // GatewayRequestTerminationUpdated is the type of announcement emitted when we observe an update to requestterminations.extension.gateway.flomesh.io + GatewayRequestTerminationUpdated Kind = "gatewayrequesttermination-updated" ) // Announcement is a struct for messages between various components of FSM signaling a need for a change in Sidecar proxy configuration diff --git a/pkg/apis/extension/v1alpha1/circuitbreaker.go b/pkg/apis/extension/v1alpha1/circuitbreaker.go index 60d542d34..dd880226d 100644 --- a/pkg/apis/extension/v1alpha1/circuitbreaker.go +++ b/pkg/apis/extension/v1alpha1/circuitbreaker.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // CircuitBreakerSpec defines the desired state of CircuitBreaker @@ -63,7 +64,7 @@ type CircuitBreakerResponse struct { // +optional // Headers is the HTTP headers of response - Headers map[string]string `json:"headers,omitempty"` + Headers map[gwv1.HeaderName]string `json:"headers,omitempty"` // +optional // +kubebuilder:default="Circuit breaker triggered" diff --git a/pkg/apis/extension/v1alpha1/externalratelimit.go b/pkg/apis/extension/v1alpha1/externalratelimit.go index 93de01793..93386f7ae 100644 --- a/pkg/apis/extension/v1alpha1/externalratelimit.go +++ b/pkg/apis/extension/v1alpha1/externalratelimit.go @@ -11,7 +11,7 @@ type ExternalRateLimitSpec struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=32 // ThrottleHosts is the list of hosts to be throttled - ThrottleHosts []string `json:"throttleHost,omitempty"` + ThrottleHosts []HostPort `json:"throttleHost,omitempty"` // +optional // +listType=set diff --git a/pkg/apis/extension/v1alpha1/faultinjection.go b/pkg/apis/extension/v1alpha1/faultinjection.go index 01a3c72e3..58aec5419 100644 --- a/pkg/apis/extension/v1alpha1/faultinjection.go +++ b/pkg/apis/extension/v1alpha1/faultinjection.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // FaultInjectionSpec defines the desired state of FaultInjection @@ -61,7 +62,7 @@ type FaultInjectionResponse struct { // +optional // Headers is the HTTP headers of response - Headers map[string]string `json:"headers,omitempty"` + Headers map[gwv1.HeaderName]string `json:"headers,omitempty"` // +optional // +kubebuilder:default="Fault injection triggered" diff --git a/pkg/apis/extension/v1alpha1/httplog.go b/pkg/apis/extension/v1alpha1/httplog.go index ed023dc12..30dae6e93 100644 --- a/pkg/apis/extension/v1alpha1/httplog.go +++ b/pkg/apis/extension/v1alpha1/httplog.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // HTTPLogSpec defines the desired state of HTTPLog @@ -12,13 +13,12 @@ type HTTPLogSpec struct { // +optional // +kubebuilder:default="POST" - // +kubebuilder:validation:Enum=GET;POST;PUT;DELETE;PATCH;HEAD;OPTIONS // Method is the HTTP method of the HTTPLog service, default is POST - Method *string `json:"method,omitempty"` + Method *gwv1.HTTPMethod `json:"method,omitempty"` // +optional // Headers is the HTTP headers of the log request - Headers map[string]string `json:"headers,omitempty"` + Headers map[gwv1.HeaderName]string `json:"headers,omitempty"` // +optional // +kubebuilder:default=1048576 diff --git a/pkg/apis/extension/v1alpha1/ratelimit.go b/pkg/apis/extension/v1alpha1/ratelimit.go index 97fdd9a6b..4b2a0002f 100644 --- a/pkg/apis/extension/v1alpha1/ratelimit.go +++ b/pkg/apis/extension/v1alpha1/ratelimit.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // RateLimitSpec defines the desired state of RateLimit @@ -53,7 +54,7 @@ type RateLimitResponse struct { // +optional // Headers is the HTTP headers of response - Headers map[string]string `json:"headers,omitempty"` + Headers map[gwv1.HeaderName]string `json:"headers,omitempty"` // +optional // +kubebuilder:default="Rate limit reached" diff --git a/pkg/apis/extension/v1alpha1/requesttermination.go b/pkg/apis/extension/v1alpha1/requesttermination.go index c8a212bf7..e6ef7718a 100644 --- a/pkg/apis/extension/v1alpha1/requesttermination.go +++ b/pkg/apis/extension/v1alpha1/requesttermination.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // RequestTerminationSpec defines the desired state of RequestTermination @@ -19,7 +20,7 @@ type RequestTerminationResponse struct { // +optional // Headers is the HTTP headers of response - Headers map[string]string `json:"headers,omitempty"` + Headers map[gwv1.HeaderName]string `json:"headers,omitempty"` // +optional // +kubebuilder:default="Request termination triggered" diff --git a/pkg/apis/extension/v1alpha1/shared_types.go b/pkg/apis/extension/v1alpha1/shared_types.go index 15bcc6bed..4e59cfe95 100644 --- a/pkg/apis/extension/v1alpha1/shared_types.go +++ b/pkg/apis/extension/v1alpha1/shared_types.go @@ -100,3 +100,9 @@ const ( // FilterAspectRoute is the aspect of filter for route FilterAspectRoute FilterAspect = "Route" ) + +// HostPort is a host name with optional port number +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=253 +// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*(:[0-9]{1,5})?$` +type HostPort string diff --git a/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go index ab59d02eb..08dde23dd 100644 --- a/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go @@ -19,9 +19,9 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" - apisv1 "sigs.k8s.io/gateway-api/apis/v1" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -95,7 +95,7 @@ func (in *CircuitBreakerResponse) DeepCopyInto(out *CircuitBreakerResponse) { } if in.Headers != nil { in, out := &in.Headers, &out.Headers - *out = make(map[string]string, len(*in)) + *out = make(map[v1.HeaderName]string, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -123,7 +123,7 @@ func (in *CircuitBreakerSpec) DeepCopyInto(out *CircuitBreakerSpec) { *out = *in if in.LatencyThreshold != nil { in, out := &in.LatencyThreshold, &out.LatencyThreshold - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.ErrorCountThreshold != nil { @@ -143,12 +143,12 @@ func (in *CircuitBreakerSpec) DeepCopyInto(out *CircuitBreakerSpec) { } if in.CheckInterval != nil { in, out := &in.CheckInterval, &out.CheckInterval - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.BreakInterval != nil { in, out := &in.BreakInterval, &out.BreakInterval - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.CircuitBreakerResponse != nil { @@ -174,7 +174,7 @@ func (in *CircuitBreakerStatus) DeepCopyInto(out *CircuitBreakerStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -279,7 +279,7 @@ func (in *ConcurrencyLimitStatus) DeepCopyInto(out *ConcurrencyLimitStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -363,12 +363,12 @@ func (in *ExternalRateLimitSpec) DeepCopyInto(out *ExternalRateLimitSpec) { *out = *in if in.ThrottleHosts != nil { in, out := &in.ThrottleHosts, &out.ThrottleHosts - *out = make([]string, len(*in)) + *out = make([]HostPort, len(*in)) copy(*out, *in) } if in.PassHeaders != nil { in, out := &in.PassHeaders, &out.PassHeaders - *out = make([]apisv1.HeaderName, len(*in)) + *out = make([]v1.HeaderName, len(*in)) copy(*out, *in) } return @@ -389,7 +389,7 @@ func (in *ExternalRateLimitStatus) DeepCopyInto(out *ExternalRateLimitStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -461,12 +461,12 @@ func (in *FaultInjectionDelay) DeepCopyInto(out *FaultInjectionDelay) { *out = *in if in.Min != nil { in, out := &in.Min, &out.Min - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.Max != nil { in, out := &in.Max, &out.Max - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } return @@ -525,7 +525,7 @@ func (in *FaultInjectionResponse) DeepCopyInto(out *FaultInjectionResponse) { } if in.Headers != nil { in, out := &in.Headers, &out.Headers - *out = make(map[string]string, len(*in)) + *out = make(map[v1.HeaderName]string, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -579,7 +579,7 @@ func (in *FaultInjectionStatus) DeepCopyInto(out *FaultInjectionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -707,7 +707,7 @@ func (in *FilterConfigStatus) DeepCopyInto(out *FilterConfigStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -817,7 +817,7 @@ func (in *FilterDefinitionStatus) DeepCopyInto(out *FilterDefinitionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -873,12 +873,12 @@ func (in *FilterSpec) DeepCopyInto(out *FilterSpec) { *out = *in if in.DefinitionRef != nil { in, out := &in.DefinitionRef, &out.DefinitionRef - *out = new(apisv1.LocalObjectReference) + *out = new(v1.LocalObjectReference) **out = **in } if in.ConfigRef != nil { in, out := &in.ConfigRef, &out.ConfigRef - *out = new(apisv1.LocalObjectReference) + *out = new(v1.LocalObjectReference) **out = **in } return @@ -899,7 +899,7 @@ func (in *FilterStatus) DeepCopyInto(out *FilterStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -955,7 +955,7 @@ func (in *HTTPLogBatch) DeepCopyInto(out *HTTPLogBatch) { } if in.Interval != nil { in, out := &in.Interval, &out.Interval - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.Prefix != nil { @@ -1024,12 +1024,12 @@ func (in *HTTPLogSpec) DeepCopyInto(out *HTTPLogSpec) { *out = *in if in.Method != nil { in, out := &in.Method, &out.Method - *out = new(string) + *out = new(v1.HTTPMethod) **out = **in } if in.Headers != nil { in, out := &in.Headers, &out.Headers - *out = make(map[string]string, len(*in)) + *out = make(map[v1.HeaderName]string, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -1062,7 +1062,7 @@ func (in *HTTPLogStatus) DeepCopyInto(out *HTTPLogStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1172,7 +1172,7 @@ func (in *IPRestrictionStatus) DeepCopyInto(out *IPRestrictionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1271,12 +1271,12 @@ func (in *ListenerFilterSpec) DeepCopyInto(out *ListenerFilterSpec) { } if in.DefinitionRef != nil { in, out := &in.DefinitionRef, &out.DefinitionRef - *out = new(apisv1.LocalObjectReference) + *out = new(v1.LocalObjectReference) **out = **in } if in.ConfigRef != nil { in, out := &in.ConfigRef, &out.ConfigRef - *out = new(apisv1.LocalObjectReference) + *out = new(v1.LocalObjectReference) **out = **in } return @@ -1297,7 +1297,7 @@ func (in *ListenerFilterStatus) DeepCopyInto(out *ListenerFilterStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1397,7 +1397,7 @@ func (in *MetricsSpec) DeepCopyInto(out *MetricsSpec) { *out = *in if in.SampleInterval != nil { in, out := &in.SampleInterval, &out.SampleInterval - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } return @@ -1418,7 +1418,7 @@ func (in *MetricsStatus) DeepCopyInto(out *MetricsStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1502,12 +1502,12 @@ func (in *ProxyTagSpec) DeepCopyInto(out *ProxyTagSpec) { *out = *in if in.DestinationHostHeader != nil { in, out := &in.DestinationHostHeader, &out.DestinationHostHeader - *out = new(apisv1.HeaderName) + *out = new(v1.HeaderName) **out = **in } if in.SourceHostHeader != nil { in, out := &in.SourceHostHeader, &out.SourceHostHeader - *out = new(apisv1.HeaderName) + *out = new(v1.HeaderName) **out = **in } return @@ -1528,7 +1528,7 @@ func (in *ProxyTagStatus) DeepCopyInto(out *ProxyTagStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1617,7 +1617,7 @@ func (in *RateLimitResponse) DeepCopyInto(out *RateLimitResponse) { } if in.Headers != nil { in, out := &in.Headers, &out.Headers - *out = make(map[string]string, len(*in)) + *out = make(map[v1.HeaderName]string, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -1655,7 +1655,7 @@ func (in *RateLimitSpec) DeepCopyInto(out *RateLimitSpec) { } if in.Interval != nil { in, out := &in.Interval, &out.Interval - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.Backlog != nil { @@ -1691,7 +1691,7 @@ func (in *RateLimitStatus) DeepCopyInto(out *RateLimitStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1775,7 +1775,7 @@ func (in *RequestTerminationResponse) DeepCopyInto(out *RequestTerminationRespon *out = *in if in.Headers != nil { in, out := &in.Headers, &out.Headers - *out = make(map[string]string, len(*in)) + *out = make(map[v1.HeaderName]string, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -1820,7 +1820,7 @@ func (in *RequestTerminationStatus) DeepCopyInto(out *RequestTerminationStatus) *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1925,7 +1925,7 @@ func (in *ZipkinStatus) DeepCopyInto(out *ZipkinStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 4ba5bcc54..714bed87e 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -508,6 +508,18 @@ const ( // GatewayProxyTagKind is the kind name of ProxyTag used in Flomesh API GatewayProxyTagKind = "ProxyTag" + + // GatewayIPRestrictionKind is the kind name of IPRestriction used in Flomesh API + GatewayIPRestrictionKind = "IPRestriction" + + // GatewayExternalRateLimitKind is the kind name of ExternalRateLimit used in Flomesh API + GatewayExternalRateLimitKind = "ExternalRateLimit" + + // GatewayRequestTerminationKind is the kind name of RequestTermination used in Flomesh API + GatewayRequestTerminationKind = "RequestTermination" + + // GatewayConcurrencyLimitKind is the kind name of ConcurrencyLimit used in Flomesh API + GatewayConcurrencyLimitKind = "ConcurrencyLimit" ) // Gateway API Annotations and Labels diff --git a/pkg/controllers/extension/v1alpha1/concurrencylimit_controller.go b/pkg/controllers/extension/v1alpha1/concurrencylimit_controller.go new file mode 100644 index 000000000..cd1931b83 --- /dev/null +++ b/pkg/controllers/extension/v1alpha1/concurrencylimit_controller.go @@ -0,0 +1,89 @@ +package v1alpha1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + fctx "github.com/flomesh-io/fsm/pkg/context" + "github.com/flomesh-io/fsm/pkg/controllers" +) + +type concurrencyLimitReconciler struct { + recorder record.EventRecorder + fctx *fctx.ControllerContext +} + +func (r *concurrencyLimitReconciler) NeedLeaderElection() bool { + return true +} + +// NewConcurrencyLimitReconciler returns a new ConcurrencyLimit Reconciler +func NewConcurrencyLimitReconciler(ctx *fctx.ControllerContext) controllers.Reconciler { + return &concurrencyLimitReconciler{ + recorder: ctx.Manager.GetEventRecorderFor("ConcurrencyLimit"), + fctx: ctx, + } +} + +// Reconcile reads that state of the cluster for a ConcurrencyLimit object and makes changes based on the state read +func (r *concurrencyLimitReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + concurrencyLimit := &extv1alpha1.ConcurrencyLimit{} + err := r.fctx.Get(ctx, req.NamespacedName, concurrencyLimit) + if errors.IsNotFound(err) { + r.fctx.GatewayEventHandler.OnDelete(&extv1alpha1.ConcurrencyLimit{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }}) + return reconcile.Result{}, nil + } + + if concurrencyLimit.DeletionTimestamp != nil { + r.fctx.GatewayEventHandler.OnDelete(concurrencyLimit) + return ctrl.Result{}, nil + } + + // As ConcurrencyLimit has no status, we don't need to update it + + r.fctx.GatewayEventHandler.OnAdd(concurrencyLimit, false) + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *concurrencyLimitReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := ctrl.NewControllerManagedBy(mgr). + For(&extv1alpha1.ConcurrencyLimit{}). + Complete(r); err != nil { + return err + } + + return addConcurrencyLimitIndexers(context.Background(), mgr) +} + +func addConcurrencyLimitIndexers(ctx context.Context, mgr manager.Manager) error { + //if err := mgr.GetFieldIndexer().IndexField(ctx, &extv1alpha1.ListenerConcurrencyLimit{}, constants.GatewayListenerConcurrencyLimitIndex, func(obj client.Object) []string { + // concurrencyLimit := obj.(*extv1alpha1.ListenerConcurrencyLimit) + // + // var gateways []string + // for _, targetRef := range concurrencyLimit.Spec.TargetRefs { + // if string(targetRef.Kind) == constants.GatewayAPIGatewayKind && + // string(targetRef.Group) == gwv1.GroupName { + // gateways = append(gateways, fmt.Sprintf("%s/%d", string(targetRef.Name), targetRef.Port)) + // } + // } + // + // return gateways + //}); err != nil { + // return err + //} + + return nil +} diff --git a/pkg/controllers/extension/v1alpha1/externalratelimit_controller.go b/pkg/controllers/extension/v1alpha1/externalratelimit_controller.go new file mode 100644 index 000000000..50b89bd9b --- /dev/null +++ b/pkg/controllers/extension/v1alpha1/externalratelimit_controller.go @@ -0,0 +1,89 @@ +package v1alpha1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + fctx "github.com/flomesh-io/fsm/pkg/context" + "github.com/flomesh-io/fsm/pkg/controllers" +) + +type externalRateLimitReconciler struct { + recorder record.EventRecorder + fctx *fctx.ControllerContext +} + +func (r *externalRateLimitReconciler) NeedLeaderElection() bool { + return true +} + +// NewExternalRateLimitReconciler returns a new ExternalRateLimit Reconciler +func NewExternalRateLimitReconciler(ctx *fctx.ControllerContext) controllers.Reconciler { + return &externalRateLimitReconciler{ + recorder: ctx.Manager.GetEventRecorderFor("ExternalRateLimit"), + fctx: ctx, + } +} + +// Reconcile reads that state of the cluster for a ExternalRateLimit object and makes changes based on the state read +func (r *externalRateLimitReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + externalRateLimit := &extv1alpha1.ExternalRateLimit{} + err := r.fctx.Get(ctx, req.NamespacedName, externalRateLimit) + if errors.IsNotFound(err) { + r.fctx.GatewayEventHandler.OnDelete(&extv1alpha1.ExternalRateLimit{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }}) + return reconcile.Result{}, nil + } + + if externalRateLimit.DeletionTimestamp != nil { + r.fctx.GatewayEventHandler.OnDelete(externalRateLimit) + return ctrl.Result{}, nil + } + + // As ExternalRateLimit has no status, we don't need to update it + + r.fctx.GatewayEventHandler.OnAdd(externalRateLimit, false) + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *externalRateLimitReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := ctrl.NewControllerManagedBy(mgr). + For(&extv1alpha1.ExternalRateLimit{}). + Complete(r); err != nil { + return err + } + + return addExternalRateLimitIndexers(context.Background(), mgr) +} + +func addExternalRateLimitIndexers(ctx context.Context, mgr manager.Manager) error { + //if err := mgr.GetFieldIndexer().IndexField(ctx, &extv1alpha1.ListenerExternalRateLimit{}, constants.GatewayListenerExternalRateLimitIndex, func(obj client.Object) []string { + // externalRateLimit := obj.(*extv1alpha1.ListenerExternalRateLimit) + // + // var gateways []string + // for _, targetRef := range externalRateLimit.Spec.TargetRefs { + // if string(targetRef.Kind) == constants.GatewayAPIGatewayKind && + // string(targetRef.Group) == gwv1.GroupName { + // gateways = append(gateways, fmt.Sprintf("%s/%d", string(targetRef.Name), targetRef.Port)) + // } + // } + // + // return gateways + //}); err != nil { + // return err + //} + + return nil +} diff --git a/pkg/controllers/extension/v1alpha1/iptrestriction_controller.go b/pkg/controllers/extension/v1alpha1/iptrestriction_controller.go new file mode 100644 index 000000000..6e8ec0967 --- /dev/null +++ b/pkg/controllers/extension/v1alpha1/iptrestriction_controller.go @@ -0,0 +1,104 @@ +package v1alpha1 + +import ( + "context" + + whtypes "github.com/flomesh-io/fsm/pkg/webhook/types" + + whblder "github.com/flomesh-io/fsm/pkg/webhook/builder" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + fctx "github.com/flomesh-io/fsm/pkg/context" + "github.com/flomesh-io/fsm/pkg/controllers" +) + +type ipRestrictionReconciler struct { + recorder record.EventRecorder + fctx *fctx.ControllerContext + webhook whtypes.Register +} + +func (r *ipRestrictionReconciler) NeedLeaderElection() bool { + return true +} + +// NewIPRestrictionReconciler returns a new IPRestriction Reconciler +func NewIPRestrictionReconciler(ctx *fctx.ControllerContext, webhook whtypes.Register) controllers.Reconciler { + return &ipRestrictionReconciler{ + recorder: ctx.Manager.GetEventRecorderFor("IPRestriction"), + fctx: ctx, + webhook: webhook, + } +} + +// Reconcile reads that state of the cluster for a IPRestriction object and makes changes based on the state read +func (r *ipRestrictionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + ipRestriction := &extv1alpha1.IPRestriction{} + err := r.fctx.Get(ctx, req.NamespacedName, ipRestriction) + if errors.IsNotFound(err) { + r.fctx.GatewayEventHandler.OnDelete(&extv1alpha1.IPRestriction{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }}) + return reconcile.Result{}, nil + } + + if ipRestriction.DeletionTimestamp != nil { + r.fctx.GatewayEventHandler.OnDelete(ipRestriction) + return ctrl.Result{}, nil + } + + // As IPRestriction has no status, we don't need to update it + + r.fctx.GatewayEventHandler.OnAdd(ipRestriction, false) + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ipRestrictionReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := whblder.WebhookManagedBy(mgr). + For(&extv1alpha1.IPRestriction{}). + WithDefaulter(r.webhook). + WithValidator(r.webhook). + RecoverPanic(). + Complete(); err != nil { + return err + } + + if err := ctrl.NewControllerManagedBy(mgr). + For(&extv1alpha1.IPRestriction{}). + Complete(r); err != nil { + return err + } + + return addIPRestrictionIndexers(context.Background(), mgr) +} + +func addIPRestrictionIndexers(ctx context.Context, mgr manager.Manager) error { + //if err := mgr.GetFieldIndexer().IndexField(ctx, &extv1alpha1.ListenerIPRestriction{}, constants.GatewayListenerIPRestrictionIndex, func(obj client.Object) []string { + // ipRestriction := obj.(*extv1alpha1.ListenerIPRestriction) + // + // var gateways []string + // for _, targetRef := range ipRestriction.Spec.TargetRefs { + // if string(targetRef.Kind) == constants.GatewayAPIGatewayKind && + // string(targetRef.Group) == gwv1.GroupName { + // gateways = append(gateways, fmt.Sprintf("%s/%d", string(targetRef.Name), targetRef.Port)) + // } + // } + // + // return gateways + //}); err != nil { + // return err + //} + + return nil +} diff --git a/pkg/controllers/extension/v1alpha1/requesttermination_controller.go b/pkg/controllers/extension/v1alpha1/requesttermination_controller.go new file mode 100644 index 000000000..ebf3c36ec --- /dev/null +++ b/pkg/controllers/extension/v1alpha1/requesttermination_controller.go @@ -0,0 +1,89 @@ +package v1alpha1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + fctx "github.com/flomesh-io/fsm/pkg/context" + "github.com/flomesh-io/fsm/pkg/controllers" +) + +type requestTerminationReconciler struct { + recorder record.EventRecorder + fctx *fctx.ControllerContext +} + +func (r *requestTerminationReconciler) NeedLeaderElection() bool { + return true +} + +// NewRequestTerminationReconciler returns a new RequestTermination Reconciler +func NewRequestTerminationReconciler(ctx *fctx.ControllerContext) controllers.Reconciler { + return &requestTerminationReconciler{ + recorder: ctx.Manager.GetEventRecorderFor("RequestTermination"), + fctx: ctx, + } +} + +// Reconcile reads that state of the cluster for a RequestTermination object and makes changes based on the state read +func (r *requestTerminationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + requestTermination := &extv1alpha1.RequestTermination{} + err := r.fctx.Get(ctx, req.NamespacedName, requestTermination) + if errors.IsNotFound(err) { + r.fctx.GatewayEventHandler.OnDelete(&extv1alpha1.RequestTermination{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }}) + return reconcile.Result{}, nil + } + + if requestTermination.DeletionTimestamp != nil { + r.fctx.GatewayEventHandler.OnDelete(requestTermination) + return ctrl.Result{}, nil + } + + // As RequestTermination has no status, we don't need to update it + + r.fctx.GatewayEventHandler.OnAdd(requestTermination, false) + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *requestTerminationReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := ctrl.NewControllerManagedBy(mgr). + For(&extv1alpha1.RequestTermination{}). + Complete(r); err != nil { + return err + } + + return addRequestTerminationIndexers(context.Background(), mgr) +} + +func addRequestTerminationIndexers(ctx context.Context, mgr manager.Manager) error { + //if err := mgr.GetFieldIndexer().IndexField(ctx, &extv1alpha1.ListenerRequestTermination{}, constants.GatewayListenerRequestTerminationIndex, func(obj client.Object) []string { + // requestTermination := obj.(*extv1alpha1.ListenerRequestTermination) + // + // var gateways []string + // for _, targetRef := range requestTermination.Spec.TargetRefs { + // if string(targetRef.Kind) == constants.GatewayAPIGatewayKind && + // string(targetRef.Group) == gwv1.GroupName { + // gateways = append(gateways, fmt.Sprintf("%s/%d", string(targetRef.Name), targetRef.Port)) + // } + // } + // + // return gateways + //}); err != nil { + // return err + //} + + return nil +} diff --git a/pkg/gateway/client.go b/pkg/gateway/client.go index 4cd665f01..208cda1f3 100644 --- a/pkg/gateway/client.go +++ b/pkg/gateway/client.go @@ -105,6 +105,10 @@ func newClient(ctx *cctx.ControllerContext) *client { fsminformers.InformerKeyGatewayMetrics: &extv1alpha1.Metrics{}, fsminformers.InformerKeyGatewayZipkin: &extv1alpha1.Zipkin{}, fsminformers.InformerKeyGatewayProxyTag: &extv1alpha1.ProxyTag{}, + fsminformers.InformerKeyGatewayExternalRateLimit: &extv1alpha1.ExternalRateLimit{}, + fsminformers.InformerKeyGatewayIPRestriction: &extv1alpha1.IPRestriction{}, + fsminformers.InformerKeyGatewayRequestTermination: &extv1alpha1.RequestTermination{}, + fsminformers.InformerKeyGatewayConcurrencyLimit: &extv1alpha1.ConcurrencyLimit{}, } if version.IsEndpointSliceEnabled(ctx.KubeClient) { diff --git a/pkg/gateway/informers.go b/pkg/gateway/informers.go index 3f9c5b4cc..98ed1af88 100644 --- a/pkg/gateway/informers.go +++ b/pkg/gateway/informers.go @@ -17,6 +17,7 @@ import ( fsminformers "github.com/flomesh-io/fsm/pkg/k8s/informers" ) +//gocyclo:ignore func getEventTypesByObjectType(obj interface{}) *k8s.EventTypes { switch obj.(type) { case *corev1.Service: @@ -75,11 +76,20 @@ func getEventTypesByObjectType(obj interface{}) *k8s.EventTypes { return getEventTypesByInformerKey(fsminformers.InformerKeyFilterConfig) case *extv1alpha1.ProxyTag: return getEventTypesByInformerKey(fsminformers.InformerKeyGatewayProxyTag) + case *extv1alpha1.ExternalRateLimit: + return getEventTypesByInformerKey(fsminformers.InformerKeyGatewayExternalRateLimit) + case *extv1alpha1.IPRestriction: + return getEventTypesByInformerKey(fsminformers.InformerKeyGatewayIPRestriction) + case *extv1alpha1.RequestTermination: + return getEventTypesByInformerKey(fsminformers.InformerKeyGatewayRequestTermination) + case *extv1alpha1.ConcurrencyLimit: + return getEventTypesByInformerKey(fsminformers.InformerKeyGatewayConcurrencyLimit) } return nil } +//gocyclo:ignore func getEventTypesByInformerKey(informerKey fsminformers.InformerKey) *k8s.EventTypes { switch informerKey { case fsminformers.InformerKeyService: @@ -251,6 +261,30 @@ func getEventTypesByInformerKey(informerKey fsminformers.InformerKey) *k8s.Event Update: announcements.GatewayProxyTagUpdated, Delete: announcements.GatewayProxyTagDeleted, } + case fsminformers.InformerKeyGatewayExternalRateLimit: + return &k8s.EventTypes{ + Add: announcements.GatewayExternalRateLimitAdded, + Update: announcements.GatewayExternalRateLimitUpdated, + Delete: announcements.GatewayExternalRateLimitDeleted, + } + case fsminformers.InformerKeyGatewayIPRestriction: + return &k8s.EventTypes{ + Add: announcements.GatewayIPRestrictionAdded, + Update: announcements.GatewayIPRestrictionUpdated, + Delete: announcements.GatewayIPRestrictionDeleted, + } + case fsminformers.InformerKeyGatewayRequestTermination: + return &k8s.EventTypes{ + Add: announcements.GatewayRequestTerminationAdded, + Update: announcements.GatewayRequestTerminationUpdated, + Delete: announcements.GatewayRequestTerminationDeleted, + } + case fsminformers.InformerKeyGatewayConcurrencyLimit: + return &k8s.EventTypes{ + Add: announcements.GatewayConcurrencyLimitAdded, + Update: announcements.GatewayConcurrencyLimitUpdated, + Delete: announcements.GatewayConcurrencyLimitDeleted, + } } return nil diff --git a/pkg/gateway/processor/triggers/extension/concurrencylimits_trigger.go b/pkg/gateway/processor/triggers/extension/concurrencylimits_trigger.go new file mode 100644 index 000000000..979f9940f --- /dev/null +++ b/pkg/gateway/processor/triggers/extension/concurrencylimits_trigger.go @@ -0,0 +1,34 @@ +package extension + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + + "github.com/flomesh-io/fsm/pkg/gateway/processor" +) + +// ConcurrencyLimitTrigger is a processor for ConcurrencyLimit objects +type ConcurrencyLimitTrigger struct{} + +// Insert adds a ConcurrencyLimit object to the processor and returns true if the processor is changed +func (p *ConcurrencyLimitTrigger) Insert(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.ConcurrencyLimit) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} + +// Delete removes a ConcurrencyLimit object from the processor and returns true if the processor is changed +func (p *ConcurrencyLimitTrigger) Delete(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.ConcurrencyLimit) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} diff --git a/pkg/gateway/processor/triggers/extension/externalratelimits_trigger.go b/pkg/gateway/processor/triggers/extension/externalratelimits_trigger.go new file mode 100644 index 000000000..536b72d20 --- /dev/null +++ b/pkg/gateway/processor/triggers/extension/externalratelimits_trigger.go @@ -0,0 +1,34 @@ +package extension + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + + "github.com/flomesh-io/fsm/pkg/gateway/processor" +) + +// ExternalRateLimitTrigger is a processor for ExternalRateLimit objects +type ExternalRateLimitTrigger struct{} + +// Insert adds a ExternalRateLimit object to the processor and returns true if the processor is changed +func (p *ExternalRateLimitTrigger) Insert(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.ExternalRateLimit) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} + +// Delete removes a ExternalRateLimit object from the processor and returns true if the processor is changed +func (p *ExternalRateLimitTrigger) Delete(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.ExternalRateLimit) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} diff --git a/pkg/gateway/processor/triggers/extension/iprestrictions_trigger.go b/pkg/gateway/processor/triggers/extension/iprestrictions_trigger.go new file mode 100644 index 000000000..a4a7dffbe --- /dev/null +++ b/pkg/gateway/processor/triggers/extension/iprestrictions_trigger.go @@ -0,0 +1,34 @@ +package extension + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + + "github.com/flomesh-io/fsm/pkg/gateway/processor" +) + +// IPRestrictionTrigger is a processor for IPRestriction objects +type IPRestrictionTrigger struct{} + +// Insert adds a IPRestriction object to the processor and returns true if the processor is changed +func (p *IPRestrictionTrigger) Insert(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.IPRestriction) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} + +// Delete removes a IPRestriction object from the processor and returns true if the processor is changed +func (p *IPRestrictionTrigger) Delete(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.IPRestriction) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} diff --git a/pkg/gateway/processor/triggers/extension/requestterminations_trigger.go b/pkg/gateway/processor/triggers/extension/requestterminations_trigger.go new file mode 100644 index 000000000..cf70d66e7 --- /dev/null +++ b/pkg/gateway/processor/triggers/extension/requestterminations_trigger.go @@ -0,0 +1,34 @@ +package extension + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + + "github.com/flomesh-io/fsm/pkg/gateway/processor" +) + +// RequestTerminationTrigger is a processor for RequestTermination objects +type RequestTerminationTrigger struct{} + +// Insert adds a RequestTermination object to the processor and returns true if the processor is changed +func (p *RequestTerminationTrigger) Insert(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.RequestTermination) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} + +// Delete removes a RequestTermination object from the processor and returns true if the processor is changed +func (p *RequestTerminationTrigger) Delete(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.RequestTermination) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} diff --git a/pkg/gateway/processor/v2/filters.go b/pkg/gateway/processor/v2/filters.go index ab108cb8a..85e7df8bc 100644 --- a/pkg/gateway/processor/v2/filters.go +++ b/pkg/gateway/processor/v2/filters.go @@ -42,6 +42,7 @@ func (c *ConfigGenerator) resolveFilterDefinition(filterType extv1alpha1.FilterT return definition } +//gocyclo:ignore func (c *ConfigGenerator) resolveFilterConfig(ref *gwv1.LocalObjectReference) map[string]interface{} { if ref == nil { return map[string]interface{}{} @@ -137,6 +138,38 @@ func (c *ConfigGenerator) resolveFilterConfig(ref *gwv1.LocalObjectReference) ma } return toMap("proxyTag", &obj.Spec) + case constants.GatewayIPRestrictionKind: + obj := &extv1alpha1.IPRestriction{} + if err := c.client.Get(ctx, key, obj); err != nil { + log.Error().Msgf("Failed to resolve IPRestriction: %s", err) + return map[string]interface{}{} + } + + return toMap("ipRestriction", &obj.Spec) + case constants.GatewayExternalRateLimitKind: + obj := &extv1alpha1.ExternalRateLimit{} + if err := c.client.Get(ctx, key, obj); err != nil { + log.Error().Msgf("Failed to resolve ExternalRateLimit: %s", err) + return map[string]interface{}{} + } + + return toMap("externalRateLimit", &obj.Spec) + case constants.GatewayRequestTerminationKind: + obj := &extv1alpha1.RequestTermination{} + if err := c.client.Get(ctx, key, obj); err != nil { + log.Error().Msgf("Failed to resolve RequestTermination: %s", err) + return map[string]interface{}{} + } + + return toMap("requestTermination", &obj.Spec) + case constants.GatewayConcurrencyLimitKind: + obj := &extv1alpha1.ConcurrencyLimit{} + if err := c.client.Get(ctx, key, obj); err != nil { + log.Error().Msgf("Failed to resolve ConcurrencyLimit: %s", err) + return map[string]interface{}{} + } + + return toMap("concurrencyLimit", &obj.Spec) case constants.GatewayAPIExtensionFilterConfigKind: obj := &extv1alpha1.FilterConfig{} if err := c.client.Get(ctx, key, obj); err != nil { diff --git a/pkg/gateway/processor/v2/processor.go b/pkg/gateway/processor/v2/processor.go index a65fcebb1..10067defd 100644 --- a/pkg/gateway/processor/v2/processor.go +++ b/pkg/gateway/processor/v2/processor.go @@ -50,6 +50,8 @@ type GatewayProcessor struct { } // NewGatewayProcessor creates a new gateway processor +// +//gocyclo:ignore func NewGatewayProcessor(ctx *cctx.ControllerContext) *GatewayProcessor { cfg := ctx.Configurator repoBaseURL := fmt.Sprintf("%s://%s:%d", "http", cfg.GetRepoServerIPAddr(), cfg.GetProxyServerPort()) @@ -88,6 +90,10 @@ func NewGatewayProcessor(ctx *cctx.ControllerContext) *GatewayProcessor { informers.ZipkinResourceType: &extensiontrigger.ZipkinTrigger{}, informers.FilterConfigsResourceType: &extensiontrigger.FilterConfigTrigger{}, informers.ProxyTagResourceType: &extensiontrigger.ProxyTagTrigger{}, + informers.IPRestrictionResourceType: &extensiontrigger.IPRestrictionTrigger{}, + informers.ExternalRateLimitResourceType: &extensiontrigger.ExternalRateLimitTrigger{}, + informers.RequestTerminationResourceType: &extensiontrigger.RequestTerminationTrigger{}, + informers.ConcurrencyLimitResourceType: &extensiontrigger.ConcurrencyLimitTrigger{}, }, mutex: new(sync.RWMutex), @@ -115,6 +121,7 @@ func (c *GatewayProcessor) Delete(obj interface{}) bool { return false } +//gocyclo:ignore func (c *GatewayProcessor) getTrigger(obj interface{}) processor.Trigger { switch obj.(type) { case *corev1.Endpoints: @@ -173,6 +180,14 @@ func (c *GatewayProcessor) getTrigger(obj interface{}) processor.Trigger { return c.triggers[informers.FilterConfigsResourceType] case *extv1alpha1.ProxyTag: return c.triggers[informers.ProxyTagResourceType] + case *extv1alpha1.IPRestriction: + return c.triggers[informers.IPRestrictionResourceType] + case *extv1alpha1.ExternalRateLimit: + return c.triggers[informers.ExternalRateLimitResourceType] + case *extv1alpha1.RequestTermination: + return c.triggers[informers.RequestTerminationResourceType] + case *extv1alpha1.ConcurrencyLimit: + return c.triggers[informers.ConcurrencyLimitResourceType] } return nil diff --git a/pkg/k8s/informers/types.go b/pkg/k8s/informers/types.go index e8e1034c2..970a76800 100644 --- a/pkg/k8s/informers/types.go +++ b/pkg/k8s/informers/types.go @@ -151,6 +151,14 @@ const ( InformerKeyGatewayZipkin InformerKey = "Gateway-Zipkin" // InformerKeyGatewayProxyTag is the InformerKey for a ProxyTag informer InformerKeyGatewayProxyTag InformerKey = "Gateway-ProxyTag" + // InformerKeyGatewayIPRestriction is the InformerKey for a IPRestriction informer + InformerKeyGatewayIPRestriction InformerKey = "Gateway-IPRestriction" + // InformerKeyGatewayConcurrencyLimit is the InformerKey for a ConcurrencyLimit informer + InformerKeyGatewayConcurrencyLimit InformerKey = "Gateway-ConcurrencyLimit" + // InformerKeyGatewayRequestTermination is the InformerKey for a RequestTermination informer + InformerKeyGatewayRequestTermination InformerKey = "Gateway-RequestTermination" + // InformerKeyGatewayExternalRateLimit is the InformerKey for a ExternalRateLimit informer + InformerKeyGatewayExternalRateLimit InformerKey = "Gateway-ExternalRateLimit" // InformerKeyXNetworkAccessControl is the InformerKey for a XNetwork AccessControl informer InformerKeyXNetworkAccessControl InformerKey = "XNetwork-AccessControl" @@ -267,4 +275,16 @@ const ( // ProxyTagResourceType is the type used to represent the proxy tag resource ProxyTagResourceType ResourceType = "proxytags" + + // IPRestrictionResourceType is the type used to represent the ip restriction resource + IPRestrictionResourceType ResourceType = "iprestrictions" + + // ConcurrencyLimitResourceType is the type used to represent the concurrency limit resource + ConcurrencyLimitResourceType ResourceType = "concurrencylimits" + + // RequestTerminationResourceType is the type used to represent the request termination resource + RequestTerminationResourceType ResourceType = "requestterminations" + + // ExternalRateLimitResourceType is the type used to represent the external rate limit resource + ExternalRateLimitResourceType ResourceType = "externalratelimits" ) diff --git a/pkg/manager/reconciler/registers.go b/pkg/manager/reconciler/registers.go index ba8288f20..ed871c37e 100644 --- a/pkg/manager/reconciler/registers.go +++ b/pkg/manager/reconciler/registers.go @@ -319,6 +319,15 @@ func getRegisters(regCfg *whtypes.RegisterConfig, mc configurator.Configurator) reconcilers[GatewayAPIExtensionProxyTag] = extensionv1alpha1.NewProxyTagReconciler(ctx) + webhooks[GatewayAPIIPRestriction] = extwhv1alpha1.NewIPRestrictionWebhook(regCfg) + reconcilers[GatewayAPIIPRestriction] = extensionv1alpha1.NewIPRestrictionReconciler(ctx, webhooks[GatewayAPIIPRestriction]) + + reconcilers[GatewayAPIExternalRateLimit] = extensionv1alpha1.NewExternalRateLimitReconciler(ctx) + + reconcilers[GatewayAPIRequestTermination] = extensionv1alpha1.NewRequestTerminationReconciler(ctx) + + reconcilers[GatewayAPIConcurrencyLimit] = extensionv1alpha1.NewConcurrencyLimitReconciler(ctx) + webhooks[GatewayAPIExtensionFaultInjection] = extwhv1alpha1.NewFaultInjectionWebhook(regCfg) reconcilers[GatewayAPIExtensionFaultInjection] = extensionv1alpha1.NewFaultInjectionReconciler(ctx, webhooks[GatewayAPIExtensionFaultInjection]) } diff --git a/pkg/manager/reconciler/types.go b/pkg/manager/reconciler/types.go index 82f021802..768367e8b 100644 --- a/pkg/manager/reconciler/types.go +++ b/pkg/manager/reconciler/types.go @@ -33,6 +33,10 @@ const ( GatewayAPIExtensionMetrics ResourceType = "GatewayAPIExtension(Metrics)" GatewayAPIExtensionZipkin ResourceType = "GatewayAPIExtension(Zipkin)" GatewayAPIExtensionProxyTag ResourceType = "GatewayAPIExtension(ProxyTag)" + GatewayAPIIPRestriction ResourceType = "GatewayAPI(IPRestriction)" + GatewayAPIExternalRateLimit ResourceType = "GatewayAPI(ExternalRateLimit)" + GatewayAPIRequestTermination ResourceType = "GatewayAPI(RequestTermination)" + GatewayAPIConcurrencyLimit ResourceType = "GatewayAPI(ConcurrencyLimit)" PolicyAttachmentHealthCheck ResourceType = "PolicyAttachment(HealthCheck)" PolicyAttachmentBackendLB ResourceType = "PolicyAttachment(BackendLB)" PolicyAttachmentBackendTLS ResourceType = "PolicyAttachment(BackendTLS)" diff --git a/pkg/messaging/broker.go b/pkg/messaging/broker.go index 45f7a4802..166e84832 100644 --- a/pkg/messaging/broker.go +++ b/pkg/messaging/broker.go @@ -1344,6 +1344,14 @@ func getGatewayUpdateEvent(msg events.PubSubMessage) *gatewayUpdateEvent { announcements.GatewayZipkinAdded, announcements.GatewayZipkinDeleted, announcements.GatewayZipkinUpdated, // ProxyTag event announcements.GatewayProxyTagAdded, announcements.GatewayProxyTagDeleted, announcements.GatewayProxyTagUpdated, + // IPRestriction event + announcements.GatewayIPRestrictionAdded, announcements.GatewayIPRestrictionDeleted, announcements.GatewayIPRestrictionUpdated, + // ConcurrencyLimit event + announcements.GatewayConcurrencyLimitAdded, announcements.GatewayConcurrencyLimitDeleted, announcements.GatewayConcurrencyLimitUpdated, + // ExternalRateLimit event + announcements.GatewayExternalRateLimitAdded, announcements.GatewayExternalRateLimitDeleted, announcements.GatewayExternalRateLimitUpdated, + // RequestTermination event + announcements.GatewayRequestTerminationAdded, announcements.GatewayRequestTerminationDeleted, announcements.GatewayRequestTerminationUpdated, // // MultiCluster events diff --git a/pkg/webhook/extension/v1alpha1/iprestriction.go b/pkg/webhook/extension/v1alpha1/iprestriction.go new file mode 100644 index 000000000..69ec5f08d --- /dev/null +++ b/pkg/webhook/extension/v1alpha1/iprestriction.go @@ -0,0 +1,113 @@ +package v1alpha1 + +import ( + "context" + "fmt" + "net" + "net/netip" + + "github.com/flomesh-io/fsm/pkg/utils" + + "k8s.io/apimachinery/pkg/util/validation/field" + + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "k8s.io/apimachinery/pkg/runtime" + + extv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + "github.com/flomesh-io/fsm/pkg/webhook" + "github.com/flomesh-io/fsm/pkg/webhook/builder" + whtypes "github.com/flomesh-io/fsm/pkg/webhook/types" +) + +type IPRestrictionWebhook struct { + webhook.DefaultWebhook +} + +func NewIPRestrictionWebhook(cfg *whtypes.RegisterConfig) whtypes.Register { + r := &IPRestrictionWebhook{ + DefaultWebhook: webhook.DefaultWebhook{ + RegisterConfig: cfg, + Client: cfg.Manager.GetClient(), + }, + } + + if blder, err := builder.WebhookConfigurationManagedBy(cfg.Manager). + For(&extv1alpha1.IPRestriction{}). + WithWebhookServiceName(cfg.WebhookSvcName). + WithWebhookServiceNamespace(cfg.WebhookSvcNs). + WithCABundle(cfg.CaBundle). + Complete(); err != nil { + return nil + } else { + r.CfgBuilder = blder + } + + return r +} + +func (r *IPRestrictionWebhook) Default(ctx context.Context, obj runtime.Object) error { + _, ok := obj.(*extv1alpha1.IPRestriction) + if !ok { + return fmt.Errorf("unexpected type: %T", obj) + } + + return nil +} + +func (r *IPRestrictionWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + return r.doValidation(ctx, obj) +} + +func (r *IPRestrictionWebhook) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (warnings admission.Warnings, err error) { + return r.doValidation(ctx, newObj) +} + +func (r *IPRestrictionWebhook) doValidation(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + ipRestriction, ok := obj.(*extv1alpha1.IPRestriction) + if !ok { + return nil, fmt.Errorf("unexpected type: %T", obj) + } + + errs := r.validateSpec(ctx, ipRestriction.Spec, field.NewPath("spec")) + + if len(errs) > 0 { + return warnings, utils.ErrorListToError(errs) + } + + return nil, nil +} + +func (r *IPRestrictionWebhook) validateSpec(ctx context.Context, spec extv1alpha1.IPRestrictionSpec, path *field.Path) field.ErrorList { + var errs field.ErrorList + + if len(spec.Allowed) == 0 && len(spec.Forbidden) == 0 { + errs = append(errs, field.Invalid(path, spec, "either allowed or forbidden must be set")) + } + + for i, ip := range spec.Allowed { + if _, err := netip.ParseAddr(ip); err == nil { + continue + } + + if _, _, err := net.ParseCIDR(ip); err == nil { + continue + } + + errs = append(errs, field.Invalid(path.Child("allowed").Index(i), ip, "invalid IP address or CIDR")) + } + + for i, ip := range spec.Forbidden { + if _, err := netip.ParseAddr(ip); err == nil { + continue + } + + if _, _, err := net.ParseCIDR(ip); err == nil { + continue + } + + errs = append(errs, field.Invalid(path.Child("forbidden").Index(i), ip, "invalid IP address or CIDR")) + } + + return errs +}