Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat :support create service #46

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9775e3f
Test:support create service
Mcduller Apr 20, 2023
e015708
Update config/definition/default.yaml
Mcduller Apr 20, 2023
1672387
Update config/definition/default.yaml
Mcduller Apr 20, 2023
1fc1e40
Update config/definition/default.yaml
Mcduller Apr 20, 2023
d94dbc3
Update config/definition/default.yaml
Mcduller Apr 20, 2023
5dbf380
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller May 9, 2023
ed3b9a6
Fix: example manager and instructions (#48)
semmet95 May 19, 2023
d7f75b1
fix unit test
Mcduller May 9, 2023
cc9781b
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller May 19, 2023
c90ef38
fix unit test
Mcduller May 19, 2023
6b882f6
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller May 20, 2023
40dc64f
fix unit test
Mcduller May 20, 2023
64956ef
Test:support create service
Mcduller Apr 20, 2023
2889aab
Update config/definition/default.yaml
Mcduller Apr 20, 2023
dddc1e5
Update config/definition/default.yaml
Mcduller Apr 20, 2023
25c23fd
Update config/definition/default.yaml
Mcduller Apr 20, 2023
729aaa3
fix unit test
Mcduller May 20, 2023
4e33ba3
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller Jun 18, 2023
aea9af9
fix unit test
Mcduller Jun 18, 2023
919a3cb
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller Jun 18, 2023
e2eeb4d
add unit test restart pod
Mcduller Jun 18, 2023
5d0c438
Merge remote-tracking branch 'origin/create_svc' into create_svc
Mcduller Jun 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions config/definition/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,31 @@ spec:
}
}

if parameter.createService == true {
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:[{
for _,v in parameter.service.ports {
name: v.name
port: v.port
targetPort: v.targetPort
}
}]
type: parameter.service.type
}
}
}

triggerService: {
name: string
namespace: *"vela-system" | string
Expand Down Expand Up @@ -147,4 +172,17 @@ spec:
ip?: string
hostNames?: [...string]
}]
createService: *true | bool
service: {
ports: *[{
name: "default"
port: 80
targetPort: 80
}] | [...{
name: string
port: int
targetPort: int
}]
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
}
}
4 changes: 3 additions & 1 deletion config/manager/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ metadata:
creationTimestamp: null
name: kube-trigger-manager-role
rules:
- resources:
- apiGroups:
- ""
resources:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change caused by our scripts? /cc @charlie0129

- configmaps
verbs:
- get
Expand Down
105 changes: 105 additions & 0 deletions controllers/triggerservice/suite_test.go
Original file line number Diff line number Diff line change
@@ -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 := false
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())
})
109 changes: 86 additions & 23 deletions controllers/triggerservice/triggerservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check createService first, if the value is true, then create the service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean check before invoking the function? or we can check the value in the function ?

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{
Expand All @@ -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
}

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
}
utils.SetOwnerReference(deploy, ts)
return r.Create(ctx, deploy)

return r.Patch(ctx, expectedDeployment, client.Merge)
}

func (r *Reconciler) createRoles(ctx context.Context, ts *standardv1alpha1.TriggerService, v cue.Value) error {
Expand Down Expand Up @@ -183,6 +208,44 @@ 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 {
needCreateService, err := v.LookupPath(cue.ParsePath("parameter.createService")).Bool()
if err != nil {
return err
}

if !needCreateService {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the needCreateService is false, then we'll try to delete the existing service? I think it is a dangerous behavior and we shouldn't clean the service for user automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add another flag to clean the service when user want to clean service?

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)
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)
Expand Down
Loading