From 8d5628b810d1c80e5e9767e08a9dd50c64615048 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Tue, 3 Jan 2023 14:50:49 +0100 Subject: [PATCH 1/6] poc of application discovery (WIP) Signed-off-by: Florian Bacher --- operator/PROJECT | 9 ++ .../api/v1alpha2/zz_generated.deepcopy.go | 94 +++++++++++++++++++ operator/config/rbac/role.yaml | 46 +++++++++ .../controllers/keptnworkload/controller.go | 6 ++ operator/go.mod | 3 + operator/go.sum | 23 +++++ operator/main.go | 35 ++++--- 7 files changed, 204 insertions(+), 12 deletions(-) diff --git a/operator/PROJECT b/operator/PROJECT index 9b4450862c..8637eaaa21 100644 --- a/operator/PROJECT +++ b/operator/PROJECT @@ -167,4 +167,13 @@ resources: kind: KeptnEvaluation path: github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2 version: v1alpha2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: keptn.sh + group: lifecycle + kind: KeptnAppCreationRequest + path: github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2 + version: v1alpha2 version: "3" diff --git a/operator/api/v1alpha2/zz_generated.deepcopy.go b/operator/api/v1alpha2/zz_generated.deepcopy.go index bf8ba4d138..fbddbd8f8f 100644 --- a/operator/api/v1alpha2/zz_generated.deepcopy.go +++ b/operator/api/v1alpha2/zz_generated.deepcopy.go @@ -197,6 +197,100 @@ func (in *KeptnApp) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnAppCreationRequest) DeepCopyInto(out *KeptnAppCreationRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnAppCreationRequest. +func (in *KeptnAppCreationRequest) DeepCopy() *KeptnAppCreationRequest { + if in == nil { + return nil + } + out := new(KeptnAppCreationRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnAppCreationRequest) 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 *KeptnAppCreationRequestList) DeepCopyInto(out *KeptnAppCreationRequestList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KeptnAppCreationRequest, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnAppCreationRequestList. +func (in *KeptnAppCreationRequestList) DeepCopy() *KeptnAppCreationRequestList { + if in == nil { + return nil + } + out := new(KeptnAppCreationRequestList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnAppCreationRequestList) 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 *KeptnAppCreationRequestSpec) DeepCopyInto(out *KeptnAppCreationRequestSpec) { + *out = *in + if in.DiscoveryDeadlineSeconds != nil { + in, out := &in.DiscoveryDeadlineSeconds, &out.DiscoveryDeadlineSeconds + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnAppCreationRequestSpec. +func (in *KeptnAppCreationRequestSpec) DeepCopy() *KeptnAppCreationRequestSpec { + if in == nil { + return nil + } + out := new(KeptnAppCreationRequestSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnAppCreationRequestStatus) DeepCopyInto(out *KeptnAppCreationRequestStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnAppCreationRequestStatus. +func (in *KeptnAppCreationRequestStatus) DeepCopy() *KeptnAppCreationRequestStatus { + if in == nil { + return nil + } + out := new(KeptnAppCreationRequestStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KeptnAppList) DeepCopyInto(out *KeptnAppList) { *out = *in diff --git a/operator/config/rbac/role.yaml b/operator/config/rbac/role.yaml index 0ce94c1e52..a6a69d3894 100644 --- a/operator/config/rbac/role.yaml +++ b/operator/config/rbac/role.yaml @@ -73,6 +73,44 @@ rules: - secrets verbs: - get +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnapp + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests/finalizers + verbs: + - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests/status + verbs: + - get + - patch + - update - apiGroups: - lifecycle.keptn.sh resources: @@ -245,6 +283,14 @@ rules: - get - patch - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnworkload + verbs: + - get + - list + - watch - apiGroups: - lifecycle.keptn.sh resources: diff --git a/operator/controllers/keptnworkload/controller.go b/operator/controllers/keptnworkload/controller.go index f5650320b6..aaba66e148 100644 --- a/operator/controllers/keptnworkload/controller.go +++ b/operator/controllers/keptnworkload/controller.go @@ -123,6 +123,12 @@ func (r *KeptnWorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Reques // SetupWithManager sets up the controller with the Manager. func (r *KeptnWorkloadReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &klcv1alpha2.KeptnWorkload{}, "spec.app", func(rawObj client.Object) []string { + workload := rawObj.(*klcv1alpha2.KeptnWorkload) + return []string{workload.Spec.AppName} + }); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). For(&klcv1alpha2.KeptnWorkload{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). Complete(r) diff --git a/operator/go.mod b/operator/go.mod index 01427c4658..4e4924ab0e 100644 --- a/operator/go.mod +++ b/operator/go.mod @@ -29,6 +29,7 @@ require ( require ( github.com/magiconair/properties v1.8.7 + github.com/onsi/ginkgo v1.16.5 k8s.io/apiserver v0.25.5 ) @@ -71,6 +72,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -94,6 +96,7 @@ require ( google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect diff --git a/operator/go.sum b/operator/go.sum index a2421342fa..b4fdb5bda3 100644 --- a/operator/go.sum +++ b/operator/go.sum @@ -114,6 +114,8 @@ github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -137,6 +139,7 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -231,6 +234,7 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 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= @@ -271,10 +275,17 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -401,6 +412,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -422,6 +434,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -468,6 +481,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -476,7 +490,10 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -497,6 +514,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -580,6 +598,7 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -741,12 +760,16 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/operator/main.go b/operator/main.go index b638cb98c9..e852ae778f 100644 --- a/operator/main.go +++ b/operator/main.go @@ -20,24 +20,13 @@ import ( "context" "flag" "fmt" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnappcreationrequest" "log" "net/http" "os" "time" "github.com/kelseyhightower/envconfig" - lifecyclev1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" - lifecyclev1alpha2 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2" - "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2/common" - controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnapp" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnappversion" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnevaluation" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptntask" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptntaskdefinition" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnworkload" - "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnworkloadinstance" - "github.com/keptn/lifecycle-toolkit/operator/webhooks" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" @@ -55,6 +44,20 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + lifecyclev1alpha1 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha1" + lifecyclev1alpha2 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2" + "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnapp" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnappversion" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnevaluation" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptntask" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptntaskdefinition" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnworkload" + "github.com/keptn/lifecycle-toolkit/operator/controllers/keptnworkloadinstance" + "github.com/keptn/lifecycle-toolkit/operator/webhooks" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -348,6 +351,14 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "KeptnApp") os.Exit(1) } + if err = (&keptnappcreationrequest.KeptnAppCreationRequestReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("KeptnAppCreationRequest Controller"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KeptnAppCreationRequest") + os.Exit(1) + } //+kubebuilder:scaffold:builder err = meter.RegisterCallback( From 1e5960185f48cbd5d1e34ececfc425c0d9967026 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Tue, 3 Jan 2023 15:28:31 +0100 Subject: [PATCH 2/6] create app creation request in webhook Signed-off-by: Florian Bacher --- operator/webhooks/pod_mutating_webhook.go | 64 ++++++++++++++--------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/operator/webhooks/pod_mutating_webhook.go b/operator/webhooks/pod_mutating_webhook.go index 75064af442..c6a0c83726 100644 --- a/operator/webhooks/pod_mutating_webhook.go +++ b/operator/webhooks/pod_mutating_webhook.go @@ -101,18 +101,18 @@ func (a *PodMutatingWebhook) Handle(ctx context.Context, req admission.Request) pod.Spec.SchedulerName = "keptn-scheduler" logger.Info("Annotations", "annotations", pod.Annotations) - isAppAnnotationPresent, err := a.isAppAnnotationPresent(pod) + _, err := a.isAppAnnotationPresent(pod) if err != nil { span.SetStatus(codes.Error, InvalidAnnotationMessage) return admission.Errored(http.StatusBadRequest, err) } - if !isAppAnnotationPresent { - if err := a.handleApp(ctx, logger, pod, req.Namespace); err != nil { - logger.Error(err, "Could not handle App") - span.SetStatus(codes.Error, err.Error()) - return admission.Errored(http.StatusBadRequest, err) - } + + if err := a.handleApp(ctx, logger, pod, req.Namespace); err != nil { + logger.Error(err, "Could not handle App") + span.SetStatus(codes.Error, err.Error()) + return admission.Errored(http.StatusBadRequest, err) } + semconv.AddAttributeFromAnnotations(span, pod.Annotations) logger.Info("Attributes from annotations set") @@ -343,26 +343,22 @@ func (a *PodMutatingWebhook) handleApp(ctx context.Context, logger logr.Logger, ctx, span := a.Tracer.Start(ctx, "create_app", trace.WithSpanKind(trace.SpanKindProducer)) defer span.End() - newApp := a.generateApp(ctx, pod, namespace) - - newApp.SetSpanAttributes(span) + newAppCreationRequest := a.generateAppCreationRequest(ctx, pod, namespace) - logger.Info("Searching for app") - - app := &klcv1alpha2.KeptnApp{} - err := a.Client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: newApp.Name}, app) + appCreationRequest := &klcv1alpha2.KeptnAppCreationRequest{} + err := a.Client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: newAppCreationRequest.Name}, appCreationRequest) if errors.IsNotFound(err) { - logger.Info("Creating app", "app", app.Name) - app = newApp - err = a.Client.Create(ctx, app) + logger.Info("Creating app creation request", "appCreationRequest", appCreationRequest.Name) + appCreationRequest = newAppCreationRequest + err = a.Client.Create(ctx, appCreationRequest) if err != nil { logger.Error(err, "Could not create App") - a.Recorder.Event(app, "Warning", "AppNotCreated", fmt.Sprintf("Could not create KeptnApp / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + a.Recorder.Event(appCreationRequest, "Warning", "AppNotCreated", fmt.Sprintf("Could not create KeptnAppCreationRequest / Namespace: %s, Name: %s ", appCreationRequest.Namespace, appCreationRequest.Name)) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(app, "Normal", "AppCreated", fmt.Sprintf("KeptnApp created / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + a.Recorder.Event(appCreationRequest, "Normal", "AppCreated", fmt.Sprintf("KeptnAppCreationRequest created / Namespace: %s, Name: %s ", appCreationRequest.Namespace, appCreationRequest.Name)) return nil } @@ -371,23 +367,23 @@ func (a *PodMutatingWebhook) handleApp(ctx context.Context, logger logr.Logger, return fmt.Errorf("could not fetch App"+": %+v", err) } - if reflect.DeepEqual(app.Spec, newApp.Spec) { + if reflect.DeepEqual(appCreationRequest.Spec, newAppCreationRequest.Spec) { logger.Info("Pod not changed, not updating anything") return nil } logger.Info("Pod changed, updating app") - app.Spec = newApp.Spec + appCreationRequest.Spec = newAppCreationRequest.Spec - err = a.Client.Update(ctx, app) + err = a.Client.Update(ctx, appCreationRequest) if err != nil { logger.Error(err, "Could not update App") - a.Recorder.Event(app, "Warning", "AppNotUpdated", fmt.Sprintf("Could not update KeptnApp / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + a.Recorder.Event(appCreationRequest, "Warning", "AppNotUpdated", fmt.Sprintf("Could not update KeptnAppCreationRequest / Namespace: %s, Name: %s ", appCreationRequest.Namespace, appCreationRequest.Name)) span.SetStatus(codes.Error, err.Error()) return err } - a.Recorder.Event(app, "Normal", "AppUpdated", fmt.Sprintf("KeptnApp updated / Namespace: %s, Name: %s ", app.Namespace, app.Name)) + a.Recorder.Event(appCreationRequest, "Normal", "AppUpdated", fmt.Sprintf("KeptnAppCreationRequest updated / Namespace: %s, Name: %s ", appCreationRequest.Namespace, appCreationRequest.Name)) return nil } @@ -445,6 +441,26 @@ func (a *PodMutatingWebhook) generateWorkload(ctx context.Context, pod *corev1.P } } +func (a *PodMutatingWebhook) generateAppCreationRequest(ctx context.Context, pod *corev1.Pod, namespace string) *klcv1alpha2.KeptnAppCreationRequest { + appName := a.getAppName(pod) + + // create TraceContext + // follow up with a Keptn propagator that JSON-encoded the OTel map into our own key + traceContextCarrier := propagation.MapCarrier{} + otel.GetTextMapPropagator().Inject(ctx, traceContextCarrier) + + return &klcv1alpha2.KeptnAppCreationRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: appName, + Namespace: namespace, + Annotations: traceContextCarrier, + }, + Spec: klcv1alpha2.KeptnAppCreationRequestSpec{ + AppName: appName, + }, + } +} + func (a *PodMutatingWebhook) generateApp(ctx context.Context, pod *corev1.Pod, namespace string) *klcv1alpha2.KeptnApp { version, _ := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.VersionAnnotation, apicommon.K8sRecommendedVersionAnnotations) appName := a.getAppName(pod) From eeda35f0f3ac6213c3bcfa05872b9822e9683cb2 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Wed, 4 Jan 2023 08:45:49 +0100 Subject: [PATCH 3/6] several fixes Signed-off-by: Florian Bacher --- operator/controllers/keptnworkloadinstance/controller.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/operator/controllers/keptnworkloadinstance/controller.go b/operator/controllers/keptnworkloadinstance/controller.go index 23bce60fd2..a9e85b5026 100644 --- a/operator/controllers/keptnworkloadinstance/controller.go +++ b/operator/controllers/keptnworkloadinstance/controller.go @@ -273,10 +273,13 @@ func (r *KeptnWorkloadInstanceReconciler) getAppVersionForWorkloadInstance(ctx c return false, klcv1alpha2.KeptnAppVersion{}, err } + r.Log.Info("found apps", "len", len(apps.Items), "workloadinstance", wli.Name) // due to effectivity reasons deprecated KeptnAppVersions are removed from the list, as there is // no point in iterating through them in the next steps apps.RemoveDeprecated() + r.Log.Info("found non-deprecated apps", "len", len(apps.Items), "workloadinstance", wli.Name) + workloadFound, latestVersion, err := getLatestAppVersion(apps, wli) if err != nil { r.Log.Error(err, "could not look up KeptnAppVersion for WorkloadInstance") @@ -296,12 +299,15 @@ func getLatestAppVersion(apps *klcv1alpha2.KeptnAppVersionList, wli *klcv1alpha2 workloadFound := false for _, app := range apps.Items { + fmt.Println(fmt.Sprintf("app: %s, wli.appName: %s", app.Name, wli.Spec.AppName)) if app.Spec.AppName == wli.Spec.AppName { for _, appWorkload := range app.Spec.Workloads { + fmt.Println(fmt.Sprintf("app.WorkloadVersion: %s, wli.version: %s, app.workload: %s, wli.spec.workloadName: %s", appWorkload.Version, wli.Spec.Version, app.GetWorkloadNameOfApp(appWorkload.Name), wli.Spec.WorkloadName)) if appWorkload.Version == wli.Spec.Version && app.GetWorkloadNameOfApp(appWorkload.Name) == wli.Spec.WorkloadName { workloadFound = true newVersion, err := version.NewVersion(app.Spec.Version) if err != nil { + fmt.Println("error when determining version: %s", err.Error()) return false, klcv1alpha2.KeptnAppVersion{}, err } if newVersion.GreaterThan(oldVersion) { From 4eb2b6f6ea27c90e65198324adc1c0968c751c80 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Wed, 4 Jan 2023 13:42:29 +0100 Subject: [PATCH 4/6] delete app creation request if user defined app has been found Signed-off-by: Florian Bacher --- examples/podtatohead-deployment/manifest.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/podtatohead-deployment/manifest.yaml b/examples/podtatohead-deployment/manifest.yaml index 6ea8ded970..c8f8c68f55 100644 --- a/examples/podtatohead-deployment/manifest.yaml +++ b/examples/podtatohead-deployment/manifest.yaml @@ -23,6 +23,7 @@ spec: component: podtato-head-entry keptn.sh/app: podtato-head keptn.sh/workload: podtato-head-entry + keptn.sh/version: 0.6.0 spec: terminationGracePeriodSeconds: 5 initContainers: @@ -38,6 +39,8 @@ spec: env: - name: PODTATO_PORT value: "9000" + - name: SOME_ENV + value: "hello" --- apiVersion: v1 kind: Service From 502d0f454ffc83fc2ab007b166eac94afaa7383e Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Wed, 4 Jan 2023 15:34:06 +0100 Subject: [PATCH 5/6] add missing files Signed-off-by: Florian Bacher --- .../v1alpha2/keptnappcreationrequest_types.go | 65 ++++++ ...cle.keptn.sh_keptnappcreationrequests.yaml | 57 +++++ ...injection_in_keptnappcreationrequests.yaml | 7 + .../webhook_in_keptnappcreationrequests.yaml | 16 ++ .../keptnappcreationrequest_editor_role.yaml | 24 +++ .../keptnappcreationrequest_viewer_role.yaml | 20 ++ ...ycle_v1alpha2_keptnappcreationrequest.yaml | 8 + .../keptnappcreationrequest/controller.go | 200 ++++++++++++++++++ 8 files changed, 397 insertions(+) create mode 100644 operator/api/v1alpha2/keptnappcreationrequest_types.go create mode 100644 operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml create mode 100644 operator/config/crd/patches/cainjection_in_keptnappcreationrequests.yaml create mode 100644 operator/config/crd/patches/webhook_in_keptnappcreationrequests.yaml create mode 100644 operator/config/rbac/keptnappcreationrequest_editor_role.yaml create mode 100644 operator/config/rbac/keptnappcreationrequest_viewer_role.yaml create mode 100644 operator/config/samples/lifecycle_v1alpha2_keptnappcreationrequest.yaml create mode 100644 operator/controllers/keptnappcreationrequest/controller.go diff --git a/operator/api/v1alpha2/keptnappcreationrequest_types.go b/operator/api/v1alpha2/keptnappcreationrequest_types.go new file mode 100644 index 0000000000..a6714cd21a --- /dev/null +++ b/operator/api/v1alpha2/keptnappcreationrequest_types.go @@ -0,0 +1,65 @@ +/* +Copyright 2022. + +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 v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// KeptnAppCreationRequestSpec defines the desired state of KeptnAppCreationRequest +type KeptnAppCreationRequestSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + AppName string `json:"appName"` + // +kubebuilder:default=30 + DiscoveryDeadlineSeconds *int64 `json:"discoveryDeadlineSeconds,omitempty"` +} + +// KeptnAppCreationRequestStatus defines the observed state of KeptnAppCreationRequest +type KeptnAppCreationRequestStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// KeptnAppCreationRequest is the Schema for the keptnappcreationrequests API +type KeptnAppCreationRequest struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KeptnAppCreationRequestSpec `json:"spec,omitempty"` + Status KeptnAppCreationRequestStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KeptnAppCreationRequestList contains a list of KeptnAppCreationRequest +type KeptnAppCreationRequestList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KeptnAppCreationRequest `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KeptnAppCreationRequest{}, &KeptnAppCreationRequestList{}) +} diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml new file mode 100644 index 0000000000..6393154baa --- /dev/null +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml @@ -0,0 +1,57 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: keptnappcreationrequests.lifecycle.keptn.sh +spec: + group: lifecycle.keptn.sh + names: + kind: KeptnAppCreationRequest + listKind: KeptnAppCreationRequestList + plural: keptnappcreationrequests + singular: keptnappcreationrequest + scope: Namespaced + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: KeptnAppCreationRequest is the Schema for the keptnappcreationrequests + 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: KeptnAppCreationRequestSpec defines the desired state of + KeptnAppCreationRequest + properties: + appName: + type: string + discoveryDeadlineSeconds: + default: 30 + format: int64 + type: integer + required: + - appName + type: object + status: + description: KeptnAppCreationRequestStatus defines the observed state + of KeptnAppCreationRequest + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/operator/config/crd/patches/cainjection_in_keptnappcreationrequests.yaml b/operator/config/crd/patches/cainjection_in_keptnappcreationrequests.yaml new file mode 100644 index 0000000000..f8023edd07 --- /dev/null +++ b/operator/config/crd/patches/cainjection_in_keptnappcreationrequests.yaml @@ -0,0 +1,7 @@ +# 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: keptnappcreationrequests.lifecycle.keptn.sh diff --git a/operator/config/crd/patches/webhook_in_keptnappcreationrequests.yaml b/operator/config/crd/patches/webhook_in_keptnappcreationrequests.yaml new file mode 100644 index 0000000000..fb68a5908b --- /dev/null +++ b/operator/config/crd/patches/webhook_in_keptnappcreationrequests.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: keptnappcreationrequests.lifecycle.keptn.sh +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/operator/config/rbac/keptnappcreationrequest_editor_role.yaml b/operator/config/rbac/keptnappcreationrequest_editor_role.yaml new file mode 100644 index 0000000000..b7fdf0bec3 --- /dev/null +++ b/operator/config/rbac/keptnappcreationrequest_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit keptnappcreationrequests. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptnappcreationrequest-editor-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests/status + verbs: + - get diff --git a/operator/config/rbac/keptnappcreationrequest_viewer_role.yaml b/operator/config/rbac/keptnappcreationrequest_viewer_role.yaml new file mode 100644 index 0000000000..bec284a954 --- /dev/null +++ b/operator/config/rbac/keptnappcreationrequest_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view keptnappcreationrequests. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptnappcreationrequest-viewer-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests + verbs: + - get + - list + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptnappcreationrequests/status + verbs: + - get diff --git a/operator/config/samples/lifecycle_v1alpha2_keptnappcreationrequest.yaml b/operator/config/samples/lifecycle_v1alpha2_keptnappcreationrequest.yaml new file mode 100644 index 0000000000..f467018c18 --- /dev/null +++ b/operator/config/samples/lifecycle_v1alpha2_keptnappcreationrequest.yaml @@ -0,0 +1,8 @@ +apiVersion: lifecycle.keptn.sh/v1alpha2 +kind: KeptnAppCreationRequest +metadata: + name: keptnappcreationrequest-sample + namespace: podtato-kubectl +spec: + appName: "my-app" + discoveryDeadlineSeconds: 10 diff --git a/operator/controllers/keptnappcreationrequest/controller.go b/operator/controllers/keptnappcreationrequest/controller.go new file mode 100644 index 0000000000..267a42379f --- /dev/null +++ b/operator/controllers/keptnappcreationrequest/controller.go @@ -0,0 +1,200 @@ +/* +Copyright 2022. + +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 keptnappcreationrequest + +import ( + "context" + "fmt" + "github.com/go-logr/logr" + "github.com/hashicorp/go-version" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "strings" + "time" + + klcv1alpha2 "github.com/keptn/lifecycle-toolkit/operator/api/v1alpha2" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// KeptnAppCreationRequestReconciler reconciles a KeptnAppCreationRequest object +type KeptnAppCreationRequestReconciler struct { + client.Client + Scheme *runtime.Scheme + Log logr.Logger +} + +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnappcreationrequests,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnappcreationrequests/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnappcreationrequests/finalizers,verbs=update +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnapp,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptnworkload,verbs=get;list;watch + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the KeptnAppCreationRequest object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile +func (r *KeptnAppCreationRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + creationRequest := &klcv1alpha2.KeptnAppCreationRequest{} + + if err := r.Get(ctx, req.NamespacedName, creationRequest); err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, fmt.Errorf("could not retrieve KeptnAppCreationRequest: %w", err) + } + + // check if we already have an app that has not been created by this controller + + appFound := false + keptnApp := &klcv1alpha2.KeptnApp{} + name := req.NamespacedName + name.Name = creationRequest.Spec.AppName + if err := r.Get(ctx, name, keptnApp); err != nil { + if errors.IsNotFound(err) { + r.Log.Info("No KeptnApp found for KeptnAppCreationRequest", "KeptnAppCreationRequest", creationRequest) + } else { + return ctrl.Result{}, fmt.Errorf("could not retrieve KeptnApp %w", err) + } + } else { + appFound = true + } + + // if the found app has not been created by this controller, we are done at this point - we don't want to mess with what the user has created + if appFound && len(keptnApp.OwnerReferences) == 0 { + r.Log.Info("User defined KeptnApp found for KeptnAppCreationRequest", "KeptnAppCreationRequest", creationRequest) + if err := r.Delete(ctx, creationRequest); err != nil { + r.Log.Error(err, "Could not delete KeptnAppCreationRequest", "KeptnAppCreationRequest", creationRequest) + } + return ctrl.Result{}, nil + } + + // check if discovery deadline has expired + discoveryDeadlineSeconds := time.Duration(*creationRequest.Spec.DiscoveryDeadlineSeconds) + if !time.Now().After(creationRequest.CreationTimestamp.Add(discoveryDeadlineSeconds * time.Second)) { + r.Log.Info("Discovery deadline not expired yet", "KeptnAppCreationRequest", creationRequest) + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + } + + // look up all the KeptnWorkloads referencing the KeptnApp + + workloads := &klcv1alpha2.KeptnWorkloadList{} + if err := r.Client.List(ctx, workloads, client.InNamespace(creationRequest.Namespace), client.MatchingFields{ + "spec.app": creationRequest.Spec.AppName, + }); err != nil { + return ctrl.Result{RequeueAfter: 10 * time.Second}, fmt.Errorf("could not retrieve KeptnWorkloads: %w", err) + } + + var err error + if !appFound { + err = r.createKeptnApp(ctx, creationRequest, workloads) + } else { + err = r.updateKeptnApp(ctx, keptnApp, workloads) + } + + if err != nil { + return ctrl.Result{}, fmt.Errorf("could not update: %w", err) + } + + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *KeptnAppCreationRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&klcv1alpha2.KeptnAppCreationRequest{}). + Complete(r) +} + +func (r *KeptnAppCreationRequestReconciler) updateKeptnApp(ctx context.Context, keptnApp *klcv1alpha2.KeptnApp, workloads *klcv1alpha2.KeptnWorkloadList) error { + + updatedVersion := false + addedWorkload := false + for _, workload := range workloads.Items { + foundWorkload := false + workloadName := strings.TrimPrefix(workload.Name, fmt.Sprintf("%s-", keptnApp.Name)) + for index, appWorkload := range keptnApp.Spec.Workloads { + if appWorkload.Name == workloadName { + // make sure the version matches the current version of the workload + if keptnApp.Spec.Workloads[index].Version != workload.Spec.Version { + keptnApp.Spec.Workloads[index].Version = workload.Spec.Version + // we may also want to increase the version of the app if any version has been changed + updatedVersion = true + } + foundWorkload = true + break + } + } + + if !foundWorkload { + keptnApp.Spec.Workloads = append(keptnApp.Spec.Workloads, klcv1alpha2.KeptnWorkloadRef{ + Name: workloadName, + Version: workload.Spec.Version, + }) + addedWorkload = true + } + } + + if !updatedVersion && !addedWorkload { + return nil + } + + if updatedVersion { + oldVersion, _ := version.NewVersion(keptnApp.Spec.Version) + keptnApp.Spec.Version = fmt.Sprintf("%d.0.0", oldVersion.Segments()[0]+1) + } + + return r.Update(ctx, keptnApp) +} + +func (r *KeptnAppCreationRequestReconciler) createKeptnApp(ctx context.Context, creationRequest *klcv1alpha2.KeptnAppCreationRequest, workloads *klcv1alpha2.KeptnWorkloadList) error { + keptnApp := &klcv1alpha2.KeptnApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: creationRequest.Spec.AppName, + Namespace: creationRequest.Namespace, + }, + Spec: klcv1alpha2.KeptnAppSpec{ + Version: "1.0.0", + PreDeploymentTasks: []string{}, + PostDeploymentTasks: []string{}, + PreDeploymentEvaluations: []string{}, + PostDeploymentEvaluations: []string{}, + Workloads: []klcv1alpha2.KeptnWorkloadRef{}, + }, + } + + if err := controllerutil.SetControllerReference(creationRequest, keptnApp, r.Scheme); err != nil { + r.Log.Error(err, "could not set controller reference for KeptnApp: "+keptnApp.Name) + } + + for _, workload := range workloads.Items { + keptnApp.Spec.Workloads = append(keptnApp.Spec.Workloads, klcv1alpha2.KeptnWorkloadRef{ + Name: strings.TrimPrefix(workload.Name, fmt.Sprintf("%s-", creationRequest.Spec.AppName)), + Version: workload.Spec.Version, + }) + } + + return r.Create(ctx, keptnApp) +} From 74b263464ce3085a7ca846f82999ba11a0960528 Mon Sep 17 00:00:00 2001 From: Florian Bacher Date: Tue, 10 Jan 2023 15:31:56 +0100 Subject: [PATCH 6/6] adapted example Signed-off-by: Florian Bacher --- examples/podtatohead-deployment/app.yaml | 46 +++++++------- .../podtatohead-deployment/check_entry.yaml | 12 ---- examples/podtatohead-deployment/manifest.yaml | 63 +++++++++++++++++++ 3 files changed, 86 insertions(+), 35 deletions(-) delete mode 100644 examples/podtatohead-deployment/check_entry.yaml diff --git a/examples/podtatohead-deployment/app.yaml b/examples/podtatohead-deployment/app.yaml index 1498cf3ab7..2b8c483bac 100644 --- a/examples/podtatohead-deployment/app.yaml +++ b/examples/podtatohead-deployment/app.yaml @@ -1,24 +1,24 @@ -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnApp -metadata: - name: podtato-head - namespace: podtato-kubectl -spec: - version: "1.3" - revision: "1" - workloads: - - name: podtato-head-left-arm - version: 0.2.7 - - name: podtato-head-left-leg - version: 0.2.7 - - name: podtato-head-entry - version: 0.2.7 - - name: podtato-head-right-arm - version: 0.1.0 - - name: podtato-head-right-leg - version: 0.2.7 - - name: podtato-head-hat - version: 0.1.0 - postDeploymentTasks: - - post-deployment-hello +#apiVersion: lifecycle.keptn.sh/v1alpha2 +#kind: KeptnApp +#metadata: +# name: podtato-head +# namespace: podtato-kubectl +#spec: +# version: "1.3" +# revision: "1" +# workloads: +# - name: podtato-head-left-arm +# version: 0.2.7 +# - name: podtato-head-left-leg +# version: 0.2.7 +# - name: podtato-head-entry +# version: 0.2.7 +# - name: podtato-head-right-arm +# version: 0.1.0 +# - name: podtato-head-right-leg +# version: 0.2.7 +# - name: podtato-head-hat +# version: 0.1.0 +# postDeploymentTasks: +# - post-deployment-hello diff --git a/examples/podtatohead-deployment/check_entry.yaml b/examples/podtatohead-deployment/check_entry.yaml deleted file mode 100644 index 61f8a2773d..0000000000 --- a/examples/podtatohead-deployment/check_entry.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: check-entry-service - namespace: podtato-kubectl -spec: - function: - httpRef: - url: https://raw.githubusercontent.com/keptn/lifecycle-toolkit/main/functions-runtime/samples/ts/http.ts - parameters: - map: - url: http://podtato-head-entry.podtato-kubectl.svc.cluster.local:9000 diff --git a/examples/podtatohead-deployment/manifest.yaml b/examples/podtatohead-deployment/manifest.yaml index c8f8c68f55..c1c8213245 100644 --- a/examples/podtatohead-deployment/manifest.yaml +++ b/examples/podtatohead-deployment/manifest.yaml @@ -6,6 +6,19 @@ metadata: annotations: keptn.sh/lifecycle-toolkit: "enabled" --- +apiVersion: lifecycle.keptn.sh/v1alpha2 +kind: KeptnTaskDefinition +metadata: + name: check-entry-service + namespace: podtato-kubectl +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn/lifecycle-toolkit/main/functions-runtime/samples/ts/http.ts + parameters: + map: + url: http://podtato-head-entry.podtato-kubectl.svc.cluster.local:9000 +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -303,3 +316,53 @@ spec: protocol: TCP targetPort: 9000 type: ClusterIP +--- +#apiVersion: apps/v1 +#kind: Deployment +#metadata: +# name: podtato-head-third-arm +# namespace: podtato-kubectl +# labels: +# app: podtato-head +#spec: +# selector: +# matchLabels: +# component: podtato-head-third-arm +# template: +# metadata: +# labels: +# component: podtato-head-third-arm +# annotations: +# app.kubernetes.io/part-of: podtato-head +# keptn.sh/workload: podtato-head-third-arm +# keptn.sh/version: 0.2.0 +# keptn.sh/pre-deployment-tasks: check-entry-service +# spec: +# terminationGracePeriodSeconds: 5 +# containers: +# - name: server +# image: ghcr.io/podtato-head/right-arm:0.2.7 +# imagePullPolicy: Always +# ports: +# - containerPort: 9000 +# env: +# - name: PODTATO_PORT +# value: "9000" +#--- +#apiVersion: v1 +#kind: Service +#metadata: +# name: podtato-head-third-arm +# namespace: podtato-kubectl +# labels: +# app: podtato-head +#spec: +# selector: +# component: podtato-head-third-arm +# ports: +# - name: http +# port: 9005 +# protocol: TCP +# targetPort: 9000 +# type: ClusterIP +# \ No newline at end of file