diff --git a/charts/fsm/components/scripts.tar.gz b/charts/fsm/components/scripts.tar.gz index 6c9ac7167..a7e76a217 100644 Binary files a/charts/fsm/components/scripts.tar.gz and b/charts/fsm/components/scripts.tar.gz differ diff --git a/charts/fsm/components/scripts/gateways/filters/http/AccessLog.js b/charts/fsm/components/scripts/gateways/filters/http/AccessLog.js new file mode 100644 index 000000000..781ea0525 --- /dev/null +++ b/charts/fsm/components/scripts/gateways/filters/http/AccessLog.js @@ -0,0 +1,42 @@ +export default function (config) { + var log = console.log + var $ctx + + return pipeline($=>$ + .onStart(c => { $ctx = c }) + .pipeNext() + .handleMessageEnd(() => { + var inbound = $ctx.parent.inbound + var headers = $ctx.head.headers + var response = $ctx.response + var target = $ctx.target + log({ + protocol: $ctx.head.protocol || '', + upstream_service_time: response.headTime, + upstream_local_address: target || '', + duration: response.tailTime - $ctx.headTime, + upstream_transport_failure_reason: '', + route_name: '', + downstream_local_address: inbound.localAddress, + user_agent: headers['user-agent'] || '', + response_code: response.head?.status, + response_flags: '', + start_time: $ctx.headTime, + method: $ctx.head.method || '', + request_id: $ctx.id, + upstream_host: target, + x_forward_for: headers['x-forwarded-for'] || '', + client_ip: inbound.remoteAddress, + requested_server_name: '', + bytes_received: $ctx.tail.headSize + $ctx.tail.bodySize, + bytes_sent: response.tail ? response.tail.headSize + response.tail.bodySize : 0, + upstream_cluster: $ctx.backendResource?.metadata?.name, + downstream_remote_address: inbound.remoteAddress, + authority: $ctx.head.authority || '', + path: $ctx.path, + response_code_details: response.head?.statusText, + trace_id: headers['x-b3-traceid'] || '', + }) + }) + ) +} diff --git a/charts/fsm/templates/fsm-rbac.yaml b/charts/fsm/templates/fsm-rbac.yaml index f39fc9eb9..c8b71bac0 100644 --- a/charts/fsm/templates/fsm-rbac.yaml +++ b/charts/fsm/templates/fsm-rbac.yaml @@ -156,13 +156,13 @@ rules: # GatewayAPI Extension - apiGroups: [ "extension.gateway.flomesh.io" ] - resources: [ "filters", "filterdefinitions", "listenerfilters", "circuitbreakers", "faultinjections", "ratelimits" ] + resources: [ "filters", "filterdefinitions", "listenerfilters", "circuitbreakers", "faultinjections", "ratelimits", "httplogs" ] verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ] - apiGroups: [ "extension.gateway.flomesh.io" ] - resources: [ "filters/finalizers", "filterdefinitions/finalizers", "listenerfilters/finalizers", "circuitbreakers/finalizers", "faultinjections/finalizers", "ratelimits/finalizers" ] + resources: [ "filters/finalizers", "filterdefinitions/finalizers", "listenerfilters/finalizers", "circuitbreakers/finalizers", "faultinjections/finalizers", "ratelimits/finalizers", "httplogs/finalizers" ] verbs: [ "update" ] - apiGroups: [ "extension.gateway.flomesh.io" ] - resources: [ "filters/status", "filterdefinitions/status", "listenerfilters/status", "circuitbreakers/status", "faultinjections/status", "ratelimits/status" ] + resources: [ "filters/status", "filterdefinitions/status", "listenerfilters/status", "circuitbreakers/status", "faultinjections/status", "ratelimits/status", "httplogs/status" ] verbs: [ "get", "patch", "update" ] # PolicyAttachment diff --git a/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml new file mode 100644 index 000000000..e2ef44b79 --- /dev/null +++ b/cmd/fsm-bootstrap/crds/extension.gateway.flomesh.io_httplogs.yaml @@ -0,0 +1,200 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/name: flomesh.io + name: httplogs.extension.gateway.flomesh.io +spec: + group: extension.gateway.flomesh.io + names: + categories: + - gateway-api + kind: HTTPLog + listKind: HTTPLogList + plural: httplogs + singular: httplog + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: HTTPLog is the Schema for the HTTPLog API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HTTPLogSpec defines the desired state of HTTPLog + properties: + batch: + default: + interval: 1s + postfix: "" + prefix: "" + separator: |2+ + + size: 1000 + description: Batch is the batch configuration of the logs + properties: + interval: + default: 1s + description: Interval is the interval to send a batch, default + is 1s + type: string + postfix: + default: "" + description: Postfix is the postfix of the batch, default is "" + type: string + prefix: + default: "" + description: Prefix is the prefix of the batch, default is "" + type: string + separator: + default: |2+ + + description: Separator is the separator of the logs in the batch, + default is "\n" + type: string + size: + default: 1000 + description: Size is the maximum number of logs in a batch, default + is 1000 + format: int32 + minimum: 1 + type: integer + type: object + bufferLimit: + default: 1048576 + description: BufferLimit is the maximum size of the buffer in bytes, + default is 1048576(1MB) + format: int64 + minimum: 1 + type: integer + headers: + additionalProperties: + type: string + description: Headers is the HTTP headers of the log request + type: object + method: + default: POST + description: Method is the HTTP method of the HTTPLog service, default + is POST + enum: + - GET + - POST + - PUT + - DELETE + - PATCH + - HEAD + - OPTIONS + type: string + target: + description: Target is the URL of the HTTPLog service + type: string + required: + - target + type: object + status: + description: HTTPLogStatus defines the observed state of HTTPLog + properties: + conditions: + description: Conditions describe the current conditions of the HTTPLog. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/announcements/types.go b/pkg/announcements/types.go index f5459bd1d..e485e027d 100644 --- a/pkg/announcements/types.go +++ b/pkg/announcements/types.go @@ -644,6 +644,17 @@ const ( // ListenerFilterUpdated is the type of announcement emitted when we observe an update to listenerfilters.extension.gateway.flomesh.io ListenerFilterUpdated Kind = "listenerfilter-updated" + + // -- + + // HTTPLogAdded is the type of announcement emitted when we observe an addition of httplogs.extension.gateway.flomesh.io + HTTPLogAdded Kind = "httplog-added" + + // HTTPLogDeleted the type of announcement emitted when we observe a deletion of httplogs.extension.gateway.flomesh.io + HTTPLogDeleted Kind = "httplog-deleted" + + // HTTPLogUpdated is the type of announcement emitted when we observe an update to httplogs.extension.gateway.flomesh.io + HTTPLogUpdated Kind = "httplog-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/httplog.go b/pkg/apis/extension/v1alpha1/httplog.go new file mode 100644 index 000000000..16876d705 --- /dev/null +++ b/pkg/apis/extension/v1alpha1/httplog.go @@ -0,0 +1,100 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// HTTPLogSpec defines the desired state of HTTPLog +type HTTPLogSpec struct { + // +kubebuilder:validation:Required + // Target is the URL of the HTTPLog service + Target string `json:"target"` + + // +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"` + + // +optional + // Headers is the HTTP headers of the log request + Headers map[string]string `json:"headers,omitempty"` + + // +optional + // +kubebuilder:default=1048576 + // +kubebuilder:validation:Minimum=1 + // BufferLimit is the maximum size of the buffer in bytes, default is 1048576(1MB) + BufferLimit *int64 `json:"bufferLimit,omitempty"` + + // +optional + // +kubebuilder:default={size: 1000, interval: "1s", prefix: "", postfix: "", separator: "\n"} + // Batch is the batch configuration of the logs + Batch *HTTPLogBatch `json:"batch,omitempty"` +} + +type HTTPLogBatch struct { + // +optional + // +kubebuilder:default=1000 + // +kubebuilder:validation:Minimum=1 + // Size is the maximum number of logs in a batch, default is 1000 + Size *int32 `json:"size,omitempty"` + + // +optional + // +kubebuilder:default="1s" + // Interval is the interval to send a batch, default is 1s + Interval *metav1.Duration `json:"interval,omitempty"` + + // +optional + // +kubebuilder:default="" + // Prefix is the prefix of the batch, default is "" + Prefix *string `json:"prefix,omitempty"` + + // +optional + // +kubebuilder:default="" + // Postfix is the postfix of the batch, default is "" + Postfix *string `json:"postfix,omitempty"` + + // +optional + // +kubebuilder:default="\n" + // Separator is the separator of the logs in the batch, default is "\n" + Separator *string `json:"separator,omitempty"` +} + +// HTTPLogStatus defines the observed state of HTTPLog +type HTTPLogStatus struct { + // Conditions describe the current conditions of the HTTPLog. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:storageversion +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced,categories=gateway-api +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:metadata:labels={app.kubernetes.io/name=flomesh.io} + +// HTTPLog is the Schema for the HTTPLog API +type HTTPLog struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPLogSpec `json:"spec,omitempty"` + Status HTTPLogStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// HTTPLogList contains a list of HTTPLog +type HTTPLogList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []HTTPLog `json:"items"` +} diff --git a/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go index 8330e9815..67cc4a2e0 100644 --- a/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/extension/v1alpha1/zz_generated.deepcopy.go @@ -602,6 +602,169 @@ func (in *FilterStatus) DeepCopy() *FilterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPLog) DeepCopyInto(out *HTTPLog) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLog. +func (in *HTTPLog) DeepCopy() *HTTPLog { + if in == nil { + return nil + } + out := new(HTTPLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPLog) 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 *HTTPLogBatch) DeepCopyInto(out *HTTPLogBatch) { + *out = *in + if in.Size != nil { + in, out := &in.Size, &out.Size + *out = new(int32) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1.Duration) + **out = **in + } + if in.Prefix != nil { + in, out := &in.Prefix, &out.Prefix + *out = new(string) + **out = **in + } + if in.Postfix != nil { + in, out := &in.Postfix, &out.Postfix + *out = new(string) + **out = **in + } + if in.Separator != nil { + in, out := &in.Separator, &out.Separator + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLogBatch. +func (in *HTTPLogBatch) DeepCopy() *HTTPLogBatch { + if in == nil { + return nil + } + out := new(HTTPLogBatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPLogList) DeepCopyInto(out *HTTPLogList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HTTPLog, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLogList. +func (in *HTTPLogList) DeepCopy() *HTTPLogList { + if in == nil { + return nil + } + out := new(HTTPLogList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPLogList) 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 *HTTPLogSpec) DeepCopyInto(out *HTTPLogSpec) { + *out = *in + if in.Method != nil { + in, out := &in.Method, &out.Method + *out = new(string) + **out = **in + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.BufferLimit != nil { + in, out := &in.BufferLimit, &out.BufferLimit + *out = new(int64) + **out = **in + } + if in.Batch != nil { + in, out := &in.Batch, &out.Batch + *out = new(HTTPLogBatch) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLogSpec. +func (in *HTTPLogSpec) DeepCopy() *HTTPLogSpec { + if in == nil { + return nil + } + out := new(HTTPLogSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPLogStatus) DeepCopyInto(out *HTTPLogStatus) { + *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]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLogStatus. +func (in *HTTPLogStatus) DeepCopy() *HTTPLogStatus { + if in == nil { + return nil + } + out := new(HTTPLogStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ListenerFilter) DeepCopyInto(out *ListenerFilter) { *out = *in diff --git a/pkg/apis/extension/v1alpha1/zz_generated.register.go b/pkg/apis/extension/v1alpha1/zz_generated.register.go index 6b5dd9285..3bf51f888 100644 --- a/pkg/apis/extension/v1alpha1/zz_generated.register.go +++ b/pkg/apis/extension/v1alpha1/zz_generated.register.go @@ -66,6 +66,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &FilterDefinition{}, &FilterDefinitionList{}, &FilterList{}, + &HTTPLog{}, + &HTTPLogList{}, &ListenerFilter{}, &ListenerFilterList{}, &RateLimit{}, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 85e08fd60..32e7924d0 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -9,12 +9,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - gwv1 "sigs.k8s.io/gateway-api/apis/v1" - gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "helm.sh/helm/v3/pkg/chartutil" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) const ( @@ -477,6 +473,9 @@ const ( // RetryPolicyKind is the kind name of RetryPolicy used in Flomesh API RetryPolicyKind = "RetryPolicy" + + // HTTPLogKind is the kind name of HTTPLog used in Flomesh API + HTTPLogKind = "HTTPLog" ) // Gateway API Annotations and Labels @@ -679,24 +678,6 @@ const ( // FLBSecretKeyDefaultAlgo is the key for the default algo FLBSecretKeyDefaultAlgo = "defaultAlgo" - - // FLBServiceMutatingWebhookPath is the path at which the flb service mutating webhook is served - FLBServiceMutatingWebhookPath = "/mutate-flb-core-v1-service" - - // FLBServiceValidatingWebhookPath is the path at which the flb service validating webhook is served - FLBServiceValidatingWebhookPath = "/validate-flb-core-v1-service" - - // FLBSecretMutatingWebhookPath is the path at which the flb secret mutating webhook is served - FLBSecretMutatingWebhookPath = "/mutate-flb-core-v1-secret" - - // FLBSecretValidatingWebhookPath is the path at which the flb secret validating webhook is served - FLBSecretValidatingWebhookPath = "/validate-flb-core-v1-secret" - - // FLBTLSSecretMutatingWebhookPath is the path at which the flb tls secret mutating webhook is served - FLBTLSSecretMutatingWebhookPath = "/mutate-flb-core-v1-secret-tls" - - // FLBTLSSecretValidatingWebhookPath is the path at which the flb tls secret validating webhook is served - FLBTLSSecretValidatingWebhookPath = "/validate-flb-core-v1-secret-tls" ) // MultiCluster variables @@ -741,21 +722,6 @@ var ( WebhookServerServingCertsPath = fmt.Sprintf(WebhookServerServingCertsPathTpl, os.TempDir()) ) -// NamespacedIngress constants -const ( - // NamespacedIngressMutatingWebhookPath is the path at which the namespaced ingress mutating webhook is served - NamespacedIngressMutatingWebhookPath = "/mutate-flomesh-io-v1alpha1-namespacedingress" - - // NamespacedIngressValidatingWebhookPath is the path at which the namespaced ingress validating webhook is served - NamespacedIngressValidatingWebhookPath = "/validate-flomesh-io-v1alpha1-namespacedingress" - - // IngressMutatingWebhookPath is the path at which the ingress mutating webhook is served - IngressMutatingWebhookPath = "/mutate-networking-v1-ingress" - - // IngressValidatingWebhookPath is the path at which the ingress validating webhook is served - IngressValidatingWebhookPath = "/validate-networking-v1-ingress" -) - // Ingress constants const ( // IngressPipyController is the name of the ingress controller @@ -842,30 +808,6 @@ var ( } ) -// GroupVersionKind variables -var ( - GatewayClassGVK = schema.FromAPIVersionAndKind(gwv1.GroupVersion.String(), GatewayClassAPIGatewayKind) - GatewayGVK = schema.FromAPIVersionAndKind(gwv1.GroupVersion.String(), GatewayAPIGatewayKind) - HTTPRouteGVK = schema.FromAPIVersionAndKind(gwv1.GroupVersion.String(), GatewayAPIHTTPRouteKind) - TLSRouteGVK = schema.FromAPIVersionAndKind(gwv1alpha2.GroupVersion.String(), GatewayAPITLSRouteKind) - TCPRouteGVK = schema.FromAPIVersionAndKind(gwv1alpha2.GroupVersion.String(), GatewayAPITCPRouteKind) - UDPRouteGVK = schema.FromAPIVersionAndKind(gwv1alpha2.GroupVersion.String(), GatewayAPIUDPRouteKind) - GRPCRouteGVK = schema.FromAPIVersionAndKind(gwv1alpha2.GroupVersion.String(), GatewayAPIGRPCRouteKind) - ReferenceGrantGVK = schema.FromAPIVersionAndKind(gwv1alpha2.GroupVersion.String(), GatewayAPIReferenceGrantKind) - SecretGVK = schema.FromAPIVersionAndKind(corev1.SchemeGroupVersion.String(), KubernetesSecretKind) - ConfigMapGVK = schema.FromAPIVersionAndKind(corev1.SchemeGroupVersion.String(), KubernetesConfigMapKind) - ServiceGVK = schema.FromAPIVersionAndKind(corev1.SchemeGroupVersion.String(), KubernetesServiceKind) - //RateLimitPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), RateLimitPolicyKind) - //SessionStickyPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), SessionStickyPolicyKind) - //LoadBalancerPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), LoadBalancerPolicyKind) - //CircuitBreakingPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), CircuitBreakingPolicyKind) - //AccessControlPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), AccessControlPolicyKind) - //HealthCheckPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), HealthCheckPolicyKind) - //FaultInjectionPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), FaultInjectionKind) - //UpstreamTLSPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), UpstreamTLSPolicyKind) - //RetryPolicyGVK = schema.FromAPIVersionAndKind(gwpav1alpha1.GroupVersion.String(), RetryPolicyKind) -) - // GatewayAPI resources variables var ( diff --git a/pkg/controllers/extension/v1alpha1/filter_controller.go b/pkg/controllers/extension/v1alpha1/filter_controller.go index fbfd7fee4..c1ab8fa1e 100644 --- a/pkg/controllers/extension/v1alpha1/filter_controller.go +++ b/pkg/controllers/extension/v1alpha1/filter_controller.go @@ -102,7 +102,7 @@ func configFilterIndex(obj client.Object) []string { var configs []string - if filter.Spec.ConfigRef.Group == extv1alpha1.GroupName { + if filter.Spec.ConfigRef != nil && filter.Spec.ConfigRef.Group == extv1alpha1.GroupName { configs = append(configs, fmt.Sprintf("%s/%s/%s", filter.Spec.ConfigRef.Kind, filter.Namespace, filter.Spec.ConfigRef.Name)) } diff --git a/pkg/controllers/extension/v1alpha1/httplog_controller.go b/pkg/controllers/extension/v1alpha1/httplog_controller.go new file mode 100644 index 000000000..21433b558 --- /dev/null +++ b/pkg/controllers/extension/v1alpha1/httplog_controller.go @@ -0,0 +1,73 @@ +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 httpLogReconciler struct { + recorder record.EventRecorder + fctx *fctx.ControllerContext +} + +func (r *httpLogReconciler) NeedLeaderElection() bool { + return true +} + +// NewHTTPLogReconciler returns a new HTTPLog Reconciler +func NewHTTPLogReconciler(ctx *fctx.ControllerContext) controllers.Reconciler { + return &httpLogReconciler{ + recorder: ctx.Manager.GetEventRecorderFor("HTTPLog"), + fctx: ctx, + } +} + +// Reconcile reads that state of the cluster for a HTTPLog object and makes changes based on the state read +func (r *httpLogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + httpLog := &extv1alpha1.HTTPLog{} + err := r.fctx.Get(ctx, req.NamespacedName, httpLog) + if errors.IsNotFound(err) { + r.fctx.GatewayEventHandler.OnDelete(&extv1alpha1.HTTPLog{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: req.Name, + }}) + return reconcile.Result{}, nil + } + + if httpLog.DeletionTimestamp != nil { + r.fctx.GatewayEventHandler.OnDelete(httpLog) + return ctrl.Result{}, nil + } + + // As HTTPLog has no status, we don't need to update it + + r.fctx.GatewayEventHandler.OnAdd(httpLog, false) + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *httpLogReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := ctrl.NewControllerManagedBy(mgr). + For(&extv1alpha1.HTTPLog{}). + Complete(r); err != nil { + return err + } + + return addHTTPLogIndexers(context.Background(), mgr) +} + +func addHTTPLogIndexers(ctx context.Context, mgr manager.Manager) error { + return nil +} diff --git a/pkg/controllers/extension/v1alpha1/listenerfilter_controller.go b/pkg/controllers/extension/v1alpha1/listenerfilter_controller.go index c4eb3d7ec..c5b48b67d 100644 --- a/pkg/controllers/extension/v1alpha1/listenerfilter_controller.go +++ b/pkg/controllers/extension/v1alpha1/listenerfilter_controller.go @@ -95,7 +95,7 @@ func configListenerFilterIndex(obj client.Object) []string { var configs []string - if filter.Spec.ConfigRef.Group == extv1alpha1.GroupName { + if filter.Spec.ConfigRef != nil && filter.Spec.ConfigRef.Group == extv1alpha1.GroupName { configs = append(configs, fmt.Sprintf("%s/%s/%s", filter.Spec.ConfigRef.Kind, filter.Namespace, filter.Spec.ConfigRef.Name)) } @@ -107,7 +107,7 @@ func filterDefinitionListenerFilterIndex(obj client.Object) []string { var definitions []string - if filter.Spec.DefinitionRef.Group == extv1alpha1.GroupName && + if filter.Spec.DefinitionRef != nil && filter.Spec.DefinitionRef.Group == extv1alpha1.GroupName && filter.Spec.DefinitionRef.Kind == constants.GatewayAPIExtensionFilterDefinitionKind { definitions = append(definitions, fmt.Sprintf("%s/%s", filter.Namespace, filter.Spec.DefinitionRef.Name)) } diff --git a/pkg/gateway/client.go b/pkg/gateway/client.go index 7197065f7..0b014a9f3 100644 --- a/pkg/gateway/client.go +++ b/pkg/gateway/client.go @@ -101,6 +101,7 @@ func newClient(ctx *cctx.ControllerContext) *client { fsminformers.InformerKeyCircuitBreaker: &extv1alpha1.CircuitBreaker{}, fsminformers.InformerKeyFaultInjection: &extv1alpha1.FaultInjection{}, fsminformers.InformerKeyRateLimit: &extv1alpha1.RateLimit{}, + fsminformers.InformerKeyHTTPLog: &extv1alpha1.HTTPLog{}, } if version.IsEndpointSliceEnabled(ctx.KubeClient) { diff --git a/pkg/gateway/fgw/config.go b/pkg/gateway/fgw/config.go index 2feecd7b9..292661c4f 100644 --- a/pkg/gateway/fgw/config.go +++ b/pkg/gateway/fgw/config.go @@ -571,3 +571,25 @@ func (r *RateLimitSpec) Interval(interval *metav1.Duration) { r.IntervalInMilliseconds = ptr.To(interval.Milliseconds()) } } + +type HTTPLogSpec struct { + Target string `json:"target"` + Method *string `json:"method,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + BufferLimit *int64 `json:"bufferLimit,omitempty"` + Batch *HTTPLogBatch `json:"batch,omitempty"` +} + +type HTTPLogBatch struct { + Size *int32 `json:"size,omitempty"` + IntervalInMilliseconds *int64 `json:"interval,omitempty"` + Prefix *string `json:"prefix,omitempty"` + Postfix *string `json:"postfix,omitempty"` + Separator *string `json:"separator,omitempty"` +} + +func (b *HTTPLogBatch) Interval(interval *metav1.Duration) { + if interval != nil { + b.IntervalInMilliseconds = ptr.To(interval.Milliseconds()) + } +} diff --git a/pkg/gateway/informers.go b/pkg/gateway/informers.go index 2d691661a..db84e23e2 100644 --- a/pkg/gateway/informers.go +++ b/pkg/gateway/informers.go @@ -67,6 +67,8 @@ func getEventTypesByObjectType(obj interface{}) *k8s.EventTypes { return getEventTypesByInformerKey(fsminformers.InformerKeyFaultInjection) case *extv1alpha1.RateLimit: return getEventTypesByInformerKey(fsminformers.InformerKeyRateLimit) + case *extv1alpha1.HTTPLog: + return getEventTypesByInformerKey(fsminformers.InformerKeyHTTPLog) } return nil @@ -219,6 +221,12 @@ func getEventTypesByInformerKey(informerKey fsminformers.InformerKey) *k8s.Event Update: announcements.RateLimitUpdated, Delete: announcements.RateLimitDeleted, } + case fsminformers.InformerKeyHTTPLog: + return &k8s.EventTypes{ + Add: announcements.HTTPLogAdded, + Update: announcements.HTTPLogUpdated, + Delete: announcements.HTTPLogDeleted, + } } return nil diff --git a/pkg/gateway/processor/triggers/extension/httplogs_trigger.go b/pkg/gateway/processor/triggers/extension/httplogs_trigger.go new file mode 100644 index 000000000..ebf83fc21 --- /dev/null +++ b/pkg/gateway/processor/triggers/extension/httplogs_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" +) + +// HTTPLogTrigger is a processor for HTTPLog objects +type HTTPLogTrigger struct{} + +// Insert adds a HTTPLog object to the processor and returns true if the processor is changed +func (p *HTTPLogTrigger) Insert(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.HTTPLog) + if !ok { + log.Error().Msgf("unexpected object type %T", obj) + return false + } + + return processor.IsFilterConfigReferred(config.Kind, client.ObjectKeyFromObject(config)) +} + +// Delete removes a HTTPLog object from the processor and returns true if the processor is changed +func (p *HTTPLogTrigger) Delete(obj interface{}, processor processor.Processor) bool { + config, ok := obj.(*extv1alpha1.HTTPLog) + 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 2dab59507..01d08cfea 100644 --- a/pkg/gateway/processor/v2/filters.go +++ b/pkg/gateway/processor/v2/filters.go @@ -93,6 +93,20 @@ func (c *ConfigGenerator) resolveFilterConfig(ref *gwv1.LocalObjectReference) ma } return toMap("rateLimit", &r2) + case constants.HTTPLogKind: + obj := &extv1alpha1.HTTPLog{} + if err := c.client.Get(ctx, key, obj); err != nil { + log.Error().Msgf("Failed to resolve HTTPLog: %s", err) + return map[string]interface{}{} + } + + l2 := fgwv2.HTTPLogSpec{} + if err := gwutils.DeepCopy(&l2, &obj.Spec); err != nil { + log.Error().Msgf("Failed to copy HTTPLog: %s", err) + return map[string]interface{}{} + } + + return toMap("httpLog", &l2) } return map[string]interface{}{} diff --git a/pkg/gateway/processor/v2/processor.go b/pkg/gateway/processor/v2/processor.go index fb1cae817..259f3c477 100644 --- a/pkg/gateway/processor/v2/processor.go +++ b/pkg/gateway/processor/v2/processor.go @@ -84,6 +84,7 @@ func NewGatewayProcessor(ctx *cctx.ControllerContext) *GatewayProcessor { informers.CircuitBreakersResourceType: &extensiontrigger.CircuitBreakerTrigger{}, informers.FaultInjectionsResourceType: &extensiontrigger.FaultInjectionTrigger{}, informers.RateLimitsResourceType: &extensiontrigger.RateLimitTrigger{}, + informers.HTTPLogsResourceType: &extensiontrigger.HTTPLogTrigger{}, }, mutex: new(sync.RWMutex), @@ -161,6 +162,8 @@ func (c *GatewayProcessor) getTrigger(obj interface{}) processor.Trigger { return c.triggers[informers.FaultInjectionsResourceType] case *extv1alpha1.RateLimit: return c.triggers[informers.RateLimitsResourceType] + case *extv1alpha1.HTTPLog: + return c.triggers[informers.HTTPLogsResourceType] } return nil diff --git a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/extension_client.go b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/extension_client.go index a8c6531f3..a3d2d6de8 100644 --- a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/extension_client.go +++ b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/extension_client.go @@ -29,6 +29,7 @@ type ExtensionV1alpha1Interface interface { FaultInjectionsGetter FiltersGetter FilterDefinitionsGetter + HTTPLogsGetter ListenerFiltersGetter RateLimitsGetter } @@ -54,6 +55,10 @@ func (c *ExtensionV1alpha1Client) FilterDefinitions() FilterDefinitionInterface return newFilterDefinitions(c) } +func (c *ExtensionV1alpha1Client) HTTPLogs(namespace string) HTTPLogInterface { + return newHTTPLogs(c, namespace) +} + func (c *ExtensionV1alpha1Client) ListenerFilters(namespace string) ListenerFilterInterface { return newListenerFilters(c, namespace) } diff --git a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_extension_client.go b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_extension_client.go index 550f81952..04519fbe5 100644 --- a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_extension_client.go +++ b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_extension_client.go @@ -41,6 +41,10 @@ func (c *FakeExtensionV1alpha1) FilterDefinitions() v1alpha1.FilterDefinitionInt return &FakeFilterDefinitions{c} } +func (c *FakeExtensionV1alpha1) HTTPLogs(namespace string) v1alpha1.HTTPLogInterface { + return &FakeHTTPLogs{c, namespace} +} + func (c *FakeExtensionV1alpha1) ListenerFilters(namespace string) v1alpha1.ListenerFilterInterface { return &FakeListenerFilters{c, namespace} } diff --git a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_httplog.go b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_httplog.go new file mode 100644 index 000000000..5a6092e2d --- /dev/null +++ b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/fake/fake_httplog.go @@ -0,0 +1,138 @@ +/* +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 client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHTTPLogs implements HTTPLogInterface +type FakeHTTPLogs struct { + Fake *FakeExtensionV1alpha1 + ns string +} + +var httplogsResource = v1alpha1.SchemeGroupVersion.WithResource("httplogs") + +var httplogsKind = v1alpha1.SchemeGroupVersion.WithKind("HTTPLog") + +// Get takes name of the hTTPLog, and returns the corresponding hTTPLog object, and an error if there is any. +func (c *FakeHTTPLogs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPLog, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(httplogsResource, c.ns, name), &v1alpha1.HTTPLog{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPLog), err +} + +// List takes label and field selectors, and returns the list of HTTPLogs that match those selectors. +func (c *FakeHTTPLogs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPLogList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(httplogsResource, httplogsKind, c.ns, opts), &v1alpha1.HTTPLogList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.HTTPLogList{ListMeta: obj.(*v1alpha1.HTTPLogList).ListMeta} + for _, item := range obj.(*v1alpha1.HTTPLogList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hTTPLogs. +func (c *FakeHTTPLogs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(httplogsResource, c.ns, opts)) + +} + +// Create takes the representation of a hTTPLog and creates it. Returns the server's representation of the hTTPLog, and an error, if there is any. +func (c *FakeHTTPLogs) Create(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.CreateOptions) (result *v1alpha1.HTTPLog, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(httplogsResource, c.ns, hTTPLog), &v1alpha1.HTTPLog{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPLog), err +} + +// Update takes the representation of a hTTPLog and updates it. Returns the server's representation of the hTTPLog, and an error, if there is any. +func (c *FakeHTTPLogs) Update(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (result *v1alpha1.HTTPLog, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(httplogsResource, c.ns, hTTPLog), &v1alpha1.HTTPLog{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPLog), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeHTTPLogs) UpdateStatus(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (*v1alpha1.HTTPLog, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(httplogsResource, "status", c.ns, hTTPLog), &v1alpha1.HTTPLog{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPLog), err +} + +// Delete takes name of the hTTPLog and deletes it. Returns an error if one occurs. +func (c *FakeHTTPLogs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(httplogsResource, c.ns, name, opts), &v1alpha1.HTTPLog{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHTTPLogs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(httplogsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.HTTPLogList{}) + return err +} + +// Patch applies the patch and returns the patched hTTPLog. +func (c *FakeHTTPLogs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPLog, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(httplogsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HTTPLog{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPLog), err +} diff --git a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/generated_expansion.go b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/generated_expansion.go index 8a8f4ae50..e8be5cbe8 100644 --- a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/generated_expansion.go +++ b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/generated_expansion.go @@ -23,6 +23,8 @@ type FilterExpansion interface{} type FilterDefinitionExpansion interface{} +type HTTPLogExpansion interface{} + type ListenerFilterExpansion interface{} type RateLimitExpansion interface{} diff --git a/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/httplog.go b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/httplog.go new file mode 100644 index 000000000..37f88f6e4 --- /dev/null +++ b/pkg/gen/client/extension/clientset/versioned/typed/extension/v1alpha1/httplog.go @@ -0,0 +1,192 @@ +/* +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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + scheme "github.com/flomesh-io/fsm/pkg/gen/client/extension/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// HTTPLogsGetter has a method to return a HTTPLogInterface. +// A group's client should implement this interface. +type HTTPLogsGetter interface { + HTTPLogs(namespace string) HTTPLogInterface +} + +// HTTPLogInterface has methods to work with HTTPLog resources. +type HTTPLogInterface interface { + Create(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.CreateOptions) (*v1alpha1.HTTPLog, error) + Update(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (*v1alpha1.HTTPLog, error) + UpdateStatus(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (*v1alpha1.HTTPLog, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HTTPLog, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPLogList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPLog, err error) + HTTPLogExpansion +} + +// hTTPLogs implements HTTPLogInterface +type hTTPLogs struct { + client rest.Interface + ns string +} + +// newHTTPLogs returns a HTTPLogs +func newHTTPLogs(c *ExtensionV1alpha1Client, namespace string) *hTTPLogs { + return &hTTPLogs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the hTTPLog, and returns the corresponding hTTPLog object, and an error if there is any. +func (c *hTTPLogs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPLog, err error) { + result = &v1alpha1.HTTPLog{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httplogs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of HTTPLogs that match those selectors. +func (c *hTTPLogs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPLogList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.HTTPLogList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httplogs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hTTPLogs. +func (c *hTTPLogs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("httplogs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a hTTPLog and creates it. Returns the server's representation of the hTTPLog, and an error, if there is any. +func (c *hTTPLogs) Create(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.CreateOptions) (result *v1alpha1.HTTPLog, err error) { + result = &v1alpha1.HTTPLog{} + err = c.client.Post(). + Namespace(c.ns). + Resource("httplogs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPLog). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a hTTPLog and updates it. Returns the server's representation of the hTTPLog, and an error, if there is any. +func (c *hTTPLogs) Update(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (result *v1alpha1.HTTPLog, err error) { + result = &v1alpha1.HTTPLog{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httplogs"). + Name(hTTPLog.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPLog). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *hTTPLogs) UpdateStatus(ctx context.Context, hTTPLog *v1alpha1.HTTPLog, opts v1.UpdateOptions) (result *v1alpha1.HTTPLog, err error) { + result = &v1alpha1.HTTPLog{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httplogs"). + Name(hTTPLog.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPLog). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the hTTPLog and deletes it. Returns an error if one occurs. +func (c *hTTPLogs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("httplogs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hTTPLogs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("httplogs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched hTTPLog. +func (c *hTTPLogs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPLog, err error) { + result = &v1alpha1.HTTPLog{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("httplogs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/httplog.go b/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/httplog.go new file mode 100644 index 000000000..6e77fa012 --- /dev/null +++ b/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/httplog.go @@ -0,0 +1,87 @@ +/* +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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + extensionv1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + versioned "github.com/flomesh-io/fsm/pkg/gen/client/extension/clientset/versioned" + internalinterfaces "github.com/flomesh-io/fsm/pkg/gen/client/extension/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/flomesh-io/fsm/pkg/gen/client/extension/listers/extension/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HTTPLogInformer provides access to a shared informer and lister for +// HTTPLogs. +type HTTPLogInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.HTTPLogLister +} + +type hTTPLogInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHTTPLogInformer constructs a new informer for HTTPLog type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHTTPLogInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHTTPLogInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHTTPLogInformer constructs a new informer for HTTPLog type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredHTTPLogInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExtensionV1alpha1().HTTPLogs(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExtensionV1alpha1().HTTPLogs(namespace).Watch(context.TODO(), options) + }, + }, + &extensionv1alpha1.HTTPLog{}, + resyncPeriod, + indexers, + ) +} + +func (f *hTTPLogInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHTTPLogInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *hTTPLogInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&extensionv1alpha1.HTTPLog{}, f.defaultInformer) +} + +func (f *hTTPLogInformer) Lister() v1alpha1.HTTPLogLister { + return v1alpha1.NewHTTPLogLister(f.Informer().GetIndexer()) +} diff --git a/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/interface.go b/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/interface.go index d34174d42..1967b25e0 100644 --- a/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/interface.go +++ b/pkg/gen/client/extension/informers/externalversions/extension/v1alpha1/interface.go @@ -29,6 +29,8 @@ type Interface interface { Filters() FilterInformer // FilterDefinitions returns a FilterDefinitionInformer. FilterDefinitions() FilterDefinitionInformer + // HTTPLogs returns a HTTPLogInformer. + HTTPLogs() HTTPLogInformer // ListenerFilters returns a ListenerFilterInformer. ListenerFilters() ListenerFilterInformer // RateLimits returns a RateLimitInformer. @@ -66,6 +68,11 @@ func (v *version) FilterDefinitions() FilterDefinitionInformer { return &filterDefinitionInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } +// HTTPLogs returns a HTTPLogInformer. +func (v *version) HTTPLogs() HTTPLogInformer { + return &hTTPLogInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // ListenerFilters returns a ListenerFilterInformer. func (v *version) ListenerFilters() ListenerFilterInformer { return &listenerFilterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/gen/client/extension/informers/externalversions/generic.go b/pkg/gen/client/extension/informers/externalversions/generic.go index 9e3bc663b..0bd61c33c 100644 --- a/pkg/gen/client/extension/informers/externalversions/generic.go +++ b/pkg/gen/client/extension/informers/externalversions/generic.go @@ -58,6 +58,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Extension().V1alpha1().Filters().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("filterdefinitions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extension().V1alpha1().FilterDefinitions().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("httplogs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Extension().V1alpha1().HTTPLogs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("listenerfilters"): return &genericInformer{resource: resource.GroupResource(), informer: f.Extension().V1alpha1().ListenerFilters().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("ratelimits"): diff --git a/pkg/gen/client/extension/listers/extension/v1alpha1/expansion_generated.go b/pkg/gen/client/extension/listers/extension/v1alpha1/expansion_generated.go index 7443aeee5..398295797 100644 --- a/pkg/gen/client/extension/listers/extension/v1alpha1/expansion_generated.go +++ b/pkg/gen/client/extension/listers/extension/v1alpha1/expansion_generated.go @@ -43,6 +43,14 @@ type FilterNamespaceListerExpansion interface{} // FilterDefinitionLister. type FilterDefinitionListerExpansion interface{} +// HTTPLogListerExpansion allows custom methods to be added to +// HTTPLogLister. +type HTTPLogListerExpansion interface{} + +// HTTPLogNamespaceListerExpansion allows custom methods to be added to +// HTTPLogNamespaceLister. +type HTTPLogNamespaceListerExpansion interface{} + // ListenerFilterListerExpansion allows custom methods to be added to // ListenerFilterLister. type ListenerFilterListerExpansion interface{} diff --git a/pkg/gen/client/extension/listers/extension/v1alpha1/httplog.go b/pkg/gen/client/extension/listers/extension/v1alpha1/httplog.go new file mode 100644 index 000000000..406f789c5 --- /dev/null +++ b/pkg/gen/client/extension/listers/extension/v1alpha1/httplog.go @@ -0,0 +1,96 @@ +/* +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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/flomesh-io/fsm/pkg/apis/extension/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// HTTPLogLister helps list HTTPLogs. +// All objects returned here must be treated as read-only. +type HTTPLogLister interface { + // List lists all HTTPLogs in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPLog, err error) + // HTTPLogs returns an object that can list and get HTTPLogs. + HTTPLogs(namespace string) HTTPLogNamespaceLister + HTTPLogListerExpansion +} + +// hTTPLogLister implements the HTTPLogLister interface. +type hTTPLogLister struct { + indexer cache.Indexer +} + +// NewHTTPLogLister returns a new HTTPLogLister. +func NewHTTPLogLister(indexer cache.Indexer) HTTPLogLister { + return &hTTPLogLister{indexer: indexer} +} + +// List lists all HTTPLogs in the indexer. +func (s *hTTPLogLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPLog, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPLog)) + }) + return ret, err +} + +// HTTPLogs returns an object that can list and get HTTPLogs. +func (s *hTTPLogLister) HTTPLogs(namespace string) HTTPLogNamespaceLister { + return hTTPLogNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// HTTPLogNamespaceLister helps list and get HTTPLogs. +// All objects returned here must be treated as read-only. +type HTTPLogNamespaceLister interface { + // List lists all HTTPLogs in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPLog, err error) + // Get retrieves the HTTPLog from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.HTTPLog, error) + HTTPLogNamespaceListerExpansion +} + +// hTTPLogNamespaceLister implements the HTTPLogNamespaceLister +// interface. +type hTTPLogNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all HTTPLogs in the indexer for a given namespace. +func (s hTTPLogNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPLog, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPLog)) + }) + return ret, err +} + +// Get retrieves the HTTPLog from the indexer for a given namespace and name. +func (s hTTPLogNamespaceLister) Get(name string) (*v1alpha1.HTTPLog, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("httplog"), name) + } + return obj.(*v1alpha1.HTTPLog), nil +} diff --git a/pkg/k8s/informers/types.go b/pkg/k8s/informers/types.go index a906752ad..5d7346dab 100644 --- a/pkg/k8s/informers/types.go +++ b/pkg/k8s/informers/types.go @@ -150,6 +150,8 @@ const ( InformerKeyListenerFilter InformerKey = "ListenerFilter" // InformerKeyFilterDefinition is the InformerKey for a FilterDefinition informer InformerKeyFilterDefinition InformerKey = "FilterDefinition" + // InformerKeyHTTPLog is the InformerKey for a HTTPLog informer + InformerKeyHTTPLog InformerKey = "HTTPLog" ) const ( @@ -260,4 +262,7 @@ const ( // FilterDefinitionsResourceType is the type used to represent the filter definitions resource FilterDefinitionsResourceType ResourceType = "filterdefinitions" + + // HTTPLogsResourceType is the type used to represent the http logs resource + HTTPLogsResourceType ResourceType = "httplogs" ) diff --git a/pkg/manager/reconciler/registers.go b/pkg/manager/reconciler/registers.go index f67def08c..d8ffcb6d4 100644 --- a/pkg/manager/reconciler/registers.go +++ b/pkg/manager/reconciler/registers.go @@ -310,6 +310,8 @@ func getRegisters(regCfg *whtypes.RegisterConfig, mc configurator.Configurator) reconcilers[GatewayAPIExtensionRateLimit] = extensionv1alpha1.NewRateLimitReconciler(ctx) + reconcilers[GatewayAPIExtensionHTTPLog] = extensionv1alpha1.NewHTTPLogReconciler(ctx) + webhooks[GatewayAPIExtensionFaultInjection] = extwhv1alpha1.NewFaultInjectionWebhook(regCfg) reconcilers[GatewayAPIExtensionFaultInjection] = extensionv1alpha1.NewFaultInjectionReconciler(ctx) } diff --git a/pkg/manager/reconciler/types.go b/pkg/manager/reconciler/types.go index 7c197422f..b024d7188 100644 --- a/pkg/manager/reconciler/types.go +++ b/pkg/manager/reconciler/types.go @@ -28,6 +28,7 @@ const ( GatewayAPIExtensionCircuitBreaker ResourceType = "GatewayAPIExtension(CircuitBreaker)" GatewayAPIExtensionFaultInjection ResourceType = "GatewayAPIExtension(FaultInjection)" GatewayAPIExtensionRateLimit ResourceType = "GatewayAPIExtension(RateLimit)" + GatewayAPIExtensionHTTPLog ResourceType = "GatewayAPIExtension(HTTPLog)" PolicyAttachmentHealthCheck ResourceType = "PolicyAttachment(HealthCheck)" PolicyAttachmentRetry ResourceType = "PolicyAttachment(Retry)" PolicyAttachmentBackendLB ResourceType = "PolicyAttachment(BackendLB)" diff --git a/pkg/messaging/broker.go b/pkg/messaging/broker.go index fc9c0615d..781987964 100644 --- a/pkg/messaging/broker.go +++ b/pkg/messaging/broker.go @@ -1194,6 +1194,8 @@ func getGatewayUpdateEvent(msg events.PubSubMessage) *gatewayUpdateEvent { announcements.ListenerFilterAdded, announcements.ListenerFilterDeleted, announcements.ListenerFilterUpdated, // FilterDefinition event announcements.FilterDefinitionAdded, announcements.FilterDefinitionDeleted, announcements.FilterDefinitionUpdated, + // HTTPLog event + announcements.HTTPLogAdded, announcements.HTTPLogDeleted, announcements.HTTPLogUpdated, // // MultiCluster events