From 9775e3f51625fb33350d0b44b6a6ded5566f28a7 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 12:58:57 +0800 Subject: [PATCH 01/12] Test:support create service Signed-off-by: Mcduller <1596582524@qq.com> --- config/definition/default.yaml | 43 +++++++++ .../triggerservice_controller.go | 96 ++++++++++++++----- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index d42ef6b..a193e21 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -109,6 +109,40 @@ spec: } } + if parameter.createService { + service:{ + apiVersion: "v1" + kind: "Service" + metadata: { + name: parameter.name + namespace: triggerService.namespace + } + spec:{ + selector:{ + "app.kubernetes.io/name": parameter.name + "trigger.oam.dev/name": triggerService.name + } + ports:[ + { + name: "default" + port: 80 + targetPort: 80 + }, + { + for _,v in parameter.service.ports{ + name: v.name + port: v.port + targetPort: v.targetPort + } + }] + if parameter.service.clusterIp != _|_ { + clusterIp: parameter.service.clusterIp + } + type: "ClusterIP" + } + } + } + triggerService: { name: string namespace: *"vela-system" | string @@ -147,4 +181,13 @@ spec: ip?: string hostNames?: [...string] }] + createService: *true | bool + service?: { + ports:[...{ + name: string + port: int + targetPort: int|string + }] + clusterIp?: string + } } diff --git a/controllers/triggerservice/triggerservice_controller.go b/controllers/triggerservice/triggerservice_controller.go index 8749eec..1403c40 100644 --- a/controllers/triggerservice/triggerservice_controller.go +++ b/controllers/triggerservice/triggerservice_controller.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "cuelang.org/go/cue" "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -35,6 +34,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "cuelang.org/go/cue" + "github.com/kubevela/pkg/cue/cuex" "github.com/kubevela/pkg/util/slices" "github.com/kubevela/pkg/util/template/definition" @@ -91,25 +92,36 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu return ctrl.Result{}, err } - deploymentList := &appsv1.DeploymentList{} - if err := r.List(ctx, deploymentList, client.MatchingLabels(map[string]string{ - triggerNameLabel: ts.Name, - })); err != nil { + if err := r.handleWorker(ctx, ts); err != nil { + logger.Error(err, "failed to handle trigger worker") return ctrl.Result{}, err } - // if no worker deployment exists, create a new one to run trigger - if len(deploymentList.Items) == 0 { - if err := r.createWorker(ctx, ts); err != nil { - logger.Error(err, "failed to create worker deployment for trigger") - return ctrl.Result{}, err - } - logger.Info("successfully create worker deployment for trigger") + return ctrl.Result{}, nil +} + +func (r *Reconciler) handleWorker(ctx context.Context, ts *standardv1alpha1.TriggerService) error { + + v, err := r.loadTemplateCueValue(ctx, ts) + if err != nil { + return err } - return ctrl.Result{}, nil + if err := r.createRoles(ctx, ts, v); err != nil { + return err + } + + if err := r.createDeployment(ctx, ts, v); err != nil { + return err + } + + if err := r.createService(ctx, ts, v); err != nil { + return err + } + + return nil } -func (r *Reconciler) createWorker(ctx context.Context, ts *standardv1alpha1.TriggerService) error { +func (r *Reconciler) loadTemplateCueValue(ctx context.Context, ts *standardv1alpha1.TriggerService) (cue.Value, error) { templateName := "default" opts := make([]cuex.CompileOption, 0) opts = append(opts, cuex.WithExtraData("triggerService", map[string]string{ @@ -124,25 +136,38 @@ func (r *Reconciler) createWorker(ctx context.Context, ts *standardv1alpha1.Trig } template, err := definition.NewTemplateLoader(ctx, r.Client).LoadTemplate(ctx, templateName, definition.WithType(triggertypes.DefinitionTypeTriggerWorker)) if err != nil { - return err + return cue.Value{}, err } + v, err := cuex.CompileStringWithOptions(ctx, template.Compile(), opts...) if err != nil { - return err - } - if err := r.createRoles(ctx, ts, v); err != nil { - return err + return cue.Value{}, err } + + return v, nil +} + +func (r *Reconciler) createDeployment(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { data, err := v.LookupPath(cue.ParsePath("deployment")).MarshalJSON() if err != nil { return err } - deploy := &appsv1.Deployment{} - if err := json.Unmarshal(data, deploy); err != nil { + + expectedDeployment := new(appsv1.Deployment) + if err := json.Unmarshal(data, expectedDeployment); err != nil { return err } - utils.SetOwnerReference(deploy, ts) - return r.Create(ctx, deploy) + + existDeployment := new(appsv1.Deployment) + if err := r.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existDeployment); err != nil { + if apierrors.IsNotFound(err) { + utils.SetOwnerReference(expectedDeployment, ts) + return r.Create(ctx, expectedDeployment) + } + return err + } + + return r.Patch(ctx, expectedDeployment, client.Merge) } func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { @@ -183,6 +208,31 @@ func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.Trigg return nil } +func (r *Reconciler) createService(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { + data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + if err != nil { + return err + } + if data == nil { + return nil + } + + expectSvc := new(corev1.Service) + if err := json.Unmarshal(data, expectSvc); err != nil { + return err + } + + existSvc := new(corev1.Service) + if err := r.Client.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existSvc); err != nil { + if apierrors.IsNotFound(err) { + utils.SetOwnerReference(expectSvc, ts) + return r.Client.Create(ctx, expectSvc) + } + return err + } + return r.Client.Patch(ctx, expectSvc, client.Merge) +} + func (r *Reconciler) handleTriggerConfig(ctx context.Context, ts *standardv1alpha1.TriggerService) error { // Add TriggerService into ConfigMap jsonByte, err := json.Marshal(ts.Spec) From e015708dd8a9a7fac904bfbca0fbbe3b0bbdae2a Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:29 +0800 Subject: [PATCH 02/12] Update config/definition/default.yaml Co-authored-by: Tianxin Dong --- config/definition/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index a193e21..8f66b4a 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -182,7 +182,7 @@ spec: hostNames?: [...string] }] createService: *true | bool - service?: { + service: { ports:[...{ name: string port: int From 1672387d53ccb84b34f331e755f0c35e53f27381 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:38 +0800 Subject: [PATCH 03/12] Update config/definition/default.yaml Co-authored-by: Tianxin Dong --- config/definition/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index 8f66b4a..4c2095b 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -188,6 +188,6 @@ spec: port: int targetPort: int|string }] - clusterIp?: string + type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" } } From 1fc1e4003150d310a62fb0c69fb148a217d69a78 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:51 +0800 Subject: [PATCH 04/12] Update config/definition/default.yaml Co-authored-by: Tianxin Dong --- config/definition/default.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index 4c2095b..608005c 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -183,7 +183,15 @@ spec: }] createService: *true | bool service: { - ports:[...{ + ports: *[{ + name: "default" + port: 80 + targetPort: 80 + }] | [...{ + name: string + port: int + targetPort: int + }] name: string port: int targetPort: int|string From d94dbc3aa9ae4e7673620323c6e9054f2ceacbe6 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:51 +0800 Subject: [PATCH 05/12] Update config/definition/default.yaml add unit test Co-authored-by: Tianxin Dong Signed-off-by: Mcduller <1596582524@qq.com> --- config/definition/default.yaml | 35 +-- controllers/triggerservice/suite_test.go | 105 +++++++ .../triggerservice_controller.go | 19 +- .../triggerservice_controller_test.go | 264 ++++++++++++++++++ controllers/utils/test_util.go | 47 ++++ go.mod | 7 +- go.sum | 11 +- 7 files changed, 463 insertions(+), 25 deletions(-) create mode 100644 controllers/triggerservice/suite_test.go create mode 100644 controllers/triggerservice/triggerservice_controller_test.go create mode 100644 controllers/utils/test_util.go diff --git a/config/definition/default.yaml b/config/definition/default.yaml index 4c2095b..6e04b83 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -109,8 +109,8 @@ spec: } } - if parameter.createService { - service:{ + if parameter.createService == true { + service: { apiVersion: "v1" kind: "Service" metadata: { @@ -122,23 +122,14 @@ spec: "app.kubernetes.io/name": parameter.name "trigger.oam.dev/name": triggerService.name } - ports:[ - { - name: "default" - port: 80 - targetPort: 80 - }, - { - for _,v in parameter.service.ports{ + ports:[{ + for _,v in parameter.service.ports { name: v.name port: v.port targetPort: v.targetPort } }] - if parameter.service.clusterIp != _|_ { - clusterIp: parameter.service.clusterIp - } - type: "ClusterIP" + type: parameter.service.type } } } @@ -183,11 +174,15 @@ spec: }] createService: *true | bool service: { - ports:[...{ - name: string - port: int - targetPort: int|string - }] - type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" + ports: *[{ + name: "default" + port: 80 + targetPort: 80 + }] | [...{ + name: string + port: int + targetPort: int + }] + type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" } } diff --git a/controllers/triggerservice/suite_test.go b/controllers/triggerservice/suite_test.go new file mode 100644 index 0000000..0de0180 --- /dev/null +++ b/controllers/triggerservice/suite_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package triggerservice + +import ( + "context" + "path/filepath" + "testing" + + standardv1alpha1 "github.com/kubevela/kube-trigger/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var reconciler *Reconciler +var controllerDone context.CancelFunc +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + useExistingCluster := true + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd"), filepath.Join("..", "..", "config", "definition")}, + ErrorIfCRDPathMissing: true, + Config: ctrl.GetConfigOrDie(), + UseExistingCluster: &useExistingCluster, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = standardv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + reconciler = &Reconciler{ + Client: k8sClient, + Scheme: scheme.Scheme, + } + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + MetricsBindAddress: "0", + LeaderElection: false, + LeaderElectionNamespace: "default", + LeaderElectionID: "test", + }) + var ctx context.Context + ctx, controllerDone = context.WithCancel(context.Background()) + go func() { + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + if controllerDone != nil { + controllerDone() + } + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/controllers/triggerservice/triggerservice_controller.go b/controllers/triggerservice/triggerservice_controller.go index 1403c40..1a12d52 100644 --- a/controllers/triggerservice/triggerservice_controller.go +++ b/controllers/triggerservice/triggerservice_controller.go @@ -209,12 +209,25 @@ func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.Trigg } func (r *Reconciler) createService(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { - data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + needCreateService, err := v.LookupPath(cue.ParsePath("parameter.createService")).Bool() if err != nil { return err } - if data == nil { - return nil + + if !needCreateService { + existSvc := new(corev1.Service) + if err := r.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existSvc); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + return r.Delete(ctx, existSvc) + } + + data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + if err != nil { + return err } expectSvc := new(corev1.Service) diff --git a/controllers/triggerservice/triggerservice_controller_test.go b/controllers/triggerservice/triggerservice_controller_test.go new file mode 100644 index 0000000..9c419c1 --- /dev/null +++ b/controllers/triggerservice/triggerservice_controller_test.go @@ -0,0 +1,264 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package triggerservice + +import ( + "context" + "encoding/json" + "github.com/kubevela/pkg/util/slices" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/kubevela/kube-trigger/api/v1alpha1" + "github.com/kubevela/kube-trigger/controllers/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/yaml" +) + +var _ = Describe("TriggerinstanceController", Ordered, func() { + ctx := context.TODO() + + ts := v1alpha1.TriggerService{} + tsJSON, _ := yaml.YAMLToJSON([]byte(normalTriggerInstance)) + + tsNoService := v1alpha1.TriggerService{} + tsNoServiceJSON, _ := yaml.YAMLToJSON([]byte(normalTriggerServiceWithOutService)) + + BeforeEach(func() { + Expect(utils.InstallDefaultDefinition()).Should(BeNil()) + Expect(json.Unmarshal(tsJSON, &ts)).Should(BeNil()) + Expect(json.Unmarshal(tsNoServiceJSON, &tsNoService)).Should(BeNil()) + }) + + AfterAll(func() { + Expect(k8sClient.Delete(ctx, ts.DeepCopy())).Should(BeNil()) + Expect(k8sClient.Delete(ctx, tsNoService.DeepCopy())).Should(BeNil()) + Expect(utils.UnInstallDefaultDefinition()).Should(BeNil()) + }) + + It("test normal triggerInstance create relevant resource", func() { + tsKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Create(ctx, ts.DeepCopy())).Should(BeNil()) + Expect(utils.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: tsKey})).Should(BeNil()) + + newTs := v1alpha1.TriggerService{} + Expect(k8sClient.Get(ctx, tsKey, &newTs)).Should(BeNil()) + Expect(newTs.Name).Should(Equal("kubetrigger-sample-config-service")) + + cm := corev1.ConfigMap{} + cmKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + + Expect(k8sClient.Get(ctx, cmKey, &cm)).Should(BeNil()) + Expect(len(cm.OwnerReferences)).Should(Equal(1)) + Expect(cm.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(cm.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + clusterRoleBing := rbacv1.ClusterRoleBinding{} + crbKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, crbKey, &clusterRoleBing)).Should(BeNil()) + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: "kube-trigger", + Namespace: ts.Namespace, + } + Expect(clusterRoleBing.Subjects[0].Name).Should(Equal("kube-trigger")) + Expect(clusterRoleBing.Subjects[0].Namespace).Should(Equal("vela-system")) + Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) + + sa := corev1.ServiceAccount{} + saKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, saKey, &sa)).Should(BeNil()) + Expect(len(sa.OwnerReferences)).Should(Equal(1)) + Expect(sa.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(sa.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + deploy := appsv1.Deployment{} + deployKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Get(ctx, deployKey, &deploy)).Should(BeNil()) + Expect(len(deploy.OwnerReferences)).Should(Equal(1)) + Expect(deploy.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(deploy.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + svc := corev1.Service{} + svcKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Get(ctx, svcKey, &svc)).Should(BeNil()) + Expect(len(svc.OwnerReferences)).Should(Equal(1)) + Expect(svc.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(svc.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + }) + + It("test normal triggerInstance create relevant resource without service", func() { + tsNoServiceKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(k8sClient.Create(ctx, tsNoService.DeepCopy())).Should(BeNil()) + Expect(utils.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: tsNoServiceKey})).Should(BeNil()) + + newTs := v1alpha1.TriggerService{} + Expect(k8sClient.Get(ctx, tsNoServiceKey, &newTs)).Should(BeNil()) + Expect(newTs.Name).Should(Equal("kubetrigger-sample-config-no-service")) + + cm := corev1.ConfigMap{} + cmKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + + Expect(k8sClient.Get(ctx, cmKey, &cm)).Should(BeNil()) + Expect(len(cm.OwnerReferences)).Should(Equal(1)) + Expect(cm.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(cm.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + clusterRoleBing := rbacv1.ClusterRoleBinding{} + crbKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, crbKey, &clusterRoleBing)).Should(BeNil()) + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: "kube-trigger", + Namespace: ts.Namespace, + } + Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) + + sa := corev1.ServiceAccount{} + saKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: "no-service", + } + Expect(k8sClient.Get(ctx, saKey, &sa)).Should(BeNil()) + Expect(len(sa.OwnerReferences)).Should(Equal(1)) + Expect(sa.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(sa.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + deploy := appsv1.Deployment{} + deployKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(k8sClient.Get(ctx, deployKey, &deploy)).Should(BeNil()) + Expect(len(deploy.OwnerReferences)).Should(Equal(1)) + Expect(deploy.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(deploy.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + svc := corev1.Service{} + svcKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(apierrors.IsNotFound(k8sClient.Get(ctx, svcKey, &svc))).Should(BeTrue()) + + }) + +}) + +const ( + normalTriggerInstance = ` +apiVersion: standard.oam.dev/v1alpha1 +kind: TriggerService +metadata: + name: kubetrigger-sample-config-service + namespace: default +spec: + # A trigger is a group of Source, Filters, and Actions. + # You can add multiple triggers. + worker: + properties: + createService: true + triggers: + - source: + type: resource-watcher + properties: + # We are interested in ConfigMap events. + apiVersion: "v1" + kind: ConfigMap + namespace: default + # Only watch update event. + events: + - update + filter: | + context: data: metadata: name: =~"this-will-trigger-update-.*" + action: + # Bump Application Revision to update Application. + type: bump-application-revision + properties: + namespace: default + # Select Applications to bump using labels. + nameSelector: + fromLabel: "watch-this" +` + normalTriggerServiceWithOutService = ` +apiVersion: standard.oam.dev/v1alpha1 +kind: TriggerService +metadata: + name: kubetrigger-sample-config-no-service + namespace: default +spec: + # A trigger is a group of Source, Filters, and Actions. + # You can add multiple triggers. + worker: + properties: + createService: false + serviceAccount: no-service + triggers: + - source: + type: resource-watcher + properties: + # We are interested in ConfigMap events. + apiVersion: "v1" + kind: ConfigMap + namespace: default + # Only watch update event. + events: + - update + filter: | + context: data: metadata: name: =~"this-will-trigger-update-.*" + action: + # Bump Application Revision to update Application. + type: bump-application-revision + properties: + namespace: default + # Select Applications to bump using labels. + namSelector: + fromLabel: "watch-this" +` +) diff --git a/controllers/utils/test_util.go b/controllers/utils/test_util.go new file mode 100644 index 0000000..1a196a9 --- /dev/null +++ b/controllers/utils/test_util.go @@ -0,0 +1,47 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "context" + "os/exec" + "path/filepath" + + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// ReconcileOnce will just reconcile once. +func ReconcileOnce(r reconcile.Reconciler, req reconcile.Request) error { + if _, err := r.Reconcile(context.TODO(), req); err != nil { + return err + } + return nil +} + +// InstallDefaultDefinition install the default template +func InstallDefaultDefinition() error { + defaultDefinitionPath := filepath.Join("..", "..", "config", "definition", "default.yaml") + cmd := exec.Command("kubectl", "apply", "-f", defaultDefinitionPath) + return cmd.Run() +} + +// UnInstallDefaultDefinition uninstall the default template +func UnInstallDefaultDefinition() error { + defaultDefinitionPath := filepath.Join("..", "..", "config", "definition", "default.yaml") + cmd := exec.Command("kubectl", "delete", "-f", defaultDefinitionPath) + return cmd.Run() +} diff --git a/go.mod b/go.mod index 867e2e0..35a04f5 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/google/go-cmp v0.5.9 github.com/kubevela/pkg v1.8.1-0.20230411071527-ac5fa22727f7 github.com/mitchellh/hashstructure/v2 v2.0.2 + github.com/onsi/ginkgo/v2 v2.9.2 + github.com/onsi/gomega v1.27.5 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.0 @@ -21,6 +23,7 @@ require ( k8s.io/klog/v2 v2.80.1 k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/yaml v1.3.0 ) require ( @@ -46,12 +49,14 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.19.14 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/cel-go v0.12.6 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/uuid v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect @@ -97,6 +102,7 @@ require ( golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 // indirect @@ -118,7 +124,6 @@ require ( sigs.k8s.io/apiserver-runtime v1.1.2-0.20221118041430-0a6394f6dda3 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( diff --git a/go.sum b/go.sum index f49d6b8..53993c7 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,9 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -109,6 +112,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -159,6 +163,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -173,6 +178,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= @@ -233,9 +239,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oam-dev/cluster-gateway v1.9.0-alpha.1 h1:1V9nm8XO4ZqJK6rkJ7c1kglQN8biQtrFiHaJnAtjjLE= github.com/oam-dev/cluster-gateway v1.9.0-alpha.1/go.mod h1:g8ivuGBu1MwtvM0AKGc81TvsLvEsosoUXENhbBFJgtc= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= +github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/openshift/library-go v0.0.0-20230327085348-8477ec72b725 h1:GC0oekPo2BDqK+2Mv6W/VuvkaUUMFcmqp0AZDN2vWrA= github.com/openshift/library-go v0.0.0-20230327085348-8477ec72b725/go.mod h1:OspkL5FZZapzNcka6UkNMFD7ifLT/dWUNvtwErpRK9k= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -417,6 +424,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -467,6 +475,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From ed3b9a63b1900dbe8ae570782d3a50401b986ba0 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Fri, 19 May 2023 12:55:27 +0530 Subject: [PATCH 06/12] Fix: example manager and instructions (#48) * Feat: upgrade k8s.io dependency to 0.26 (#39) * Feat: upgrade k8s.io dependency to 0.26 Signed-off-by: Yin Da * Fix: golangci-lint action failure Signed-off-by: Yin Da --------- Signed-off-by: Yin Da Signed-off-by: Amit Singh * Feat: add cluster info to action context (#43) Feat: add cluster info in action context Signed-off-by: yangsoon Co-authored-by: yangsoon Signed-off-by: Amit Singh * Feat: add cronjob source (#42) * Refactor: allow the use of singleton for one Source, while also allowing non-singleton Sources, making it more extensible Signed-off-by: Charlie Chiang * Feat: add cronjob source Signed-off-by: Charlie Chiang * Chore: update cron lib Signed-off-by: Charlie Chiang * Feat: do not exit if config contains invalid sources Signed-off-by: Charlie Chiang * Chore: add tests and fix linter Signed-off-by: Charlie Chiang * Chore: go mod tidy Signed-off-by: Charlie Chiang * Docs: add cronjob example Signed-off-by: Charlie Chiang * fix comments Signed-off-by: Charlie Chiang * Chore: force lint to use the exact version Signed-off-by: Charlie Chiang * remove unnecessary dep Signed-off-by: Charlie Chiang * fix comments Signed-off-by: Charlie Chiang * use codecov token Signed-off-by: Charlie Chiang * update lint script Signed-off-by: Charlie Chiang --------- Signed-off-by: Charlie Chiang Signed-off-by: Amit Singh * Feat: support leader election for kube-trigger (#45) Signed-off-by: yangsoon Co-authored-by: yangsoon Signed-off-by: Amit Singh * adds instruction to enable kube-trigger addon Signed-off-by: Amit Singh * updates sample.yaml path Signed-off-by: Amit Singh * fix: updates instructions to apply config files in order Signed-off-by: Amit Singh * fix: adds the missing api group to the first rule Signed-off-by: Amit Singh * Revert "Feat: support leader election for kube-trigger (#45)" This reverts commit 4351008607f9f311cb43f1132292cfa42746d57a. Removing changes unrelated to this branch Signed-off-by: Amit Singh * Revert "Feat: add cronjob source (#42)" This reverts commit 46f5975b64e9c3e412e7d3e5564b08d111814ce5. Removes changes unrelated to the branch Signed-off-by: Amit Singh * Revert "Feat: add cluster info to action context (#43)" This reverts commit e214be5be37d60222170eb416c3234135ef994bf. Removes changes unrelated to the branch Signed-off-by: Amit Singh * Revert "Feat: upgrade k8s.io dependency to 0.26 (#39)" This reverts commit a22e31611efdf53ad8029e254dfea48ba0cf32ac. Removes changes unrelated to the branch Signed-off-by: Amit Singh --------- Signed-off-by: Yin Da Signed-off-by: Amit Singh Signed-off-by: yangsoon Signed-off-by: Charlie Chiang Co-authored-by: Somefive Co-authored-by: yangs Co-authored-by: yangsoon Co-authored-by: Charlie Chiang --- config/manager/role.yaml | 4 +++- examples/README.md | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/config/manager/role.yaml b/config/manager/role.yaml index 675adf7..031d7d0 100644 --- a/config/manager/role.yaml +++ b/config/manager/role.yaml @@ -5,7 +5,9 @@ metadata: creationTimestamp: null name: kube-trigger-manager-role rules: -- resources: +- apiGroups: + - "" + resources: - configmaps verbs: - get diff --git a/examples/README.md b/examples/README.md index a6e9eb0..35be2d1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,6 +7,10 @@ updated whenever the ConfigMaps that are referenced by `ref-objects` are updated ## Prerequisites - Install [KubeVela](https://kubevela.net/docs/install) in your cluster +- Enable the `kube-trigger` addon +```shell +vela addon enable kube-trigger +``` ## What we want to achieve? @@ -26,7 +30,7 @@ Apply `sample.yaml` to create 2 Applications and 2 ConfigMaps in the default nam trigger 2 Application updates. ```shell -kubectl apply sample.yaml +kubectl apply -f examples/sample.yaml ``` 2. **Run kube-trigger** @@ -43,7 +47,9 @@ Standalone: In-Cluster: ```shell -kubectl apply -f config/ +kubectl apply -f config/crd/ +kubectl apply -f config/definition/ +kubectl apply -f config/manager/ ``` 3. **Watch ApplicationRevision changes** so that you can see what it does. From 40dc64fb6d1a500a20aedd787c7392232d32865e Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Sat, 20 May 2023 16:06:44 +0800 Subject: [PATCH 07/12] fix unit test Signed-off-by: Mcduller <1596582524@qq.com> --- controllers/triggerservice/suite_test.go | 2 +- .../triggerservice_controller_test.go | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/controllers/triggerservice/suite_test.go b/controllers/triggerservice/suite_test.go index 053443c..875b763 100644 --- a/controllers/triggerservice/suite_test.go +++ b/controllers/triggerservice/suite_test.go @@ -51,7 +51,7 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - useExistingCluster := true + useExistingCluster := false By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd")}, diff --git a/controllers/triggerservice/triggerservice_controller_test.go b/controllers/triggerservice/triggerservice_controller_test.go index 45a2d01..62efd19 100644 --- a/controllers/triggerservice/triggerservice_controller_test.go +++ b/controllers/triggerservice/triggerservice_controller_test.go @@ -51,8 +51,20 @@ var _ = Describe("TriggerinstanceController", Ordered, func() { }, } + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-trigger", + }, + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: "cluster-admin", + APIGroup: "rbac.authorization.k8s.io", + }, + } + BeforeEach(func() { Expect(k8sClient.Create(ctx, ns.DeepCopy())).Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) + Expect(k8sClient.Create(ctx, clusterRoleBinding.DeepCopy())).Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) for _, file := range []string{"bump-application-revision", "create-event-listener", "default", "patch-resource", "record-event"} { Expect(utils.InstallDefinition(ctx, k8sClient, filepath.Join("../../config/definition", file+".yaml"))). Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) @@ -106,8 +118,6 @@ var _ = Describe("TriggerinstanceController", Ordered, func() { Name: "kube-trigger", Namespace: ts.Namespace, } - Expect(clusterRoleBing.Subjects[0].Name).Should(Equal("kube-trigger")) - Expect(clusterRoleBing.Subjects[0].Namespace).Should(Equal("vela-system")) Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) sa := corev1.ServiceAccount{} From 64956ef573ad89058dee008930a084f034a10a8c Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 12:58:57 +0800 Subject: [PATCH 08/12] Test:support create service Signed-off-by: Mcduller <1596582524@qq.com> --- config/definition/default.yaml | 43 +++++++++ .../triggerservice_controller.go | 96 ++++++++++++++----- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index d42ef6b..a193e21 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -109,6 +109,40 @@ spec: } } + if parameter.createService { + service:{ + apiVersion: "v1" + kind: "Service" + metadata: { + name: parameter.name + namespace: triggerService.namespace + } + spec:{ + selector:{ + "app.kubernetes.io/name": parameter.name + "trigger.oam.dev/name": triggerService.name + } + ports:[ + { + name: "default" + port: 80 + targetPort: 80 + }, + { + for _,v in parameter.service.ports{ + name: v.name + port: v.port + targetPort: v.targetPort + } + }] + if parameter.service.clusterIp != _|_ { + clusterIp: parameter.service.clusterIp + } + type: "ClusterIP" + } + } + } + triggerService: { name: string namespace: *"vela-system" | string @@ -147,4 +181,13 @@ spec: ip?: string hostNames?: [...string] }] + createService: *true | bool + service?: { + ports:[...{ + name: string + port: int + targetPort: int|string + }] + clusterIp?: string + } } diff --git a/controllers/triggerservice/triggerservice_controller.go b/controllers/triggerservice/triggerservice_controller.go index 8749eec..1403c40 100644 --- a/controllers/triggerservice/triggerservice_controller.go +++ b/controllers/triggerservice/triggerservice_controller.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "cuelang.org/go/cue" "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -35,6 +34,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "cuelang.org/go/cue" + "github.com/kubevela/pkg/cue/cuex" "github.com/kubevela/pkg/util/slices" "github.com/kubevela/pkg/util/template/definition" @@ -91,25 +92,36 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu return ctrl.Result{}, err } - deploymentList := &appsv1.DeploymentList{} - if err := r.List(ctx, deploymentList, client.MatchingLabels(map[string]string{ - triggerNameLabel: ts.Name, - })); err != nil { + if err := r.handleWorker(ctx, ts); err != nil { + logger.Error(err, "failed to handle trigger worker") return ctrl.Result{}, err } - // if no worker deployment exists, create a new one to run trigger - if len(deploymentList.Items) == 0 { - if err := r.createWorker(ctx, ts); err != nil { - logger.Error(err, "failed to create worker deployment for trigger") - return ctrl.Result{}, err - } - logger.Info("successfully create worker deployment for trigger") + return ctrl.Result{}, nil +} + +func (r *Reconciler) handleWorker(ctx context.Context, ts *standardv1alpha1.TriggerService) error { + + v, err := r.loadTemplateCueValue(ctx, ts) + if err != nil { + return err } - return ctrl.Result{}, nil + if err := r.createRoles(ctx, ts, v); err != nil { + return err + } + + if err := r.createDeployment(ctx, ts, v); err != nil { + return err + } + + if err := r.createService(ctx, ts, v); err != nil { + return err + } + + return nil } -func (r *Reconciler) createWorker(ctx context.Context, ts *standardv1alpha1.TriggerService) error { +func (r *Reconciler) loadTemplateCueValue(ctx context.Context, ts *standardv1alpha1.TriggerService) (cue.Value, error) { templateName := "default" opts := make([]cuex.CompileOption, 0) opts = append(opts, cuex.WithExtraData("triggerService", map[string]string{ @@ -124,25 +136,38 @@ func (r *Reconciler) createWorker(ctx context.Context, ts *standardv1alpha1.Trig } template, err := definition.NewTemplateLoader(ctx, r.Client).LoadTemplate(ctx, templateName, definition.WithType(triggertypes.DefinitionTypeTriggerWorker)) if err != nil { - return err + return cue.Value{}, err } + v, err := cuex.CompileStringWithOptions(ctx, template.Compile(), opts...) if err != nil { - return err - } - if err := r.createRoles(ctx, ts, v); err != nil { - return err + return cue.Value{}, err } + + return v, nil +} + +func (r *Reconciler) createDeployment(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { data, err := v.LookupPath(cue.ParsePath("deployment")).MarshalJSON() if err != nil { return err } - deploy := &appsv1.Deployment{} - if err := json.Unmarshal(data, deploy); err != nil { + + expectedDeployment := new(appsv1.Deployment) + if err := json.Unmarshal(data, expectedDeployment); err != nil { return err } - utils.SetOwnerReference(deploy, ts) - return r.Create(ctx, deploy) + + existDeployment := new(appsv1.Deployment) + if err := r.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existDeployment); err != nil { + if apierrors.IsNotFound(err) { + utils.SetOwnerReference(expectedDeployment, ts) + return r.Create(ctx, expectedDeployment) + } + return err + } + + return r.Patch(ctx, expectedDeployment, client.Merge) } func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { @@ -183,6 +208,31 @@ func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.Trigg return nil } +func (r *Reconciler) createService(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { + data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + if err != nil { + return err + } + if data == nil { + return nil + } + + expectSvc := new(corev1.Service) + if err := json.Unmarshal(data, expectSvc); err != nil { + return err + } + + existSvc := new(corev1.Service) + if err := r.Client.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existSvc); err != nil { + if apierrors.IsNotFound(err) { + utils.SetOwnerReference(expectSvc, ts) + return r.Client.Create(ctx, expectSvc) + } + return err + } + return r.Client.Patch(ctx, expectSvc, client.Merge) +} + func (r *Reconciler) handleTriggerConfig(ctx context.Context, ts *standardv1alpha1.TriggerService) error { // Add TriggerService into ConfigMap jsonByte, err := json.Marshal(ts.Spec) From 2889aabf5d121d90c05576a1e51b5cedfc72f2a9 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:29 +0800 Subject: [PATCH 09/12] Update config/definition/default.yaml Co-authored-by: Tianxin Dong --- config/definition/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index a193e21..8f66b4a 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -182,7 +182,7 @@ spec: hostNames?: [...string] }] createService: *true | bool - service?: { + service: { ports:[...{ name: string port: int From dddc1e518ddb257002239dd652cfb9419e884e65 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:38 +0800 Subject: [PATCH 10/12] Update config/definition/default.yaml Co-authored-by: Tianxin Dong --- config/definition/default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/definition/default.yaml b/config/definition/default.yaml index 8f66b4a..4c2095b 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -188,6 +188,6 @@ spec: port: int targetPort: int|string }] - clusterIp?: string + type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" } } From 25c23fd14a3f15991c4ce17c87781f6dc5407e8c Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Thu, 20 Apr 2023 16:09:51 +0800 Subject: [PATCH 11/12] Update config/definition/default.yaml add unit test Co-authored-by: Tianxin Dong Signed-off-by: Mcduller <1596582524@qq.com> --- config/definition/default.yaml | 35 +-- controllers/triggerservice/suite_test.go | 105 +++++++ .../triggerservice_controller.go | 19 +- .../triggerservice_controller_test.go | 264 ++++++++++++++++++ controllers/utils/test_util.go | 47 ++++ go.mod | 7 +- go.sum | 11 +- 7 files changed, 463 insertions(+), 25 deletions(-) create mode 100644 controllers/triggerservice/suite_test.go create mode 100644 controllers/triggerservice/triggerservice_controller_test.go create mode 100644 controllers/utils/test_util.go diff --git a/config/definition/default.yaml b/config/definition/default.yaml index 4c2095b..6e04b83 100644 --- a/config/definition/default.yaml +++ b/config/definition/default.yaml @@ -109,8 +109,8 @@ spec: } } - if parameter.createService { - service:{ + if parameter.createService == true { + service: { apiVersion: "v1" kind: "Service" metadata: { @@ -122,23 +122,14 @@ spec: "app.kubernetes.io/name": parameter.name "trigger.oam.dev/name": triggerService.name } - ports:[ - { - name: "default" - port: 80 - targetPort: 80 - }, - { - for _,v in parameter.service.ports{ + ports:[{ + for _,v in parameter.service.ports { name: v.name port: v.port targetPort: v.targetPort } }] - if parameter.service.clusterIp != _|_ { - clusterIp: parameter.service.clusterIp - } - type: "ClusterIP" + type: parameter.service.type } } } @@ -183,11 +174,15 @@ spec: }] createService: *true | bool service: { - ports:[...{ - name: string - port: int - targetPort: int|string - }] - type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" + ports: *[{ + name: "default" + port: 80 + targetPort: 80 + }] | [...{ + name: string + port: int + targetPort: int + }] + type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" } } diff --git a/controllers/triggerservice/suite_test.go b/controllers/triggerservice/suite_test.go new file mode 100644 index 0000000..0de0180 --- /dev/null +++ b/controllers/triggerservice/suite_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package triggerservice + +import ( + "context" + "path/filepath" + "testing" + + standardv1alpha1 "github.com/kubevela/kube-trigger/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var reconciler *Reconciler +var controllerDone context.CancelFunc +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + useExistingCluster := true + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd"), filepath.Join("..", "..", "config", "definition")}, + ErrorIfCRDPathMissing: true, + Config: ctrl.GetConfigOrDie(), + UseExistingCluster: &useExistingCluster, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = standardv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + reconciler = &Reconciler{ + Client: k8sClient, + Scheme: scheme.Scheme, + } + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + MetricsBindAddress: "0", + LeaderElection: false, + LeaderElectionNamespace: "default", + LeaderElectionID: "test", + }) + var ctx context.Context + ctx, controllerDone = context.WithCancel(context.Background()) + go func() { + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + if controllerDone != nil { + controllerDone() + } + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/controllers/triggerservice/triggerservice_controller.go b/controllers/triggerservice/triggerservice_controller.go index 1403c40..1a12d52 100644 --- a/controllers/triggerservice/triggerservice_controller.go +++ b/controllers/triggerservice/triggerservice_controller.go @@ -209,12 +209,25 @@ func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.Trigg } func (r *Reconciler) createService(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error { - data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + needCreateService, err := v.LookupPath(cue.ParsePath("parameter.createService")).Bool() if err != nil { return err } - if data == nil { - return nil + + if !needCreateService { + existSvc := new(corev1.Service) + if err := r.Get(ctx, types.NamespacedName{Namespace: ts.Namespace, Name: ts.Name}, existSvc); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + return r.Delete(ctx, existSvc) + } + + data, err := v.LookupPath(cue.ParsePath("service")).MarshalJSON() + if err != nil { + return err } expectSvc := new(corev1.Service) diff --git a/controllers/triggerservice/triggerservice_controller_test.go b/controllers/triggerservice/triggerservice_controller_test.go new file mode 100644 index 0000000..9c419c1 --- /dev/null +++ b/controllers/triggerservice/triggerservice_controller_test.go @@ -0,0 +1,264 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package triggerservice + +import ( + "context" + "encoding/json" + "github.com/kubevela/pkg/util/slices" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/kubevela/kube-trigger/api/v1alpha1" + "github.com/kubevela/kube-trigger/controllers/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/yaml" +) + +var _ = Describe("TriggerinstanceController", Ordered, func() { + ctx := context.TODO() + + ts := v1alpha1.TriggerService{} + tsJSON, _ := yaml.YAMLToJSON([]byte(normalTriggerInstance)) + + tsNoService := v1alpha1.TriggerService{} + tsNoServiceJSON, _ := yaml.YAMLToJSON([]byte(normalTriggerServiceWithOutService)) + + BeforeEach(func() { + Expect(utils.InstallDefaultDefinition()).Should(BeNil()) + Expect(json.Unmarshal(tsJSON, &ts)).Should(BeNil()) + Expect(json.Unmarshal(tsNoServiceJSON, &tsNoService)).Should(BeNil()) + }) + + AfterAll(func() { + Expect(k8sClient.Delete(ctx, ts.DeepCopy())).Should(BeNil()) + Expect(k8sClient.Delete(ctx, tsNoService.DeepCopy())).Should(BeNil()) + Expect(utils.UnInstallDefaultDefinition()).Should(BeNil()) + }) + + It("test normal triggerInstance create relevant resource", func() { + tsKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Create(ctx, ts.DeepCopy())).Should(BeNil()) + Expect(utils.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: tsKey})).Should(BeNil()) + + newTs := v1alpha1.TriggerService{} + Expect(k8sClient.Get(ctx, tsKey, &newTs)).Should(BeNil()) + Expect(newTs.Name).Should(Equal("kubetrigger-sample-config-service")) + + cm := corev1.ConfigMap{} + cmKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + + Expect(k8sClient.Get(ctx, cmKey, &cm)).Should(BeNil()) + Expect(len(cm.OwnerReferences)).Should(Equal(1)) + Expect(cm.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(cm.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + clusterRoleBing := rbacv1.ClusterRoleBinding{} + crbKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, crbKey, &clusterRoleBing)).Should(BeNil()) + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: "kube-trigger", + Namespace: ts.Namespace, + } + Expect(clusterRoleBing.Subjects[0].Name).Should(Equal("kube-trigger")) + Expect(clusterRoleBing.Subjects[0].Namespace).Should(Equal("vela-system")) + Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) + + sa := corev1.ServiceAccount{} + saKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, saKey, &sa)).Should(BeNil()) + Expect(len(sa.OwnerReferences)).Should(Equal(1)) + Expect(sa.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(sa.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + deploy := appsv1.Deployment{} + deployKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Get(ctx, deployKey, &deploy)).Should(BeNil()) + Expect(len(deploy.OwnerReferences)).Should(Equal(1)) + Expect(deploy.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(deploy.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + svc := corev1.Service{} + svcKey := client.ObjectKey{ + Namespace: ts.Namespace, + Name: ts.Name, + } + Expect(k8sClient.Get(ctx, svcKey, &svc)).Should(BeNil()) + Expect(len(svc.OwnerReferences)).Should(Equal(1)) + Expect(svc.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(svc.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + }) + + It("test normal triggerInstance create relevant resource without service", func() { + tsNoServiceKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(k8sClient.Create(ctx, tsNoService.DeepCopy())).Should(BeNil()) + Expect(utils.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: tsNoServiceKey})).Should(BeNil()) + + newTs := v1alpha1.TriggerService{} + Expect(k8sClient.Get(ctx, tsNoServiceKey, &newTs)).Should(BeNil()) + Expect(newTs.Name).Should(Equal("kubetrigger-sample-config-no-service")) + + cm := corev1.ConfigMap{} + cmKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + + Expect(k8sClient.Get(ctx, cmKey, &cm)).Should(BeNil()) + Expect(len(cm.OwnerReferences)).Should(Equal(1)) + Expect(cm.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(cm.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + clusterRoleBing := rbacv1.ClusterRoleBinding{} + crbKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: "kube-trigger", + } + Expect(k8sClient.Get(ctx, crbKey, &clusterRoleBing)).Should(BeNil()) + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: "kube-trigger", + Namespace: ts.Namespace, + } + Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) + + sa := corev1.ServiceAccount{} + saKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: "no-service", + } + Expect(k8sClient.Get(ctx, saKey, &sa)).Should(BeNil()) + Expect(len(sa.OwnerReferences)).Should(Equal(1)) + Expect(sa.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(sa.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + deploy := appsv1.Deployment{} + deployKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(k8sClient.Get(ctx, deployKey, &deploy)).Should(BeNil()) + Expect(len(deploy.OwnerReferences)).Should(Equal(1)) + Expect(deploy.OwnerReferences[0].Name).Should(Equal(newTs.Name)) + Expect(deploy.OwnerReferences[0].UID).Should(Equal(newTs.UID)) + + svc := corev1.Service{} + svcKey := client.ObjectKey{ + Namespace: tsNoService.Namespace, + Name: tsNoService.Name, + } + Expect(apierrors.IsNotFound(k8sClient.Get(ctx, svcKey, &svc))).Should(BeTrue()) + + }) + +}) + +const ( + normalTriggerInstance = ` +apiVersion: standard.oam.dev/v1alpha1 +kind: TriggerService +metadata: + name: kubetrigger-sample-config-service + namespace: default +spec: + # A trigger is a group of Source, Filters, and Actions. + # You can add multiple triggers. + worker: + properties: + createService: true + triggers: + - source: + type: resource-watcher + properties: + # We are interested in ConfigMap events. + apiVersion: "v1" + kind: ConfigMap + namespace: default + # Only watch update event. + events: + - update + filter: | + context: data: metadata: name: =~"this-will-trigger-update-.*" + action: + # Bump Application Revision to update Application. + type: bump-application-revision + properties: + namespace: default + # Select Applications to bump using labels. + nameSelector: + fromLabel: "watch-this" +` + normalTriggerServiceWithOutService = ` +apiVersion: standard.oam.dev/v1alpha1 +kind: TriggerService +metadata: + name: kubetrigger-sample-config-no-service + namespace: default +spec: + # A trigger is a group of Source, Filters, and Actions. + # You can add multiple triggers. + worker: + properties: + createService: false + serviceAccount: no-service + triggers: + - source: + type: resource-watcher + properties: + # We are interested in ConfigMap events. + apiVersion: "v1" + kind: ConfigMap + namespace: default + # Only watch update event. + events: + - update + filter: | + context: data: metadata: name: =~"this-will-trigger-update-.*" + action: + # Bump Application Revision to update Application. + type: bump-application-revision + properties: + namespace: default + # Select Applications to bump using labels. + namSelector: + fromLabel: "watch-this" +` +) diff --git a/controllers/utils/test_util.go b/controllers/utils/test_util.go new file mode 100644 index 0000000..1a196a9 --- /dev/null +++ b/controllers/utils/test_util.go @@ -0,0 +1,47 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "context" + "os/exec" + "path/filepath" + + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// ReconcileOnce will just reconcile once. +func ReconcileOnce(r reconcile.Reconciler, req reconcile.Request) error { + if _, err := r.Reconcile(context.TODO(), req); err != nil { + return err + } + return nil +} + +// InstallDefaultDefinition install the default template +func InstallDefaultDefinition() error { + defaultDefinitionPath := filepath.Join("..", "..", "config", "definition", "default.yaml") + cmd := exec.Command("kubectl", "apply", "-f", defaultDefinitionPath) + return cmd.Run() +} + +// UnInstallDefaultDefinition uninstall the default template +func UnInstallDefaultDefinition() error { + defaultDefinitionPath := filepath.Join("..", "..", "config", "definition", "default.yaml") + cmd := exec.Command("kubectl", "delete", "-f", defaultDefinitionPath) + return cmd.Run() +} diff --git a/go.mod b/go.mod index 867e2e0..35a04f5 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/google/go-cmp v0.5.9 github.com/kubevela/pkg v1.8.1-0.20230411071527-ac5fa22727f7 github.com/mitchellh/hashstructure/v2 v2.0.2 + github.com/onsi/ginkgo/v2 v2.9.2 + github.com/onsi/gomega v1.27.5 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.0 @@ -21,6 +23,7 @@ require ( k8s.io/klog/v2 v2.80.1 k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/yaml v1.3.0 ) require ( @@ -46,12 +49,14 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.19.14 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/cel-go v0.12.6 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/uuid v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect @@ -97,6 +102,7 @@ require ( golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 // indirect @@ -118,7 +124,6 @@ require ( sigs.k8s.io/apiserver-runtime v1.1.2-0.20221118041430-0a6394f6dda3 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( diff --git a/go.sum b/go.sum index f49d6b8..53993c7 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,9 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -109,6 +112,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -159,6 +163,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -173,6 +178,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= @@ -233,9 +239,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oam-dev/cluster-gateway v1.9.0-alpha.1 h1:1V9nm8XO4ZqJK6rkJ7c1kglQN8biQtrFiHaJnAtjjLE= github.com/oam-dev/cluster-gateway v1.9.0-alpha.1/go.mod h1:g8ivuGBu1MwtvM0AKGc81TvsLvEsosoUXENhbBFJgtc= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= +github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/openshift/library-go v0.0.0-20230327085348-8477ec72b725 h1:GC0oekPo2BDqK+2Mv6W/VuvkaUUMFcmqp0AZDN2vWrA= github.com/openshift/library-go v0.0.0-20230327085348-8477ec72b725/go.mod h1:OspkL5FZZapzNcka6UkNMFD7ifLT/dWUNvtwErpRK9k= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -417,6 +424,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -467,6 +475,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 729aaa3d3d98f9e1321791b8fd501d7127bfb9a4 Mon Sep 17 00:00:00 2001 From: Mcduller <1596582524@qq.com> Date: Sat, 20 May 2023 16:06:44 +0800 Subject: [PATCH 12/12] fix unit test Signed-off-by: Mcduller <1596582524@qq.com> --- controllers/triggerservice/suite_test.go | 2 +- .../triggerservice_controller_test.go | 39 ++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/controllers/triggerservice/suite_test.go b/controllers/triggerservice/suite_test.go index 0de0180..7be1c77 100644 --- a/controllers/triggerservice/suite_test.go +++ b/controllers/triggerservice/suite_test.go @@ -51,7 +51,7 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - useExistingCluster := true + useExistingCluster := false By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd"), filepath.Join("..", "..", "config", "definition")}, diff --git a/controllers/triggerservice/triggerservice_controller_test.go b/controllers/triggerservice/triggerservice_controller_test.go index 9c419c1..62efd19 100644 --- a/controllers/triggerservice/triggerservice_controller_test.go +++ b/controllers/triggerservice/triggerservice_controller_test.go @@ -21,6 +21,8 @@ import ( "encoding/json" "github.com/kubevela/pkg/util/slices" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "path/filepath" "github.com/kubevela/kube-trigger/api/v1alpha1" "github.com/kubevela/kube-trigger/controllers/utils" @@ -43,16 +45,43 @@ var _ = Describe("TriggerinstanceController", Ordered, func() { tsNoService := v1alpha1.TriggerService{} tsNoServiceJSON, _ := yaml.YAMLToJSON([]byte(normalTriggerServiceWithOutService)) + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vela-system", + }, + } + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-trigger", + }, + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: "cluster-admin", + APIGroup: "rbac.authorization.k8s.io", + }, + } + BeforeEach(func() { - Expect(utils.InstallDefaultDefinition()).Should(BeNil()) + Expect(k8sClient.Create(ctx, ns.DeepCopy())).Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) + Expect(k8sClient.Create(ctx, clusterRoleBinding.DeepCopy())).Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) + for _, file := range []string{"bump-application-revision", "create-event-listener", "default", "patch-resource", "record-event"} { + Expect(utils.InstallDefinition(ctx, k8sClient, filepath.Join("../../config/definition", file+".yaml"))). + Should(SatisfyAny(Succeed(), &utils.AlreadyExistMatcher{})) + } Expect(json.Unmarshal(tsJSON, &ts)).Should(BeNil()) Expect(json.Unmarshal(tsNoServiceJSON, &tsNoService)).Should(BeNil()) }) AfterAll(func() { - Expect(k8sClient.Delete(ctx, ts.DeepCopy())).Should(BeNil()) - Expect(k8sClient.Delete(ctx, tsNoService.DeepCopy())).Should(BeNil()) - Expect(utils.UnInstallDefaultDefinition()).Should(BeNil()) + Expect(k8sClient.Delete(ctx, ts.DeepCopy())). + Should(SatisfyAny(Succeed(), &utils.NotFoundMatcher{})) + Expect(k8sClient.Delete(ctx, tsNoService.DeepCopy())). + Should(SatisfyAny(Succeed(), &utils.NotFoundMatcher{})) + for _, file := range []string{"bump-application-revision", "create-event-listener", "default", "patch-resource", "record-event"} { + Expect(utils.UnInstallDefinition(ctx, k8sClient, filepath.Join("../../config/definition", file+".yaml"))). + Should(SatisfyAny(Succeed(), &utils.NotFoundMatcher{})) + } }) It("test normal triggerInstance create relevant resource", func() { @@ -89,8 +118,6 @@ var _ = Describe("TriggerinstanceController", Ordered, func() { Name: "kube-trigger", Namespace: ts.Namespace, } - Expect(clusterRoleBing.Subjects[0].Name).Should(Equal("kube-trigger")) - Expect(clusterRoleBing.Subjects[0].Namespace).Should(Equal("vela-system")) Expect(slices.Contains(clusterRoleBing.Subjects, subject)).Should(BeTrue()) sa := corev1.ServiceAccount{}