This is an automated email from the ASF dual-hosted git repository.

hoshea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-swck.git


The following commit(s) were added to refs/heads/master by this push:
     new 1e3f050  Add CRD, Controller for SkyWalking Event Exporter (#113)
1e3f050 is described below

commit 1e3f050716f5b90ad7a633f67496eee2424e3b2d
Author: Jicheng Zhi <[email protected]>
AuthorDate: Mon Apr 15 17:39:51 2024 +1000

    Add CRD, Controller for SkyWalking Event Exporter (#113)
    
    * Add CRD and Controller for EventExporter and EventExporterConfig
    
    * Add auto-generated config files for EventExporter and EventExporterConfig
    
    * Merge EventExporterConfig into EventExporter
    
    * Remove EventExporterConfig in PROJECT
    
    * Remove unused type in eventexporter_types.go
    
    * Pass linting
    
    * Refactor event exporter CRD and controller
    
    * Remove unused dependency
    
    * Update EventExporterController rabc and configMap
    
    * Add a simple test case to eventexporter e2e test
    
    * Add eventexporter e2e test to GitHub workflows
    
    * Remove unused UI component in event exporter e2e test
    
    * Update event exporter controller: delete old configMap after creating the 
new one, generate fixed-length name for configMaps
---
 .github/workflows/go.yml                           |  17 +-
 operator/PROJECT                                   |  13 ++
 .../apis/operator/v1alpha1/eventexporter_types.go  |  79 ++++++++
 .../operator/v1alpha1/eventexporter_webhook.go     |  95 +++++++++
 .../operator/v1alpha1/zz_generated.deepcopy.go     |  97 ++++++++-
 ...rator.skywalking.apache.org_eventexporters.yaml | 136 +++++++++++++
 operator/config/crd/kustomization.yaml             |   3 +
 .../cainjection_in_operator_eventexporters.yaml    |  24 +++
 .../webhook_in_operator_eventexporters.yaml        |  33 ++++
 .../rbac/operator_eventexporter_editor_role.yaml   |  48 +++++
 .../rbac/operator_eventexporter_viewer_role.yaml   |  44 +++++
 operator/config/rbac/role.yaml                     |  27 ++-
 operator/config/samples/eventexporter.yaml         |  58 ++++++
 operator/config/webhook/manifests.yaml             |  43 +++-
 .../operator/eventexporter_controller.go           | 218 +++++++++++++++++++++
 operator/main.go                                   |  13 ++
 .../eventexporter/templates/cluster_role.yaml      |  31 +++
 .../templates/cluster_role_binding.yaml            |  32 +++
 .../eventexporter/templates/deployment.yaml        |  65 ++++++
 .../eventexporter/templates/service_account.yaml   |  26 +++
 operator/pkg/operator/manifests/repo.go            |   2 +-
 test/e2e/oap-eventexporter/e2e.yaml                |  83 ++++++++
 .../skywalking-components-with-eventexporter.yaml  |  62 ++++++
 test/e2e/verify/eventexporter-event.yaml           |  23 +++
 24 files changed, 1265 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index e647d9a..1ef554e 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -227,9 +227,24 @@ jobs:
         uses: 
apache/skywalking-infra-e2e@996ed8902e941e2883fcf0ac5b3090364942d205
         with:
           e2e-file: 
test/e2e/oap-ui-agent-oapserverconfig-oapserverdynamicconfig/e2e.yaml
+  oapserver-eventexporter-e2e-tests:
+    name: e2e tests(oap+eventexporter)
+    runs-on: ubuntu-latest
+    steps:
+      - name: Install Go
+        uses: actions/setup-go@v3
+        with:
+          go-version: '1.22'
+        id: go
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v3
+      - name: Run E2E Test
+        uses: 
apache/skywalking-infra-e2e@996ed8902e941e2883fcf0ac5b3090364942d205
+        with:
+          e2e-file: test/e2e/oap-eventexporter/e2e.yaml
   checks:
     name: build
     runs-on: ubuntu-20.04
-    needs: [check, build, unit-tests, e2e-tests, swagent-e2e-tests, 
swagent-configmap-e2e-tests, internel-storage-e2e-tests, 
external-storage-e2e-tests, adapter-hpa-e2e-tests, e2e-tests-with-satellite, 
adapter-satellite-hpa-e2e-tests, oapserver-configuration-e2e-tests]
+    needs: [check, build, unit-tests, e2e-tests, swagent-e2e-tests, 
swagent-configmap-e2e-tests, internel-storage-e2e-tests, 
external-storage-e2e-tests, adapter-hpa-e2e-tests, e2e-tests-with-satellite, 
adapter-satellite-hpa-e2e-tests, oapserver-configuration-e2e-tests, 
oapserver-eventexporter-e2e-tests]
     steps:
       - run: echo 'success'
diff --git a/operator/PROJECT b/operator/PROJECT
index 73b6a37..e603a61 100644
--- a/operator/PROJECT
+++ b/operator/PROJECT
@@ -131,4 +131,17 @@ resources:
   kind: BanyanDB
   path: github.com/apache/skywalking-swck/operator/apis/operator/v1alpha1
   version: v1alpha1
+- api:
+    crdVersion: v1
+    namespaced: true
+  controller: true
+  domain: skywalking.apache.org
+  group: operator
+  kind: EventExporter
+  path: github.com/apache/skywalking-swck/operator/apis/operator/v1alpha1
+  version: v1alpha1
+  webhooks:
+    defaulting: true
+    validation: true
+    webhookVersion: v1
 version: "3"
diff --git a/operator/apis/operator/v1alpha1/eventexporter_types.go 
b/operator/apis/operator/v1alpha1/eventexporter_types.go
new file mode 100644
index 0000000..f548fd7
--- /dev/null
+++ b/operator/apis/operator/v1alpha1/eventexporter_types.go
@@ -0,0 +1,79 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package v1alpha1
+
+import (
+       appsv1 "k8s.io/api/apps/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EventExporterSpec defines the desired state of EventExporter
+type EventExporterSpec struct {
+       // Version of EventExporter.
+       // +kubebuilder:validation:Required
+       Version string `json:"version,omitempty"`
+       // Image is the event exporter Docker image to deploy.
+       Image string `json:"image,omitempty"`
+       // Replicas is the number of event exporter pods
+       // +kubebuilder:validation:Required
+       Replicas int32 `json:"replicas,omitempty"`
+       // Config of filters and exporters
+       // +kubebuilder:validation:Optional
+       Config string `json:"config,omitempty"`
+}
+
+// EventExporterStatus defines the observed state of EventExporter
+type EventExporterStatus struct {
+       // Total number of available pods targeted by this deployment.
+       // +kubebuilder:validation:Optional
+       AvailableReplicas int32 `json:"availableReplicas,omitempty"`
+       // Represents the latest available observations of the underlying 
deployment's current state.
+       // +kubebuilder:validation:Optional
+       Conditions []appsv1.DeploymentCondition `json:"conditions,omitempty"`
+       // Name of the configMap.
+       // +kubebuilder:validation:Optional
+       ConfigMapName string `json:"configMapName,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+// 
+kubebuilder:printcolumn:name="Version",type="string",priority=1,JSONPath=".spec.version",description="The
 version"
+// 
+kubebuilder:printcolumn:name="Image",type="string",priority=1,JSONPath=".spec.image"
+// 
+kubebuilder:printcolumn:name="Instances",type="string",JSONPath=".spec.instances",description="The
 number of expected instances"
+
+// EventExporter is the Schema for the eventexporters API
+type EventExporter struct {
+       metav1.TypeMeta   `json:",inline"`
+       metav1.ObjectMeta `json:"metadata,omitempty"`
+
+       Spec   EventExporterSpec   `json:"spec,omitempty"`
+       Status EventExporterStatus `json:"status,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+
+// EventExporterList contains a list of EventExporter
+type EventExporterList struct {
+       metav1.TypeMeta `json:",inline"`
+       metav1.ListMeta `json:"metadata,omitempty"`
+       Items           []EventExporter `json:"items"`
+}
+
+func init() {
+       SchemeBuilder.Register(&EventExporter{}, &EventExporterList{})
+}
diff --git a/operator/apis/operator/v1alpha1/eventexporter_webhook.go 
b/operator/apis/operator/v1alpha1/eventexporter_webhook.go
new file mode 100644
index 0000000..eac2e55
--- /dev/null
+++ b/operator/apis/operator/v1alpha1/eventexporter_webhook.go
@@ -0,0 +1,95 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package v1alpha1
+
+import (
+       "fmt"
+
+       "k8s.io/apimachinery/pkg/runtime"
+       ctrl "sigs.k8s.io/controller-runtime"
+       logf "sigs.k8s.io/controller-runtime/pkg/log"
+       "sigs.k8s.io/controller-runtime/pkg/webhook"
+       "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const latestVersion = "latest"
+const image = "apache/skywalking-kubernetes-event-exporter"
+
+// log is for logging in this package.
+var eventexporterlog = logf.Log.WithName("eventexporter-resource")
+
+func (r *EventExporter) SetupWebhookWithManager(mgr ctrl.Manager) error {
+       return ctrl.NewWebhookManagedBy(mgr).
+               For(r).
+               Complete()
+}
+
+// nolint: lll
+//+kubebuilder:webhook:path=/mutate-operator-skywalking-apache-org-v1alpha1-eventexporter,mutating=true,failurePolicy=fail,sideEffects=None,groups=operator.skywalking.apache.org,resources=eventexporters,verbs=create;update,versions=v1alpha1,name=meventexporter.kb.io,admissionReviewVersions=v1
+
+var _ webhook.Defaulter = &EventExporter{}
+
+// Default implements webhook.Defaulter so a webhook will be registered for 
the type
+func (r *EventExporter) Default() {
+       eventexporterlog.Info("default", "name", r.Name)
+
+       if r.Spec.Version == "" {
+               r.Spec.Version = latestVersion
+       }
+
+       if r.Spec.Image == "" {
+               r.Spec.Image = fmt.Sprintf("%s:%s", image, r.Spec.Version)
+       }
+
+       if r.Spec.Replicas == 0 {
+               r.Spec.Replicas = 1
+       }
+}
+
+// nolint: lll
+// 
+kubebuilder:webhook:admissionReviewVersions=v1,sideEffects=None,path=/mutate-operator-skywalking-apache-org-v1alpha1-eventexporter,mutating=true,failurePolicy=fail,groups=operator.skywalking.apache.org,resources=eventexporters,verbs=create;update,versions=v1alpha1,name=meventexporter.kb.io
+
+var _ webhook.Validator = &EventExporter{}
+
+// ValidateCreate implements webhook.Validator so a webhook will be registered 
for the type
+func (r *EventExporter) ValidateCreate() (admission.Warnings, error) {
+       eventexporterlog.Info("validate create", "name", r.Name)
+
+       return nil, r.validate()
+}
+
+// ValidateUpdate implements webhook.Validator so a webhook will be registered 
for the type
+func (r *EventExporter) ValidateUpdate(_ runtime.Object) (admission.Warnings, 
error) {
+       eventexporterlog.Info("validate update", "name", r.Name)
+
+       return nil, r.validate()
+}
+
+// ValidateDelete implements webhook.Validator so a webhook will be registered 
for the type
+func (r *EventExporter) ValidateDelete() (admission.Warnings, error) {
+       eventexporterlog.Info("validate delete", "name", r.Name)
+
+       return nil, nil
+}
+
+func (r *EventExporter) validate() error {
+       if r.Spec.Image == "" {
+               return fmt.Errorf("image is absent")
+       }
+       return nil
+}
diff --git a/operator/apis/operator/v1alpha1/zz_generated.deepcopy.go 
b/operator/apis/operator/v1alpha1/zz_generated.deepcopy.go
index 627c68a..340ef8b 100644
--- a/operator/apis/operator/v1alpha1/zz_generated.deepcopy.go
+++ b/operator/apis/operator/v1alpha1/zz_generated.deepcopy.go
@@ -1,5 +1,4 @@
 //go:build !ignore_autogenerated
-// +build !ignore_autogenerated
 
 // Licensed to Apache Software Foundation (ASF) under one or more contributor
 // license agreements. See the NOTICE file distributed with
@@ -155,6 +154,102 @@ func (in *Config) DeepCopy() *Config {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *EventExporter) DeepCopyInto(out *EventExporter) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+       out.Spec = in.Spec
+       in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new EventExporter.
+func (in *EventExporter) DeepCopy() *EventExporter {
+       if in == nil {
+               return nil
+       }
+       out := new(EventExporter)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *EventExporter) 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 *EventExporterList) DeepCopyInto(out *EventExporterList) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ListMeta.DeepCopyInto(&out.ListMeta)
+       if in.Items != nil {
+               in, out := &in.Items, &out.Items
+               *out = make([]EventExporter, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new EventExporterList.
+func (in *EventExporterList) DeepCopy() *EventExporterList {
+       if in == nil {
+               return nil
+       }
+       out := new(EventExporterList)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, 
creating a new runtime.Object.
+func (in *EventExporterList) 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 *EventExporterSpec) DeepCopyInto(out *EventExporterSpec) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new EventExporterSpec.
+func (in *EventExporterSpec) DeepCopy() *EventExporterSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(EventExporterSpec)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *EventExporterStatus) DeepCopyInto(out *EventExporterStatus) {
+       *out = *in
+       if in.Conditions != nil {
+               in, out := &in.Conditions, &out.Conditions
+               *out = make([]v1.DeploymentCondition, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new EventExporterStatus.
+func (in *EventExporterStatus) DeepCopy() *EventExporterStatus {
+       if in == nil {
+               return nil
+       }
+       out := new(EventExporterStatus)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *Fetcher) DeepCopyInto(out *Fetcher) {
        *out = *in
diff --git 
a/operator/config/crd/bases/operator.skywalking.apache.org_eventexporters.yaml 
b/operator/config/crd/bases/operator.skywalking.apache.org_eventexporters.yaml
new file mode 100644
index 0000000..568bb11
--- /dev/null
+++ 
b/operator/config/crd/bases/operator.skywalking.apache.org_eventexporters.yaml
@@ -0,0 +1,136 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.14.0
+  name: eventexporters.operator.skywalking.apache.org
+spec:
+  group: operator.skywalking.apache.org
+  names:
+    kind: EventExporter
+    listKind: EventExporterList
+    plural: eventexporters
+    singular: eventexporter
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - description: The version
+      jsonPath: .spec.version
+      name: Version
+      priority: 1
+      type: string
+    - jsonPath: .spec.image
+      name: Image
+      priority: 1
+      type: string
+    - description: The number of expected instances
+      jsonPath: .spec.instances
+      name: Instances
+      type: string
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: EventExporter is the Schema for the eventexporters 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: EventExporterSpec defines the desired state of 
EventExporter
+            properties:
+              config:
+                description: Config of filters and exporters
+                type: string
+              image:
+                description: Image is the event exporter Docker image to 
deploy.
+                type: string
+              replicas:
+                description: Replicas is the number of event exporter pods
+                format: int32
+                type: integer
+              version:
+                description: Version of EventExporter.
+                type: string
+            type: object
+          status:
+            description: EventExporterStatus defines the observed state of 
EventExporter
+            properties:
+              availableReplicas:
+                description: Total number of available pods targeted by this 
deployment.
+                format: int32
+                type: integer
+              conditions:
+                description: Represents the latest available observations of 
the underlying
+                  deployment's current state.
+                items:
+                  description: DeploymentCondition describes the state of a 
deployment
+                    at a certain point.
+                  properties:
+                    lastTransitionTime:
+                      description: Last time the condition transitioned from 
one status
+                        to another.
+                      format: date-time
+                      type: string
+                    lastUpdateTime:
+                      description: The last time this condition was updated.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A human readable message indicating details 
about
+                        the transition.
+                      type: string
+                    reason:
+                      description: The reason for the condition's last 
transition.
+                      type: string
+                    status:
+                      description: Status of the condition, one of True, 
False, Unknown.
+                      type: string
+                    type:
+                      description: Type of deployment condition.
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              configMapName:
+                description: Name of the configMap.
+                type: string
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/operator/config/crd/kustomization.yaml 
b/operator/config/crd/kustomization.yaml
index 468804a..109b8ec 100644
--- a/operator/config/crd/kustomization.yaml
+++ b/operator/config/crd/kustomization.yaml
@@ -29,6 +29,7 @@ resources:
 - bases/operator.skywalking.apache.org_oapserverconfigs.yaml
 - bases/operator.skywalking.apache.org_oapserverdynamicconfigs.yaml
 - bases/operator.skywalking.apache.org_banyandbs.yaml
+- bases/operator.skywalking.apache.org_eventexporters.yaml
 #+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -44,6 +45,7 @@ patchesStrategicMerge:
 #- patches/webhook_in_oapserverdynamicconfigs.yaml
 #- patches/webhook_in_swagents.yaml
 #- patches/webhook_in_banyandbs.yaml
+#- patches/webhook_in_eventexporters.yaml
 #+kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable cert-manager, uncomment all the sections with 
[CERTMANAGER] prefix.
@@ -58,6 +60,7 @@ patchesStrategicMerge:
 #- patches/cainjection_in_oapserverdynamicconfigs.yaml
 #- patches/cainjection_in_swagents.yaml
 #- patches/cainjection_in_banyandbs.yaml
+#- patches/cainjection_in_eventexporters.yaml
 #+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for 
CRDs.
diff --git 
a/operator/config/crd/patches/cainjection_in_operator_eventexporters.yaml 
b/operator/config/crd/patches/cainjection_in_operator_eventexporters.yaml
new file mode 100644
index 0000000..0519865
--- /dev/null
+++ b/operator/config/crd/patches/cainjection_in_operator_eventexporters.yaml
@@ -0,0 +1,24 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The following patch adds a directive for certmanager to inject CA into the 
CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: 
$(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: eventexporters.operator.skywalking.apache.org
diff --git 
a/operator/config/crd/patches/webhook_in_operator_eventexporters.yaml 
b/operator/config/crd/patches/webhook_in_operator_eventexporters.yaml
new file mode 100644
index 0000000..9751a89
--- /dev/null
+++ b/operator/config/crd/patches/webhook_in_operator_eventexporters.yaml
@@ -0,0 +1,33 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The following patch enables a conversion webhook for the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: eventexporters.operator.skywalking.apache.org
+spec:
+  conversion:
+    strategy: Webhook
+    webhook:
+      clientConfig:
+        service:
+          namespace: system
+          name: webhook-service
+          path: /convert
+      conversionReviewVersions:
+      - v1
diff --git a/operator/config/rbac/operator_eventexporter_editor_role.yaml 
b/operator/config/rbac/operator_eventexporter_editor_role.yaml
new file mode 100644
index 0000000..e0f27b7
--- /dev/null
+++ b/operator/config/rbac/operator_eventexporter_editor_role.yaml
@@ -0,0 +1,48 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# permissions for end users to edit eventexporters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: eventexporter-editor-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: skywalking-swck
+    app.kubernetes.io/part-of: skywalking-swck
+    app.kubernetes.io/managed-by: kustomize
+  name: eventexporter-editor-role
+rules:
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters/status
+  verbs:
+  - get
diff --git a/operator/config/rbac/operator_eventexporter_viewer_role.yaml 
b/operator/config/rbac/operator_eventexporter_viewer_role.yaml
new file mode 100644
index 0000000..e5202e1
--- /dev/null
+++ b/operator/config/rbac/operator_eventexporter_viewer_role.yaml
@@ -0,0 +1,44 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# permissions for end users to view eventexporters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: eventexporter-viewer-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: skywalking-swck
+    app.kubernetes.io/part-of: skywalking-swck
+    app.kubernetes.io/managed-by: kustomize
+  name: eventexporter-viewer-role
+rules:
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters/status
+  verbs:
+  - get
diff --git a/operator/config/rbac/role.yaml b/operator/config/rbac/role.yaml
index 9e941d3..01d4d38 100644
--- a/operator/config/rbac/role.yaml
+++ b/operator/config/rbac/role.yaml
@@ -19,7 +19,6 @@
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
-  creationTimestamp: null
   name: manager-role
 rules:
 - apiGroups:
@@ -179,6 +178,32 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - eventexporters/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - operator.skywalking.apache.org
   resources:
diff --git a/operator/config/samples/eventexporter.yaml 
b/operator/config/samples/eventexporter.yaml
new file mode 100644
index 0000000..edab9d0
--- /dev/null
+++ b/operator/config/samples/eventexporter.yaml
@@ -0,0 +1,58 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: operator.skywalking.apache.org/v1alpha1
+kind: EventExporter
+metadata:
+  name: eventexporter-sample
+spec:
+  replicas: 1
+  config: |
+    filters:
+      - reason: ""     
+        message: ""    
+        minCount: 1    
+        type: ""       
+        action: ""     
+        kind: "Pod|Service"       
+        namespace: "^default$"  
+        name: ""       
+        service: "[^\\s]{1,}"  
+        exporters:     
+          - skywalking
+    exporters:         
+      skywalking:      
+        template:      
+          source:
+            service: "{{ .Service.Name }}"
+            serviceInstance: "{{ .Pod.Name }}"
+            endpoint: ""
+          message: "{{ .Event.Message }}" 
+        address: "default-oap:11800"
+
+---
+apiVersion: operator.skywalking.apache.org/v1alpha1
+kind: OAPServer
+metadata:
+  name: default
+spec:
+  version: 9.5.0
+  instances: 1
+  image: apache/skywalking-oap-server:9.5.0
+  service:
+    template:
+      type: ClusterIP
\ No newline at end of file
diff --git a/operator/config/webhook/manifests.yaml 
b/operator/config/webhook/manifests.yaml
index 5c1811c..24970d7 100644
--- a/operator/config/webhook/manifests.yaml
+++ b/operator/config/webhook/manifests.yaml
@@ -19,7 +19,6 @@
 apiVersion: admissionregistration.k8s.io/v1
 kind: MutatingWebhookConfiguration
 metadata:
-  creationTimestamp: null
   name: mutating-webhook-configuration
 webhooks:
 - admissionReviewVersions:
@@ -42,6 +41,46 @@ webhooks:
     resources:
     - banyandbs
   sideEffects: None
+- admissionReviewVersions:
+  - v1
+  clientConfig:
+    service:
+      name: webhook-service
+      namespace: system
+      path: /mutate-operator-skywalking-apache-org-v1alpha1-eventexporter
+  failurePolicy: Fail
+  name: meventexporter.kb.io
+  rules:
+  - apiGroups:
+    - operator.skywalking.apache.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    - UPDATE
+    resources:
+    - eventexporters
+  sideEffects: None
+- admissionReviewVersions:
+  - v1
+  clientConfig:
+    service:
+      name: webhook-service
+      namespace: system
+      path: /mutate-operator-skywalking-apache-org-v1alpha1-eventexporter
+  failurePolicy: Fail
+  name: meventexporter.kb.io
+  rules:
+  - apiGroups:
+    - operator.skywalking.apache.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    - UPDATE
+    resources:
+    - eventexporters
+  sideEffects: None
 - admissionReviewVersions:
   - v1
   clientConfig:
@@ -242,12 +281,10 @@ webhooks:
     resources:
     - pods
   sideEffects: None
-
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
 metadata:
-  creationTimestamp: null
   name: validating-webhook-configuration
 webhooks:
 - admissionReviewVersions:
diff --git a/operator/controllers/operator/eventexporter_controller.go 
b/operator/controllers/operator/eventexporter_controller.go
new file mode 100644
index 0000000..ba94285
--- /dev/null
+++ b/operator/controllers/operator/eventexporter_controller.go
@@ -0,0 +1,218 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package operator
+
+import (
+       "context"
+       "fmt"
+       "text/template"
+
+       "github.com/go-logr/logr"
+       l "github.com/sirupsen/logrus"
+       apps "k8s.io/api/apps/v1"
+       core "k8s.io/api/core/v1"
+       apiequal "k8s.io/apimachinery/pkg/api/equality"
+       apierrors "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/client-go/tools/record"
+       "k8s.io/client-go/util/retry"
+       ctrl "sigs.k8s.io/controller-runtime"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       runtimelog "sigs.k8s.io/controller-runtime/pkg/log"
+
+       operatorv1alpha1 
"github.com/apache/skywalking-swck/operator/apis/operator/v1alpha1"
+       "github.com/apache/skywalking-swck/operator/pkg/kubernetes"
+)
+
+var useImmutableConfigMap = true
+
+// EventExporterReconciler reconciles a EventExporter object
+type EventExporterReconciler struct {
+       client.Client
+       Scheme   *runtime.Scheme
+       FileRepo kubernetes.Repo
+       Recorder record.EventRecorder
+}
+
+// 
+kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=eventexporters,verbs=get;list;watch;create;update;patch;delete
+// 
+kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=eventexporters/status,verbs=get;update;patch
+// 
+kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=eventexporters/finalizers,verbs=update
+// 
+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+// 
+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
+
+func (r *EventExporterReconciler) Reconcile(ctx context.Context, req 
ctrl.Request) (ctrl.Result, error) {
+       log := runtimelog.FromContext(ctx)
+       log.Info(fmt.Sprintf("===============eventexporter reconcile started 
(ns: %s, name: %s)===============", req.Namespace, req.Name))
+
+       eventExporter := operatorv1alpha1.EventExporter{}
+       if err := r.Client.Get(ctx, req.NamespacedName, &eventExporter); err != 
nil {
+               return ctrl.Result{}, client.IgnoreNotFound(err)
+       }
+
+       newConfigMapName := configMapName(&eventExporter)
+       if _, err := r.overlayData(ctx, log, &eventExporter, newConfigMapName); 
err != nil {
+               log.Error(err, "failed to delete eventexporter's configMap")
+               return ctrl.Result{}, err
+       }
+
+       ff, err := r.FileRepo.GetFilesRecursive("templates")
+       if err != nil {
+               log.Error(err, "failed to load resource templates")
+               return ctrl.Result{}, err
+       }
+
+       app := kubernetes.Application{
+               Client:   r.Client,
+               FileRepo: r.FileRepo,
+               CR:       &eventExporter,
+               GVK:      
operatorv1alpha1.GroupVersion.WithKind("EventExporter"),
+               Recorder: r.Recorder,
+               TmplFunc: template.FuncMap{
+                       "configMapName": func() string { return 
newConfigMapName },
+               },
+       }
+
+       if err := app.ApplyAll(ctx, ff, log); err != nil {
+               return ctrl.Result{}, err
+       }
+
+       if err := r.checkState(ctx, log, &eventExporter); err != nil {
+               l.Error(err, "failed to check sub resources state")
+               return ctrl.Result{}, err
+       }
+
+       return ctrl.Result{RequeueAfter: schedDuration}, nil
+
+}
+
+func (r *EventExporterReconciler) overlayData(ctx context.Context, log 
logr.Logger,
+       eventExporter *operatorv1alpha1.EventExporter, newConfigMapName string) 
(changed bool, err error) {
+
+       oldConfigMapName := eventExporter.Status.ConfigMapName
+       if oldConfigMapName == newConfigMapName {
+               log.Info("eventexporter configuration keeps the same as before")
+               return false, nil
+       }
+
+       newConfigMap := core.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: eventExporter.Namespace,
+                       Name:      newConfigMapName,
+                       OwnerReferences: []metav1.OwnerReference{
+                               {
+                                       APIVersion: eventExporter.APIVersion,
+                                       Kind:       eventExporter.Kind,
+                                       Name:       eventExporter.Name,
+                                       UID:        eventExporter.UID,
+                               },
+                       },
+                       Labels: map[string]string{
+                               "app": "eventexporter",
+                               
"operator.skywalking.apache.org/eventexporter-name": eventExporter.Name,
+                               "operator.skywalking.apache.org/application":   
     "eventexporter",
+                               "operator.skywalking.apache.org/component":     
     "configmap",
+                       },
+               },
+               Immutable: &useImmutableConfigMap,
+               Data:      map[string]string{"config.yaml": 
eventExporter.Spec.Config},
+       }
+
+       if err = r.Client.Create(ctx, &newConfigMap); err != nil {
+               log.Error(err, "failed to create eventexporter's configmap")
+               return true, err
+       }
+
+       oldConfigMap := core.ConfigMap{}
+       err = r.Client.Get(ctx, client.ObjectKey{Namespace: 
eventExporter.Namespace, Name: oldConfigMapName}, &oldConfigMap)
+       if err != nil && !apierrors.IsNotFound(err) {
+               log.Error(err, "failed to get the eventexporter's configmap")
+               return true, err
+       }
+
+       if !apierrors.IsNotFound(err) {
+               if err = r.Client.Delete(ctx, &oldConfigMap); err != nil {
+                       log.Error(err, "failed to delete eventexporter's 
configmap")
+                       return true, err
+               }
+       }
+
+       return true, nil
+}
+
+func (r *EventExporterReconciler) checkState(ctx context.Context, log 
logr.Logger, eventExporter *operatorv1alpha1.EventExporter) error {
+       overlay := operatorv1alpha1.EventExporterStatus{}
+       deployment := apps.Deployment{}
+       errCol := new(kubernetes.ErrorCollector)
+
+       if err := r.Client.Get(ctx, client.ObjectKey{Namespace: 
eventExporter.Namespace,
+               Name: eventExporter.Name + "-eventexporter"}, &deployment); err 
!= nil && !apierrors.IsNotFound(err) {
+               errCol.Collect(fmt.Errorf("failed to get deployment: %w", err))
+       } else {
+               overlay.Conditions = deployment.Status.Conditions
+               overlay.AvailableReplicas = deployment.Status.AvailableReplicas
+               overlay.ConfigMapName = configMapName(eventExporter)
+       }
+
+       if apiequal.Semantic.DeepDerivative(overlay, eventExporter.Status) {
+               log.Info("Status keeps the same as before")
+               return errCol.Error()
+       }
+
+       if err := r.updateStatus(ctx, eventExporter, overlay, errCol); err != 
nil {
+               errCol.Collect(fmt.Errorf("failed to update status of 
EventExporter: %w", err))
+       }
+
+       log.Info("updated Status sub resource")
+
+       return errCol.Error()
+}
+
+func (r *EventExporterReconciler) updateStatus(ctx context.Context, 
eventExporter *operatorv1alpha1.EventExporter,
+       overlay operatorv1alpha1.EventExporterStatus, errCol 
*kubernetes.ErrorCollector) error {
+       // avoid resource conflict
+       return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
+               if err := r.Client.Get(ctx, client.ObjectKey{Name: 
eventExporter.Name, Namespace: eventExporter.Namespace}, eventExporter); err != 
nil {
+                       errCol.Collect(fmt.Errorf("failed to get EventExporter: 
%w", err))
+               }
+
+               eventExporter.Status = overlay
+               eventExporter.Kind = "EventExporter"
+               if err := kubernetes.ApplyOverlay(eventExporter, 
&operatorv1alpha1.EventExporter{Status: overlay}); err != nil {
+                       errCol.Collect(fmt.Errorf("failed to apply overlay: 
%w", err))
+               }
+
+               if err := r.Status().Update(ctx, eventExporter); err != nil {
+                       errCol.Collect(fmt.Errorf("failed to update status of 
EventExporter: %w", err))
+               }
+               return errCol.Error()
+       })
+}
+
+func configMapName(eventExporter *operatorv1alpha1.EventExporter) string {
+       md5 := MD5Hash(eventExporter.Name + eventExporter.Spec.Config)
+       return fmt.Sprintf("eventexporter-cm-%s", md5)
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *EventExporterReconciler) SetupWithManager(mgr ctrl.Manager) error {
+       return ctrl.NewControllerManagedBy(mgr).
+               For(&operatorv1alpha1.EventExporter{}).
+               Complete(r)
+}
diff --git a/operator/main.go b/operator/main.go
index 7ee7408..c048484 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -170,6 +170,15 @@ func main() {
                setupLog.Error(err, "unable to create controller", 
"controller", "BanyanDB")
                os.Exit(1)
        }
+       if err = (&operatorcontrollers.EventExporterReconciler{
+               Client:   mgr.GetClient(),
+               Scheme:   mgr.GetScheme(),
+               FileRepo: manifests.NewRepo("eventexporter"),
+               Recorder: mgr.GetEventRecorderFor("eventexporter-controller"),
+       }).SetupWithManager(mgr); err != nil {
+               setupLog.Error(err, "unable to create controller", 
"controller", "EventExporter")
+               os.Exit(1)
+       }
        //+kubebuilder:scaffold:builder
        if os.Getenv("ENABLE_WEBHOOKS") != "false" {
                if err = 
(&operatorv1alpha1.OAPServer{}).SetupWebhookWithManager(mgr); err != nil {
@@ -212,6 +221,10 @@ func main() {
                        setupLog.Error(err, "unable to create webhook", 
"webhook", "BanyanDB")
                        os.Exit(1)
                }
+               if err = 
(&operatorv1alpha1.EventExporter{}).SetupWebhookWithManager(mgr); err != nil {
+                       setupLog.Error(err, "unable to create webhook", 
"webhook", "EventExporter")
+                       os.Exit(1)
+               }
                // register a webhook to enable the java agent injector
                setupLog.Info("registering /mutate-v1-pod webhook")
                mgr.GetWebhookServer().Register("/mutate-v1-pod",
diff --git 
a/operator/pkg/operator/manifests/eventexporter/templates/cluster_role.yaml 
b/operator/pkg/operator/manifests/eventexporter/templates/cluster_role.yaml
new file mode 100644
index 0000000..1566e6b
--- /dev/null
+++ b/operator/pkg/operator/manifests/eventexporter/templates/cluster_role.yaml
@@ -0,0 +1,31 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: swck:eventexporter
+  labels:
+    operator.skywalking.apache.org/application: eventexporter
+    operator.skywalking.apache.org/component: rbac
+rules:
+  - apiGroups: [""]
+    resources: ["pods", "endpoints", "configmaps", "events", "services"]
+    verbs: ["get", "watch", "list"]
+  - apiGroups: ["extensions"]
+    resources: ["deployments", "replicasets"]
+    verbs: ["get", "watch", "list"]
diff --git 
a/operator/pkg/operator/manifests/eventexporter/templates/cluster_role_binding.yaml
 
b/operator/pkg/operator/manifests/eventexporter/templates/cluster_role_binding.yaml
new file mode 100644
index 0000000..e174eb0
--- /dev/null
+++ 
b/operator/pkg/operator/manifests/eventexporter/templates/cluster_role_binding.yaml
@@ -0,0 +1,32 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: swck:eventexporter
+  labels:
+    operator.skywalking.apache.org/application: eventexporter
+    operator.skywalking.apache.org/component: rbac
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: swck:eventexporter
+subjects:
+  - kind: ServiceAccount
+    name: {{ .Name }}-eventexporter
+    namespace: {{ .Namespace }}
diff --git 
a/operator/pkg/operator/manifests/eventexporter/templates/deployment.yaml 
b/operator/pkg/operator/manifests/eventexporter/templates/deployment.yaml
new file mode 100644
index 0000000..3d3629e
--- /dev/null
+++ b/operator/pkg/operator/manifests/eventexporter/templates/deployment.yaml
@@ -0,0 +1,65 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Name }}-eventexporter
+  namespace: {{ .Namespace }}
+  labels:
+    app: eventexporter
+    operator.skywalking.apache.org/eventexporter-name: {{ .Name }}
+    operator.skywalking.apache.org/application: eventexporter
+    operator.skywalking.apache.org/component: deployment
+spec:
+  replicas: {{ .Spec.Replicas }}
+  selector:
+    matchLabels:
+      operator.skywalking.apache.org/eventexporter-name: {{ .Name }}
+  template:
+    metadata:
+      labels:
+        operator.skywalking.apache.org/eventexporter-name: {{ .Name }}
+        operator.skywalking.apache.org/application: eventexporter
+        operator.skywalking.apache.org/component: pod
+    spec:
+      serviceAccountName: {{ .Name }}-eventexporter
+      affinity:
+        podAntiAffinity:
+          preferredDuringSchedulingIgnoredDuringExecution:
+            - weight: 1
+              podAffinityTerm:
+                topologyKey: kubernetes.io/hostname
+                labelSelector:
+                  matchLabels:
+                    app: eventexporter
+                    operator.skywalking.apache.org/eventexporter-name: {{ 
.Name }}
+      containers:
+        - name: eventexporter
+          image: {{ .Spec.Image }}
+          imagePullPolicy: IfNotPresent
+          args:
+            - start
+            - -v=debug
+            - -c=/data/config.yaml
+          volumeMounts:
+            - mountPath: /data
+              name: config
+      volumes:
+        - name: config
+          configMap:
+            name: {{ configMapName }}
diff --git 
a/operator/pkg/operator/manifests/eventexporter/templates/service_account.yaml 
b/operator/pkg/operator/manifests/eventexporter/templates/service_account.yaml
new file mode 100644
index 0000000..1cf5baa
--- /dev/null
+++ 
b/operator/pkg/operator/manifests/eventexporter/templates/service_account.yaml
@@ -0,0 +1,26 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ .Name }}-eventexporter
+  namespace: {{ .Namespace }}
+  labels:
+    operator.skywalking.apache.org/fetcher-name: {{ .Name }}
+    operator.skywalking.apache.org/application: eventexporter
+    operator.skywalking.apache.org/component: rbac
diff --git a/operator/pkg/operator/manifests/repo.go 
b/operator/pkg/operator/manifests/repo.go
index 769697f..420cdf5 100644
--- a/operator/pkg/operator/manifests/repo.go
+++ b/operator/pkg/operator/manifests/repo.go
@@ -28,7 +28,7 @@ import (
 
 var _ kubernetes.Repo = &AssetsRepo{}
 
-//go:embed fetcher injector oapserver satellite storage ui banyandb
+//go:embed fetcher injector oapserver satellite storage ui banyandb 
eventexporter
 var manifests embed.FS
 
 // AssetsRepo provides templates through assets
diff --git a/test/e2e/oap-eventexporter/e2e.yaml 
b/test/e2e/oap-eventexporter/e2e.yaml
new file mode 100644
index 0000000..9116bd9
--- /dev/null
+++ b/test/e2e/oap-eventexporter/e2e.yaml
@@ -0,0 +1,83 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+setup:
+  env: kind
+  file: ../kind.yaml
+  steps:
+    - name: prepare e2e.yaml
+      command: bash hack/prepare-e2e.sh
+    - name: install cert-manager
+      command: |
+        # kind k8s cluster is in $TMPDIR
+        export KUBECONFIG=$TMPDIR/e2e-k8s.config
+        kubectl apply -f 
https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml
+      wait:
+        - namespace: cert-manager
+          resource: pod
+          for: condition=Ready
+    - name: install operator
+      command: |
+        export OPERATOR_IMG=controller
+        make -C operator docker-build   
+        kind load docker-image controller
+        make -C operator install
+        make -C operator deploy
+      wait:
+        - namespace: skywalking-swck-system
+          resource: pod
+          for: condition=Ready
+    - name: setup oapserver and eventexporter
+      command: |
+        kubectl create namespace skywalking-system
+        kubectl apply -f test/e2e/skywalking-components-with-eventexporter.yaml
+      wait:
+        - namespace: skywalking-system
+          resource: OAPServer/skywalking-system
+          for: condition=Available
+        - namespace: skywalking-system
+          resource: EventExporter/skywalking-system
+          for: condition=Available
+    - name: setup java agent demo
+      command: |
+        kubectl label namespace skywalking-system swck-injection=enabled
+        sed 's/oap-service/skywalking-system-oap.skywalking-system/' 
test/e2e/demo.yaml | kubectl create -f -
+      wait:
+        - namespace: skywalking-system
+          resource: deployment/demo
+          for: condition=Available
+
+  kind:
+    expose-ports:
+      - namespace: skywalking-system
+        resource: service/skywalking-system-oap
+        port: 12800
+  timeout: 20m
+
+cleanup:
+  # always never success failure
+  on: always
+
+verify:
+  # verify with retry strategy
+  retry:
+    # max retry count
+    count: 10
+    # the interval between two attempts, e.g. 10s, 1m.
+    interval: 10s
+  cases:
+    # test eventexporter
+    - query: 'swctl --display yaml 
--base-url=http://${service_skywalking_system_oap_host}:${service_skywalking_system_oap_12800}/graphql
 event ls | yq e ''.events | map({"service": .source.service, "name": .name})'''
+      expected: ../verify/eventexporter-event.yaml
\ No newline at end of file
diff --git a/test/e2e/skywalking-components-with-eventexporter.yaml 
b/test/e2e/skywalking-components-with-eventexporter.yaml
new file mode 100644
index 0000000..484c059
--- /dev/null
+++ b/test/e2e/skywalking-components-with-eventexporter.yaml
@@ -0,0 +1,62 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: operator.skywalking.apache.org/v1alpha1
+kind: OAPServer
+metadata:
+  name: skywalking-system
+  namespace: skywalking-system
+spec:
+  version: 9.5.0
+  instances: 1
+  image: apache/skywalking-oap-server:9.5.0
+  service:
+    template:
+      type: ClusterIP
+
+---
+apiVersion: operator.skywalking.apache.org/v1alpha1
+kind: EventExporter
+metadata:
+  name: skywalking-system
+  namespace: skywalking-system
+spec:
+  replicas: 1
+  config: |
+    filters:
+      - reason: ""     
+        message: ""    
+        minCount: 1    
+        type: ""       
+        action: ""     
+        kind: "Pod|Service"
+        namespace: "^skywalking-system$"  
+        name: ""       
+        service: "[^\\s]{1,}"  
+        exporters:     
+          - skywalking 
+    exporters:         
+      skywalking:      
+        template:      
+          source:
+            service: "{{ .Service.Name }}"
+            serviceInstance: "{{ .Pod.Name }}"
+            endpoint: ""
+          message: "{{ .Event.Message }}" 
+        address: "skywalking-system-oap.skywalking-system:11800"
+
+    
\ No newline at end of file
diff --git a/test/e2e/verify/eventexporter-event.yaml 
b/test/e2e/verify/eventexporter-event.yaml
new file mode 100644
index 0000000..015f1c8
--- /dev/null
+++ b/test/e2e/verify/eventexporter-event.yaml
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{{- contains . }}
+- service: demo
+  name: Pulled
+- service: demo
+  name: Started
+- service: demo
+  name: Created
+{{- end}}
\ No newline at end of file

Reply via email to