diff --git a/api/v1beta1/ratelimitpolicy_types.go b/api/v1beta1/ratelimitpolicy_types.go deleted file mode 100644 index fe12ee53d..000000000 --- a/api/v1beta1/ratelimitpolicy_types.go +++ /dev/null @@ -1,313 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -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 v1beta1 - -import ( - "fmt" - "reflect" - - "github.com/go-logr/logr" - "github.com/google/go-cmp/cmp" - "github.com/kuadrant/kuadrant-operator/pkg/common" - limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// MetadataSource https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-enum-config-route-v3-ratelimit-action-metadata-source - -// +kubebuilder:validation:Enum=DYNAMIC;ROUTE_ENTRY -type MetadataSource string - -type GenericKeySpec struct { - DescriptorValue string `json:"descriptor_value"` - // +optional - DescriptorKey *string `json:"descriptor_key,omitempty"` -} - -type MetadataPathSegmentKey struct { - Key string `json:"key"` -} - -type MetadataPathSegment struct { - Segment MetadataPathSegmentKey `json:"segment"` -} - -type MetadataKeySpec struct { - Key string `json:"key"` - Path []MetadataPathSegment `json:"path"` -} - -type MetadataSpec struct { - DescriptorKey string `json:"descriptor_key"` - MetadataKey MetadataKeySpec `json:"metadata_key"` - // +optional - DefaultValue *string `json:"default_value,omitempty"` - // +kubebuilder:default=DYNAMIC - Source MetadataSource `json:"source,omitempty"` -} - -// RemoteAddressSpec no need to specify -// descriptor entry is populated using the trusted address from -// [x-forwarded-for](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#config-http-conn-man-headers-x-forwarded-for) -type RemoteAddressSpec struct { -} - -// RequestHeadersSpec Rate limit on request headers. -type RequestHeadersSpec struct { - HeaderName string `json:"header_name"` - DescriptorKey string `json:"descriptor_key"` - // +optional - SkipIfAbsent *bool `json:"skip_if_absent,omitempty"` -} - -// Action_Specifier defines one envoy rate limit action -type ActionSpecifier struct { - // +optional - GenericKey *GenericKeySpec `json:"generic_key,omitempty"` - // +optional - Metadata *MetadataSpec `json:"metadata,omitempty"` - // +optional - RemoteAddress *RemoteAddressSpec `json:"remote_address,omitempty"` - // +optional - RequestHeaders *RequestHeadersSpec `json:"request_headers,omitempty"` -} - -// Rule defines a single condition for the rate limit configuration -// All defined fields within the rule must be met to have a rule match -type Rule struct { - // +optional - Paths []string `json:"paths,omitempty"` - // +optional - Methods []string `json:"methods,omitempty"` - // +optional - Hosts []string `json:"hosts,omitempty"` -} - -// Configuration represents an action configuration. -// The equivalent of [config.route.v3.RateLimit](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-msg-config-route-v3-ratelimit) -// envoy object. -// Each action configuration produces, at most, one descriptor. -// Depending on the incoming request, one configuration may or may not produce -// a rate limit descriptor. -type Configuration struct { - // Actions holds list of action specifiers. Each action specifier can only define one action type. - Actions []ActionSpecifier `json:"actions"` -} - -// Limit represents partially a Limitador limit. -type Limit struct { - MaxValue int `json:"maxValue"` - Seconds int `json:"seconds"` - - // +optional - Conditions []string `json:"conditions,omitempty"` - // +optional - Variables []string `json:"variables,omitempty"` -} - -func LimitFromLimitadorRateLimit(limit *limitadorv1alpha1.RateLimit) *Limit { - if limit == nil { - return nil - } - - rlpLimit := &Limit{ - MaxValue: limit.MaxValue, - Seconds: limit.Seconds, - Conditions: nil, - Variables: nil, - } - - if limit.Conditions != nil { - // deep copy - rlpLimit.Conditions = make([]string, len(limit.Conditions)) - copy(rlpLimit.Conditions, limit.Conditions) - } - - if limit.Variables != nil { - // deep copy - rlpLimit.Variables = make([]string, len(limit.Variables)) - copy(rlpLimit.Variables, limit.Variables) - } - - return rlpLimit -} - -// RateLimit represents a complete rate limit configuration -type RateLimit struct { - // Configurations holds list of (action) configuration. - // +optional - Configurations []Configuration `json:"configurations,omitempty"` - - // Rules represents the definition of the scope of the rate limit object - // Defines a list of conditions for which rate limit configuration will apply - // Matching occurs when at least one rule applies against the incoming request. - // If rules are not set, or empty, it is equivalent to matching all the requests. - // +optional - Rules []Rule `json:"rules,omitempty"` - - // Limits holds a list of Limitador limits - // +optional - Limits []Limit `json:"limits,omitempty"` -} - -type GatewayRateLimits struct { - GatewayName string `json:"gatewayName"` - - // RateLimits holds the list of rate limit configurations - // +optional - RateLimits []RateLimit `json:"rateLimits,omitempty"` -} - -// RateLimitPolicySpec defines the desired state of RateLimitPolicy -type RateLimitPolicySpec struct { - // TargetRef identifies an API object to apply policy to. - TargetRef gatewayapiv1alpha2.PolicyTargetReference `json:"targetRef"` - // RateLimits holds the list of rate limit configurations - // +optional - RateLimits []RateLimit `json:"rateLimits,omitempty"` -} - -// RateLimitPolicyStatus defines the observed state of RateLimitPolicy -type RateLimitPolicyStatus struct { - // ObservedGeneration reflects the generation of the most recently observed spec. - // +optional - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - - // Represents the observations of a foo's current state. - // Known .status.conditions.type are: "Available" - // +patchMergeKey=type - // +patchStrategy=merge - // +listType=map - // +listMapKey=type - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` - - // GatewaysRateLimits shows the rate limit configuration applied by policies at the gateway level - // this field is only meant for rate limit policies targeting a route - // +optional - GatewaysRateLimits []GatewayRateLimits `json:"gatewaysRateLimits,omitempty"` -} - -func (s *RateLimitPolicyStatus) Equals(other *RateLimitPolicyStatus, logger logr.Logger) bool { - if s.ObservedGeneration != other.ObservedGeneration { - diff := cmp.Diff(s.ObservedGeneration, other.ObservedGeneration) - logger.V(1).Info("ObservedGeneration not equal", "difference", diff) - return false - } - - // Marshalling sorts by condition type - currentMarshaledJSON, _ := common.ConditionMarshal(s.Conditions) - otherMarshaledJSON, _ := common.ConditionMarshal(other.Conditions) - if string(currentMarshaledJSON) != string(otherMarshaledJSON) { - diff := cmp.Diff(string(currentMarshaledJSON), string(otherMarshaledJSON)) - logger.V(1).Info("Conditions not equal", "difference", diff) - return false - } - - // TODO(eastizle): reflect.DeepEqual does not work well with lists without order - if !reflect.DeepEqual(s.GatewaysRateLimits, other.GatewaysRateLimits) { - logger.V(1).Info("GatewaysRateLimits not equal") - return false - } - return true -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// RateLimitPolicy is the Schema for the ratelimitpolicies API -type RateLimitPolicy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RateLimitPolicySpec `json:"spec,omitempty"` - Status RateLimitPolicyStatus `json:"status,omitempty"` -} - -func (r *RateLimitPolicy) Validate() error { - if r.Spec.TargetRef.Group != gatewayapiv1alpha2.Group("gateway.networking.k8s.io") { - return fmt.Errorf("invalid targetRef.Group %s. The only supported group is gateway.networking.k8s.io", r.Spec.TargetRef.Group) - } - - if r.Spec.TargetRef.Kind != gatewayapiv1alpha2.Kind("HTTPRoute") && r.Spec.TargetRef.Kind != gatewayapiv1alpha2.Kind("Gateway") { - return fmt.Errorf("invalid targetRef.Kind %s. The only supported kind types are HTTPRoute and Gateway", r.Spec.TargetRef.Kind) - } - - if r.Spec.TargetRef.Namespace != nil && string(*r.Spec.TargetRef.Namespace) != r.Namespace { - return fmt.Errorf("invalid targetRef.Namespace %s. Currently only supporting references to the same namespace", *r.Spec.TargetRef.Namespace) - } - - return nil -} - -func (r *RateLimitPolicy) TargetKey() client.ObjectKey { - tmpNS := r.Namespace - if r.Spec.TargetRef.Namespace != nil { - tmpNS = string(*r.Spec.TargetRef.Namespace) - } - - return client.ObjectKey{ - Name: string(r.Spec.TargetRef.Name), - Namespace: tmpNS, - } -} - -// FlattenLimits returns Limit list from RateLimit list -func (r *RateLimitPolicy) FlattenLimits() []Limit { - flattenLimits := make([]Limit, 0) - - for idx := range r.Spec.RateLimits { - flattenLimits = append(flattenLimits, r.Spec.RateLimits[idx].Limits...) - } - - return flattenLimits -} - -//+kubebuilder:object:root=true - -// RateLimitPolicyList contains a list of RateLimitPolicy -type RateLimitPolicyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RateLimitPolicy `json:"items"` -} - -func (r *RateLimitPolicy) GetTargetRef() gatewayapiv1alpha2.PolicyTargetReference { - return r.Spec.TargetRef -} - -func (r *RateLimitPolicy) GetWrappedNamespace() gatewayapiv1alpha2.Namespace { - return gatewayapiv1alpha2.Namespace(r.Namespace) -} - -func (r *RateLimitPolicy) GetRulesHostnames() (ruleHosts []string) { - ruleHosts = make([]string, 0) - for idx := range r.Spec.RateLimits { - for ruleIdx := range r.Spec.RateLimits[idx].Rules { - ruleHosts = append(ruleHosts, r.Spec.RateLimits[idx].Rules[ruleIdx].Hosts...) - } - } - return -} - -func init() { - SchemeBuilder.Register(&RateLimitPolicy{}, &RateLimitPolicyList{}) -} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 3fc493d56..c66c17f0f 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -27,41 +27,6 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ActionSpecifier) DeepCopyInto(out *ActionSpecifier) { - *out = *in - if in.GenericKey != nil { - in, out := &in.GenericKey, &out.GenericKey - *out = new(GenericKeySpec) - (*in).DeepCopyInto(*out) - } - if in.Metadata != nil { - in, out := &in.Metadata, &out.Metadata - *out = new(MetadataSpec) - (*in).DeepCopyInto(*out) - } - if in.RemoteAddress != nil { - in, out := &in.RemoteAddress, &out.RemoteAddress - *out = new(RemoteAddressSpec) - **out = **in - } - if in.RequestHeaders != nil { - in, out := &in.RequestHeaders, &out.RequestHeaders - *out = new(RequestHeadersSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActionSpecifier. -func (in *ActionSpecifier) DeepCopy() *ActionSpecifier { - if in == nil { - return nil - } - out := new(ActionSpecifier) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthPolicy) DeepCopyInto(out *AuthPolicy) { *out = *in @@ -281,70 +246,6 @@ func (in *AuthSchemeSpec) DeepCopy() *AuthSchemeSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Configuration) DeepCopyInto(out *Configuration) { - *out = *in - if in.Actions != nil { - in, out := &in.Actions, &out.Actions - *out = make([]ActionSpecifier, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration. -func (in *Configuration) DeepCopy() *Configuration { - if in == nil { - return nil - } - out := new(Configuration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayRateLimits) DeepCopyInto(out *GatewayRateLimits) { - *out = *in - if in.RateLimits != nil { - in, out := &in.RateLimits, &out.RateLimits - *out = make([]RateLimit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayRateLimits. -func (in *GatewayRateLimits) DeepCopy() *GatewayRateLimits { - if in == nil { - return nil - } - out := new(GatewayRateLimits) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GenericKeySpec) DeepCopyInto(out *GenericKeySpec) { - *out = *in - if in.DescriptorKey != nil { - in, out := &in.DescriptorKey, &out.DescriptorKey - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericKeySpec. -func (in *GenericKeySpec) DeepCopy() *GenericKeySpec { - if in == nil { - return nil - } - out := new(GenericKeySpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Kuadrant) DeepCopyInto(out *Kuadrant) { *out = *in @@ -440,312 +341,3 @@ func (in *KuadrantStatus) DeepCopy() *KuadrantStatus { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Limit) DeepCopyInto(out *Limit) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Variables != nil { - in, out := &in.Variables, &out.Variables - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Limit. -func (in *Limit) DeepCopy() *Limit { - if in == nil { - return nil - } - out := new(Limit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataKeySpec) DeepCopyInto(out *MetadataKeySpec) { - *out = *in - if in.Path != nil { - in, out := &in.Path, &out.Path - *out = make([]MetadataPathSegment, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataKeySpec. -func (in *MetadataKeySpec) DeepCopy() *MetadataKeySpec { - if in == nil { - return nil - } - out := new(MetadataKeySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataPathSegment) DeepCopyInto(out *MetadataPathSegment) { - *out = *in - out.Segment = in.Segment -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataPathSegment. -func (in *MetadataPathSegment) DeepCopy() *MetadataPathSegment { - if in == nil { - return nil - } - out := new(MetadataPathSegment) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataPathSegmentKey) DeepCopyInto(out *MetadataPathSegmentKey) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataPathSegmentKey. -func (in *MetadataPathSegmentKey) DeepCopy() *MetadataPathSegmentKey { - if in == nil { - return nil - } - out := new(MetadataPathSegmentKey) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataSpec) DeepCopyInto(out *MetadataSpec) { - *out = *in - in.MetadataKey.DeepCopyInto(&out.MetadataKey) - 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 MetadataSpec. -func (in *MetadataSpec) DeepCopy() *MetadataSpec { - if in == nil { - return nil - } - out := new(MetadataSpec) - 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.Configurations != nil { - in, out := &in.Configurations, &out.Configurations - *out = make([]Configuration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]Rule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Limits != nil { - in, out := &in.Limits, &out.Limits - *out = make([]Limit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// 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 *RateLimitPolicy) DeepCopyInto(out *RateLimitPolicy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicy. -func (in *RateLimitPolicy) DeepCopy() *RateLimitPolicy { - if in == nil { - return nil - } - out := new(RateLimitPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RateLimitPolicy) 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 *RateLimitPolicyList) DeepCopyInto(out *RateLimitPolicyList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RateLimitPolicy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicyList. -func (in *RateLimitPolicyList) DeepCopy() *RateLimitPolicyList { - if in == nil { - return nil - } - out := new(RateLimitPolicyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RateLimitPolicyList) 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 *RateLimitPolicySpec) DeepCopyInto(out *RateLimitPolicySpec) { - *out = *in - in.TargetRef.DeepCopyInto(&out.TargetRef) - if in.RateLimits != nil { - in, out := &in.RateLimits, &out.RateLimits - *out = make([]RateLimit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicySpec. -func (in *RateLimitPolicySpec) DeepCopy() *RateLimitPolicySpec { - if in == nil { - return nil - } - out := new(RateLimitPolicySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RateLimitPolicyStatus) DeepCopyInto(out *RateLimitPolicyStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.GatewaysRateLimits != nil { - in, out := &in.GatewaysRateLimits, &out.GatewaysRateLimits - *out = make([]GatewayRateLimits, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicyStatus. -func (in *RateLimitPolicyStatus) DeepCopy() *RateLimitPolicyStatus { - if in == nil { - return nil - } - out := new(RateLimitPolicyStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemoteAddressSpec) DeepCopyInto(out *RemoteAddressSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteAddressSpec. -func (in *RemoteAddressSpec) DeepCopy() *RemoteAddressSpec { - if in == nil { - return nil - } - out := new(RemoteAddressSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestHeadersSpec) DeepCopyInto(out *RequestHeadersSpec) { - *out = *in - if in.SkipIfAbsent != nil { - in, out := &in.SkipIfAbsent, &out.SkipIfAbsent - *out = new(bool) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeadersSpec. -func (in *RequestHeadersSpec) DeepCopy() *RequestHeadersSpec { - if in == nil { - return nil - } - out := new(RequestHeadersSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Rule) DeepCopyInto(out *Rule) { - *out = *in - if in.Paths != nil { - in, out := &in.Paths, &out.Paths - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule. -func (in *Rule) DeepCopy() *Rule { - if in == nil { - return nil - } - out := new(Rule) - in.DeepCopyInto(out) - return out -} diff --git a/api/v2beta1/groupversion_info.go b/api/v2beta1/groupversion_info.go new file mode 100644 index 000000000..e7d88eb3b --- /dev/null +++ b/api/v2beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2021. + +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 v1beta1 contains API Schema definitions for the kuadrant v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=kuadrant.io +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "kuadrant.io", Version: "v2beta1"} + + // 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/api/v2beta1/ratelimitpolicy_types.go b/api/v2beta1/ratelimitpolicy_types.go new file mode 100644 index 000000000..061547932 --- /dev/null +++ b/api/v2beta1/ratelimitpolicy_types.go @@ -0,0 +1,218 @@ +/* +Copyright 2021 Red Hat, Inc. + +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 v2beta1 + +import ( + "fmt" + "reflect" + + "github.com/go-logr/logr" + "github.com/google/go-cmp/cmp" + "github.com/kuadrant/kuadrant-operator/pkg/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ContextSelector defines one item from the well known selectors +// TODO Document properly "Well-known selector" https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=253 +type ContextSelector string + +// +kubebuilder:validation:Enum:=eq;neq;incl;excl;matches +type WhenConditionOperator string + +// +kubebuilder:validation:Enum:=second;minute;hour;day +type TimeUnit string + +// Rate defines the actual rate limit that will be used when there is a match +type Rate struct { + // Limit defines the max value allowed for a given period of time + Limit int `json:"limit"` + + // Duration defines the time period for which the Limit specified above applies. + Duration int `json:"duration"` + + // Duration defines the time uni + // Possible values are: "second", "minute", "hour", "day" + Unit TimeUnit `json:"unit"` +} + +// RouteSelector defines semantics for matching an HTTP request based on conditions +// https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec +type WhenCondition struct { + // Selector defines one item from the well known selectors + // TODO Document properly "Well-known selector" https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors + Selector ContextSelector `json:"selector"` + + // The binary operator to be applied to the content fetched from the selector + // Possible values are: "eq" (equal to), "neq" (not equal to), "incl" (includes; for arrays), "excl" (excludes; for arrays), "matches" (regex) + Operator WhenConditionOperator `json:"operator"` + + // The value of reference for the comparison. + // If used with the "matches" operator, the value must compile to a valid Golang regex. + Value string `json:"value"` +} + +// RouteSelector defines semantics for matching an HTTP request based on conditions +// https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec +type RouteSelector struct { + // Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request + // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec + // +optional + Hostnames []gatewayapiv1alpha2.Hostname `json:"hostnames,omitempty"` + + // Matches define conditions used for matching the rule against incoming HTTP requests. + // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec + // +optional + Matches []gatewayapiv1alpha2.HTTPRouteMatch `json:"matches,omitempty"` +} + +// Limit represents a complete rate limit configuration +type Limit struct { + // RouteSelectors defines semantics for matching an HTTP request based on conditions + // +optional + RouteSelectors []RouteSelector `json:"routeSelectors,omitempty"` + + // When holds the list of conditions for the policy to be enforced. + // Called also "soft" conditions as route selectors must also match + // +optional + When []WhenCondition `json:"when,omitempty"` + + // Counters defines additional rate limit counters based on context qualifiers and well known selectors + // TODO Document properly "Well-known selector" https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors + // +optional + Counters []ContextSelector `json:"counters,omitempty"` + + // Rates holds the list of limit rates + // +optional + Rates []Rate `json:"rates,omitempty"` +} + +// RateLimitPolicySpec defines the desired state of RateLimitPolicy +type RateLimitPolicySpec struct { + // TargetRef identifies an API object to apply policy to. + TargetRef gatewayapiv1alpha2.PolicyTargetReference `json:"targetRef"` + + // Limits holds the struct of limits indexed by a unique name + // +optional + Limits map[string]Limit `json:"limits,omitempty"` +} + +// RateLimitPolicyStatus defines the observed state of RateLimitPolicy +type RateLimitPolicyStatus struct { + // ObservedGeneration reflects the generation of the most recently observed spec. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: "Available" + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +} + +func (s *RateLimitPolicyStatus) Equals(other *RateLimitPolicyStatus, logger logr.Logger) bool { + if s.ObservedGeneration != other.ObservedGeneration { + diff := cmp.Diff(s.ObservedGeneration, other.ObservedGeneration) + logger.V(1).Info("ObservedGeneration not equal", "difference", diff) + return false + } + + // Marshalling sorts by condition type + currentMarshaledJSON, _ := common.ConditionMarshal(s.Conditions) + otherMarshaledJSON, _ := common.ConditionMarshal(other.Conditions) + if string(currentMarshaledJSON) != string(otherMarshaledJSON) { + diff := cmp.Diff(string(currentMarshaledJSON), string(otherMarshaledJSON)) + logger.V(1).Info("Conditions not equal", "difference", diff) + return false + } + + // TODO(eastizle): reflect.DeepEqual does not work well with lists without order + if !reflect.DeepEqual(s.GatewaysRateLimits, other.GatewaysRateLimits) { + logger.V(1).Info("GatewaysRateLimits not equal") + return false + } + return true +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// RateLimitPolicy is the Schema for the ratelimitpolicies API +type RateLimitPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RateLimitPolicySpec `json:"spec,omitempty"` + Status RateLimitPolicyStatus `json:"status,omitempty"` +} + +func (r *RateLimitPolicy) Validate() error { + if r.Spec.TargetRef.Group != gatewayapiv1alpha2.Group("gateway.networking.k8s.io") { + return fmt.Errorf("invalid targetRef.Group %s. The only supported group is gateway.networking.k8s.io", r.Spec.TargetRef.Group) + } + + if r.Spec.TargetRef.Kind != gatewayapiv1alpha2.Kind("HTTPRoute") && r.Spec.TargetRef.Kind != gatewayapiv1alpha2.Kind("Gateway") { + return fmt.Errorf("invalid targetRef.Kind %s. The only supported kind types are HTTPRoute and Gateway", r.Spec.TargetRef.Kind) + } + + if r.Spec.TargetRef.Namespace != nil && string(*r.Spec.TargetRef.Namespace) != r.Namespace { + return fmt.Errorf("invalid targetRef.Namespace %s. Currently only supporting references to the same namespace", *r.Spec.TargetRef.Namespace) + } + + return nil +} + +func (r *RateLimitPolicy) TargetKey() client.ObjectKey { + tmpNS := r.Namespace + if r.Spec.TargetRef.Namespace != nil { + tmpNS = string(*r.Spec.TargetRef.Namespace) + } + + return client.ObjectKey{ + Name: string(r.Spec.TargetRef.Name), + Namespace: tmpNS, + } +} + +//+kubebuilder:object:root=true + +// RateLimitPolicyList contains a list of RateLimitPolicy +type RateLimitPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RateLimitPolicy `json:"items"` +} + +func (r *RateLimitPolicy) GetTargetRef() gatewayapiv1alpha2.PolicyTargetReference { + return r.Spec.TargetRef +} + +func (r *RateLimitPolicy) GetWrappedNamespace() gatewayapiv1alpha2.Namespace { + return gatewayapiv1alpha2.Namespace(r.Namespace) +} + +func init() { + SchemeBuilder.Register(&RateLimitPolicy{}, &RateLimitPolicyList{}) +} diff --git a/api/v1beta1/ratelimitpolicy_types_test.go b/api/v2beta1/ratelimitpolicy_types_test.go similarity index 99% rename from api/v1beta1/ratelimitpolicy_types_test.go rename to api/v2beta1/ratelimitpolicy_types_test.go index d21bf1d1f..c50b0d7d6 100644 --- a/api/v1beta1/ratelimitpolicy_types_test.go +++ b/api/v2beta1/ratelimitpolicy_types_test.go @@ -1,6 +1,6 @@ //go:build unit -package v1beta1 +package v2beta1 import ( "reflect" diff --git a/api/v2beta1/zz_generated.deepcopy.go b/api/v2beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..da53b133c --- /dev/null +++ b/api/v2beta1/zz_generated.deepcopy.go @@ -0,0 +1,226 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021. + +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 v2beta1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Limit) DeepCopyInto(out *Limit) { + *out = *in + if in.RouteSelectors != nil { + in, out := &in.RouteSelectors, &out.RouteSelectors + *out = make([]RouteSelector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.When != nil { + in, out := &in.When, &out.When + *out = make([]WhenCondition, len(*in)) + copy(*out, *in) + } + if in.Counters != nil { + in, out := &in.Counters, &out.Counters + *out = make([]ContextSelector, len(*in)) + copy(*out, *in) + } + if in.Rates != nil { + in, out := &in.Rates, &out.Rates + *out = make([]Rate, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Limit. +func (in *Limit) DeepCopy() *Limit { + if in == nil { + return nil + } + out := new(Limit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rate) DeepCopyInto(out *Rate) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rate. +func (in *Rate) DeepCopy() *Rate { + if in == nil { + return nil + } + out := new(Rate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitPolicy) DeepCopyInto(out *RateLimitPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicy. +func (in *RateLimitPolicy) DeepCopy() *RateLimitPolicy { + if in == nil { + return nil + } + out := new(RateLimitPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RateLimitPolicy) 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 *RateLimitPolicyList) DeepCopyInto(out *RateLimitPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RateLimitPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicyList. +func (in *RateLimitPolicyList) DeepCopy() *RateLimitPolicyList { + if in == nil { + return nil + } + out := new(RateLimitPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RateLimitPolicyList) 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 *RateLimitPolicySpec) DeepCopyInto(out *RateLimitPolicySpec) { + *out = *in + in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + *out = make(map[string]Limit, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicySpec. +func (in *RateLimitPolicySpec) DeepCopy() *RateLimitPolicySpec { + if in == nil { + return nil + } + out := new(RateLimitPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateLimitPolicyStatus) DeepCopyInto(out *RateLimitPolicyStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitPolicyStatus. +func (in *RateLimitPolicyStatus) DeepCopy() *RateLimitPolicyStatus { + if in == nil { + return nil + } + out := new(RateLimitPolicyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteSelector) DeepCopyInto(out *RouteSelector) { + *out = *in + if in.Hostnames != nil { + in, out := &in.Hostnames, &out.Hostnames + *out = make([]v1alpha2.Hostname, len(*in)) + copy(*out, *in) + } + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]v1alpha2.HTTPRouteMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteSelector. +func (in *RouteSelector) DeepCopy() *RouteSelector { + if in == nil { + return nil + } + out := new(RouteSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WhenCondition) DeepCopyInto(out *WhenCondition) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhenCondition. +func (in *WhenCondition) DeepCopy() *WhenCondition { + if in == nil { + return nil + } + out := new(WhenCondition) + in.DeepCopyInto(out) + return out +} diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 61866a55b..c893739f8 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -25,33 +25,33 @@ spec: securityContext: runAsNonRoot: true containers: - - command: - - /manager - env: - - name: RELATED_IMAGE_WASMSHIM - value: "oci://quay.io/kuadrant/wasm-shim:latest" - image: controller:latest - name: manager - securityContext: - allowPrivilegeEscalation: false - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 200m - memory: 300Mi - requests: - cpu: 200m - memory: 200Mi + - command: + - /manager + env: + - name: RELATED_IMAGE_WASMSHIM + value: "oci://quay.io/kuadrant/wasm-shim:latest" + image: controller:latest + name: manager + securityContext: + allowPrivilegeEscalation: false + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 200m + memory: 300Mi + requests: + cpu: 200m + memory: 200Mi serviceAccountName: controller-manager terminationGracePeriodSeconds: 10