diff --git a/examples/taskonly-hello-keptn/README.md b/examples/taskonly-hello-keptn/README.md new file mode 100644 index 0000000000..ff9f7f5e30 --- /dev/null +++ b/examples/taskonly-hello-keptn/README.md @@ -0,0 +1,25 @@ +# Hello, Keptn! + +## Goal +This example shows how to define an inline function and pass over parameters to its execution. + +## Variants +* `inline` - shows how to specify a function directly in the `KeptnTaskDefinition` +* `http` - fetches the Script from the Web +* `upstream` - shows how functions could be reused + +## Usage + +* Edit task.yaml and add your name to `spec.parameters.map.name` +* Navigate to the corresponding folder +* Apply the manifests: `kubectl apply -f .` + +## Outcome + +* A KeptnTaskDefinition `hello-keptn-` should be created +* A KeptnTask `hello-developer` should be created +* You can track the state of the job with `kubectl get KeptnTask hello-developer` +``` +NAME APPLICATION WORKLOAD VERSION JOB NAME STATUS +hello-developer my-app my-workload 1.0 klc-my-app-my-workload-1.0-57692 Succeeded +``` \ No newline at end of file diff --git a/examples/taskonly-hello-keptn/http/hello-keptn.ts b/examples/taskonly-hello-keptn/http/hello-keptn.ts new file mode 100644 index 0000000000..dc1dab8cf7 --- /dev/null +++ b/examples/taskonly-hello-keptn/http/hello-keptn.ts @@ -0,0 +1,7 @@ +let text = Deno.env.get("DATA"); +let data; +let name; +data = JSON.parse(text); + +name = data.name +console.log("Hello, " + name); diff --git a/examples/taskonly-hello-keptn/http/task.yaml b/examples/taskonly-hello-keptn/http/task.yaml new file mode 100644 index 0000000000..39c16acfd4 --- /dev/null +++ b/examples/taskonly-hello-keptn/http/task.yaml @@ -0,0 +1,12 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: hello-developer-http +spec: + taskDefinition: hello-keptn-http + workload: my-workload + workloadVersion: "1.0" + application: my-app + parameters: + map: + name: "Keptn-Developer" diff --git a/examples/taskonly-hello-keptn/http/taskdefinition.yaml b/examples/taskonly-hello-keptn/http/taskdefinition.yaml new file mode 100644 index 0000000000..ed4c3b2ba8 --- /dev/null +++ b/examples/taskonly-hello-keptn/http/taskdefinition.yaml @@ -0,0 +1,10 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-http +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn-sandbox/lifecycle-controller/main/examples/taskonly-hello-keptn/http/hello-keptn.ts + + diff --git a/examples/taskonly-hello-keptn/inline/task.yaml b/examples/taskonly-hello-keptn/inline/task.yaml new file mode 100644 index 0000000000..960546cf1a --- /dev/null +++ b/examples/taskonly-hello-keptn/inline/task.yaml @@ -0,0 +1,12 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: hello-developer-inline +spec: + taskDefinition: hello-keptn-inline + workload: my-workload + workloadVersion: "1.0" + application: my-app + parameters: + map: + name: "Keptn Developer 1" diff --git a/examples/taskonly-hello-keptn/inline/taskdefinition.yaml b/examples/taskonly-hello-keptn/inline/taskdefinition.yaml new file mode 100644 index 0000000000..b6aa2dd47c --- /dev/null +++ b/examples/taskonly-hello-keptn/inline/taskdefinition.yaml @@ -0,0 +1,16 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-inline +spec: + function: + inlineRef: + code: | + let text = Deno.env.get("DATA"); + let data; + let name; + data = JSON.parse(text); + + name = data.name + console.log("Hello, " + name + " new"); + diff --git a/examples/taskonly-hello-keptn/upstream/hello-keptn.ts b/examples/taskonly-hello-keptn/upstream/hello-keptn.ts new file mode 100644 index 0000000000..dc1dab8cf7 --- /dev/null +++ b/examples/taskonly-hello-keptn/upstream/hello-keptn.ts @@ -0,0 +1,7 @@ +let text = Deno.env.get("DATA"); +let data; +let name; +data = JSON.parse(text); + +name = data.name +console.log("Hello, " + name); diff --git a/examples/taskonly-hello-keptn/upstream/task.yaml b/examples/taskonly-hello-keptn/upstream/task.yaml new file mode 100644 index 0000000000..6552743b5b --- /dev/null +++ b/examples/taskonly-hello-keptn/upstream/task.yaml @@ -0,0 +1,9 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: hello-developer-upstream +spec: + taskDefinition: hello-keptn-upstream + workload: my-workload + workloadVersion: "1.0" + application: my-app \ No newline at end of file diff --git a/examples/taskonly-hello-keptn/upstream/taskdefinition.yaml b/examples/taskonly-hello-keptn/upstream/taskdefinition.yaml new file mode 100644 index 0000000000..6e5067b4cc --- /dev/null +++ b/examples/taskonly-hello-keptn/upstream/taskdefinition.yaml @@ -0,0 +1,13 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-upstream +spec: + function: + functionRef: + name: hello-keptn-upstream-parent + parameters: + map: + name: "Developer" + + diff --git a/examples/taskonly-hello-keptn/upstream/taskdefinition_upstream.yaml b/examples/taskonly-hello-keptn/upstream/taskdefinition_upstream.yaml new file mode 100644 index 0000000000..1c89b83180 --- /dev/null +++ b/examples/taskonly-hello-keptn/upstream/taskdefinition_upstream.yaml @@ -0,0 +1,12 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-upstream-parent +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn-sandbox/lifecycle-controller/main/examples/taskonly-hello-keptn/http/hello-keptn.ts +parameters: + map: + name: "Parent Developer" + diff --git a/operator/PROJECT b/operator/PROJECT index 4894ac8998..544c2d07e1 100644 --- a/operator/PROJECT +++ b/operator/PROJECT @@ -44,4 +44,22 @@ resources: kind: ServiceRun path: github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: keptn.sh + group: lifecycle + kind: KeptnTaskDefinition + path: github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: keptn.sh + group: lifecycle + kind: KeptnTask + path: github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/operator/api/v1alpha1/keptntask_types.go b/operator/api/v1alpha1/keptntask_types.go new file mode 100644 index 0000000000..f99926f55f --- /dev/null +++ b/operator/api/v1alpha1/keptntask_types.go @@ -0,0 +1,93 @@ +/* +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 v1alpha1 + +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. + +// KeptnTaskSpec defines the desired state of KeptnTask +type KeptnTaskSpec struct { + Workload string `json:"workload"` + WorkloadVersion string `json:"workloadVersion"` + Application string `json:"application"` + TaskDefinition string `json:"taskDefinition"` + Parameters TaskParameters `json:"parameters,omitempty"` + SecureParameters SecureParameters `json:"secureParameters,omitempty"` +} + +type KeptnTaskPhase string + +const ( + // TaskPending means the task has been accepted by the system, but the corresponding Job did not start + TaskPending KeptnTaskPhase = "Pending" + // TaskRunning means that the Job has been started. + TaskRunning KeptnTaskPhase = "Running" + // TaskFailed means that the Job failed + TaskFailed KeptnTaskPhase = "Failed" + // TaskSucceeded means that the Job has finished successfully + TaskSucceeded KeptnTaskPhase = "Succeeded" +) + +type TaskParameters struct { + Inline map[string]string `json:"map,omitempty"` +} + +type SecureParameters struct { + Secret string `json:"secret,omitempty"` +} + +// KeptnTaskStatus defines the observed state of KeptnTask +type KeptnTaskStatus struct { + JobName string `json:"jobName,omitempty"` + Status KeptnTaskPhase `json:"status,omitempty"` + // 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 +// +kubebuilder:printcolumn:name="Application",type=string,JSONPath=`.spec.application` +// +kubebuilder:printcolumn:name="Workload",type=string,JSONPath=`.spec.workload` +// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.workloadVersion` +// +kubebuilder:printcolumn:name="Job Name",type=string,JSONPath=`.status.jobName` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` + +// KeptnTask is the Schema for the keptntasks API +type KeptnTask struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KeptnTaskSpec `json:"spec,omitempty"` + Status KeptnTaskStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KeptnTaskList contains a list of KeptnTask +type KeptnTaskList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KeptnTask `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KeptnTask{}, &KeptnTaskList{}) +} diff --git a/operator/api/v1alpha1/keptntaskdefinition_types.go b/operator/api/v1alpha1/keptntaskdefinition_types.go new file mode 100644 index 0000000000..385e3bd31d --- /dev/null +++ b/operator/api/v1alpha1/keptntaskdefinition_types.go @@ -0,0 +1,95 @@ +/* +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 v1alpha1 + +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. + +// KeptnTaskDefinitionSpec defines the desired state of KeptnTaskDefinition +type KeptnTaskDefinitionSpec struct { + Function FunctionSpec `json:"function,omitempty"` +} + +type FunctionSpec struct { + FunctionReference *FunctionReference `json:"functionRef,omitempty"` + Inline *Inline `json:"inline,omitempty"` + HttpReference *HttpReference `json:"httpRef,omitempty"` + ConfigMapReference *ConfigMapReference `json:"configMapRef,omitempty"` + Parameters TaskParameters `json:"parameters,omitempty"` + SecureParameters SecureParameters `json:"secureParameters,omitempty"` +} + +type ConfigMapReference struct { + Name string `json:"name,omitempty"` +} + +type FunctionReference struct { + Name string `json:"name,omitempty"` +} + +type Inline struct { + Code string `json:"code,omitempty"` +} + +type HttpReference struct { + Url string `json:"url,omitempty"` +} + +type ContainerSpec struct { +} + +// KeptnTaskDefinitionStatus defines the observed state of KeptnTaskDefinition +type KeptnTaskDefinitionStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Function FunctionStatus `json:"function,omitempty"` +} + +type FunctionStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + ConfigMap string `json:"configMap,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// KeptnTaskDefinition is the Schema for the keptntaskdefinitions API +type KeptnTaskDefinition struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KeptnTaskDefinitionSpec `json:"spec,omitempty"` + Status KeptnTaskDefinitionStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KeptnTaskDefinitionList contains a list of KeptnTaskDefinition +type KeptnTaskDefinitionList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KeptnTaskDefinition `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KeptnTaskDefinition{}, &KeptnTaskDefinitionList{}) +} diff --git a/operator/api/v1alpha1/zz_generated.deepcopy.go b/operator/api/v1alpha1/zz_generated.deepcopy.go index e77a7a6e1f..66dc9fcdbd 100644 --- a/operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/operator/api/v1alpha1/zz_generated.deepcopy.go @@ -131,6 +131,36 @@ func (in *ApplicationStatus) DeepCopy() *ApplicationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapReference) DeepCopyInto(out *ConfigMapReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapReference. +func (in *ConfigMapReference) DeepCopy() *ConfigMapReference { + if in == nil { + return nil + } + out := new(ConfigMapReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerSpec) DeepCopyInto(out *ContainerSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSpec. +func (in *ContainerSpec) DeepCopy() *ContainerSpec { + if in == nil { + return nil + } + out := new(ContainerSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Event) DeepCopyInto(out *Event) { *out = *in @@ -221,6 +251,284 @@ func (in *EventStatus) DeepCopy() *EventStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionReference) DeepCopyInto(out *FunctionReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionReference. +func (in *FunctionReference) DeepCopy() *FunctionReference { + if in == nil { + return nil + } + out := new(FunctionReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) { + *out = *in + out.FunctionReference = in.FunctionReference + out.Inline = in.Inline + out.HttpReference = in.HttpReference + out.ConfigMapReference = in.ConfigMapReference + in.Parameters.DeepCopyInto(&out.Parameters) + out.SecureParameters = in.SecureParameters +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionSpec. +func (in *FunctionSpec) DeepCopy() *FunctionSpec { + if in == nil { + return nil + } + out := new(FunctionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatus. +func (in *FunctionStatus) DeepCopy() *FunctionStatus { + if in == nil { + return nil + } + out := new(FunctionStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HttpReference) DeepCopyInto(out *HttpReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HttpReference. +func (in *HttpReference) DeepCopy() *HttpReference { + if in == nil { + return nil + } + out := new(HttpReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Inline) DeepCopyInto(out *Inline) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Inline. +func (in *Inline) DeepCopy() *Inline { + if in == nil { + return nil + } + out := new(Inline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnTask) DeepCopyInto(out *KeptnTask) { + *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 KeptnTask. +func (in *KeptnTask) DeepCopy() *KeptnTask { + if in == nil { + return nil + } + out := new(KeptnTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnTask) 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 *KeptnTaskDefinition) DeepCopyInto(out *KeptnTaskDefinition) { + *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 KeptnTaskDefinition. +func (in *KeptnTaskDefinition) DeepCopy() *KeptnTaskDefinition { + if in == nil { + return nil + } + out := new(KeptnTaskDefinition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnTaskDefinition) 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 *KeptnTaskDefinitionList) DeepCopyInto(out *KeptnTaskDefinitionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KeptnTaskDefinition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskDefinitionList. +func (in *KeptnTaskDefinitionList) DeepCopy() *KeptnTaskDefinitionList { + if in == nil { + return nil + } + out := new(KeptnTaskDefinitionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnTaskDefinitionList) 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 *KeptnTaskDefinitionSpec) DeepCopyInto(out *KeptnTaskDefinitionSpec) { + *out = *in + in.Function.DeepCopyInto(&out.Function) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskDefinitionSpec. +func (in *KeptnTaskDefinitionSpec) DeepCopy() *KeptnTaskDefinitionSpec { + if in == nil { + return nil + } + out := new(KeptnTaskDefinitionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnTaskDefinitionStatus) DeepCopyInto(out *KeptnTaskDefinitionStatus) { + *out = *in + out.Function = in.Function +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskDefinitionStatus. +func (in *KeptnTaskDefinitionStatus) DeepCopy() *KeptnTaskDefinitionStatus { + if in == nil { + return nil + } + out := new(KeptnTaskDefinitionStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnTaskList) DeepCopyInto(out *KeptnTaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KeptnTask, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskList. +func (in *KeptnTaskList) DeepCopy() *KeptnTaskList { + if in == nil { + return nil + } + out := new(KeptnTaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeptnTaskList) 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 *KeptnTaskSpec) DeepCopyInto(out *KeptnTaskSpec) { + *out = *in + in.Parameters.DeepCopyInto(&out.Parameters) + out.SecureParameters = in.SecureParameters +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskSpec. +func (in *KeptnTaskSpec) DeepCopy() *KeptnTaskSpec { + if in == nil { + return nil + } + out := new(KeptnTaskSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeptnTaskStatus) DeepCopyInto(out *KeptnTaskStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeptnTaskStatus. +func (in *KeptnTaskStatus) DeepCopy() *KeptnTaskStatus { + if in == nil { + return nil + } + out := new(KeptnTaskStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecureParameters) DeepCopyInto(out *SecureParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecureParameters. +func (in *SecureParameters) DeepCopy() *SecureParameters { + if in == nil { + return nil + } + out := new(SecureParameters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Service) DeepCopyInto(out *Service) { *out = *in @@ -399,3 +707,25 @@ func (in *ServiceStatus) DeepCopy() *ServiceStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskParameters) DeepCopyInto(out *TaskParameters) { + *out = *in + if in.Inline != nil { + in, out := &in.Inline, &out.Inline + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskParameters. +func (in *TaskParameters) DeepCopy() *TaskParameters { + if in == nil { + return nil + } + out := new(TaskParameters) + in.DeepCopyInto(out) + return out +} diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml new file mode 100644 index 0000000000..c06275ee72 --- /dev/null +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml @@ -0,0 +1,94 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: keptntaskdefinitions.lifecycle.keptn.sh +spec: + group: lifecycle.keptn.sh + names: + kind: KeptnTaskDefinition + listKind: KeptnTaskDefinitionList + plural: keptntaskdefinitions + singular: keptntaskdefinition + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: KeptnTaskDefinition is the Schema for the keptntaskdefinitions + 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: KeptnTaskDefinitionSpec defines the desired state of KeptnTaskDefinition + properties: + function: + properties: + configMapRef: + properties: + name: + type: string + type: object + functionRef: + properties: + name: + type: string + type: object + httpRef: + properties: + url: + type: string + type: object + inline: + properties: + code: + type: string + type: object + parameters: + properties: + map: + additionalProperties: + type: string + type: object + type: object + secureParameters: + properties: + secret: + type: string + type: object + type: object + type: object + status: + description: KeptnTaskDefinitionStatus defines the observed state of KeptnTaskDefinition + properties: + function: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + properties: + configMap: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed + state of cluster Important: Run "make" to regenerate code after + modifying this file' + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml new file mode 100644 index 0000000000..1b8f371bb9 --- /dev/null +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml @@ -0,0 +1,92 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: keptntasks.lifecycle.keptn.sh +spec: + group: lifecycle.keptn.sh + names: + kind: KeptnTask + listKind: KeptnTaskList + plural: keptntasks + singular: keptntask + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.application + name: Application + type: string + - jsonPath: .spec.workload + name: Workload + type: string + - jsonPath: .spec.workloadVersion + name: Version + type: string + - jsonPath: .status.jobName + name: Job Name + type: string + - jsonPath: .status.status + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: KeptnTask is the Schema for the keptntasks 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: KeptnTaskSpec defines the desired state of KeptnTask + properties: + application: + type: string + parameters: + properties: + map: + additionalProperties: + type: string + type: object + type: object + secureParameters: + properties: + secret: + type: string + type: object + taskDefinition: + type: string + workload: + type: string + workloadVersion: + type: string + required: + - application + - taskDefinition + - workload + - workloadVersion + type: object + status: + description: KeptnTaskStatus defines the observed state of KeptnTask + properties: + jobName: + type: string + status: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/operator/config/crd/kustomization.yaml b/operator/config/crd/kustomization.yaml index 65c4eadc18..88ddf67784 100644 --- a/operator/config/crd/kustomization.yaml +++ b/operator/config/crd/kustomization.yaml @@ -6,6 +6,8 @@ resources: - bases/lifecycle.keptn.sh_services.yaml - bases/lifecycle.keptn.sh_events.yaml - bases/lifecycle.keptn.sh_serviceruns.yaml +- bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml +- bases/lifecycle.keptn.sh_keptntasks.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -15,6 +17,8 @@ patchesStrategicMerge: #- patches/webhook_in_services.yaml #- patches/webhook_in_events.yaml #- patches/webhook_in_serviceruns.yaml +#- patches/webhook_in_keptntaskdefinitions.yaml +#- patches/webhook_in_keptntasks.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -23,6 +27,8 @@ patchesStrategicMerge: #- patches/cainjection_in_services.yaml #- patches/cainjection_in_events.yaml #- patches/cainjection_in_serviceruns.yaml +#- patches/cainjection_in_keptntaskdefinitions.yaml +#- patches/cainjection_in_keptntasks.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/operator/config/crd/patches/cainjection_in_keptntaskdefinitions.yaml b/operator/config/crd/patches/cainjection_in_keptntaskdefinitions.yaml new file mode 100644 index 0000000000..4307cb3538 --- /dev/null +++ b/operator/config/crd/patches/cainjection_in_keptntaskdefinitions.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: keptntaskdefinitions.lifecycle.keptn.sh diff --git a/operator/config/crd/patches/cainjection_in_keptntasks.yaml b/operator/config/crd/patches/cainjection_in_keptntasks.yaml new file mode 100644 index 0000000000..83fc85ce21 --- /dev/null +++ b/operator/config/crd/patches/cainjection_in_keptntasks.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: keptntasks.lifecycle.keptn.sh diff --git a/operator/config/crd/patches/webhook_in_keptntaskdefinitions.yaml b/operator/config/crd/patches/webhook_in_keptntaskdefinitions.yaml new file mode 100644 index 0000000000..154700ce30 --- /dev/null +++ b/operator/config/crd/patches/webhook_in_keptntaskdefinitions.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: keptntaskdefinitions.lifecycle.keptn.sh +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/operator/config/crd/patches/webhook_in_keptntasks.yaml b/operator/config/crd/patches/webhook_in_keptntasks.yaml new file mode 100644 index 0000000000..c86330ed55 --- /dev/null +++ b/operator/config/crd/patches/webhook_in_keptntasks.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: keptntasks.lifecycle.keptn.sh +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/operator/config/manager/kustomization.yaml b/operator/config/manager/kustomization.yaml index 8df12195ff..92cf1f7e18 100644 --- a/operator/config/manager/kustomization.yaml +++ b/operator/config/manager/kustomization.yaml @@ -12,5 +12,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: docker.io/odubajdt/klo - newTag: latest + newName: thschue/lifecycle-controller + newTag: 0.0.11 diff --git a/operator/config/rbac/keptntask_editor_role.yaml b/operator/config/rbac/keptntask_editor_role.yaml new file mode 100644 index 0000000000..b2d8ddfa1f --- /dev/null +++ b/operator/config/rbac/keptntask_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit keptntasks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptntask-editor-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks/status + verbs: + - get diff --git a/operator/config/rbac/keptntask_viewer_role.yaml b/operator/config/rbac/keptntask_viewer_role.yaml new file mode 100644 index 0000000000..e16fff25b6 --- /dev/null +++ b/operator/config/rbac/keptntask_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view keptntasks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptntask-viewer-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks + verbs: + - get + - list + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks/status + verbs: + - get diff --git a/operator/config/rbac/keptntaskdefinition_editor_role.yaml b/operator/config/rbac/keptntaskdefinition_editor_role.yaml new file mode 100644 index 0000000000..970b52e8b6 --- /dev/null +++ b/operator/config/rbac/keptntaskdefinition_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit keptntaskdefinitions. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptntaskdefinition-editor-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions/status + verbs: + - get diff --git a/operator/config/rbac/keptntaskdefinition_viewer_role.yaml b/operator/config/rbac/keptntaskdefinition_viewer_role.yaml new file mode 100644 index 0000000000..b9c03978fd --- /dev/null +++ b/operator/config/rbac/keptntaskdefinition_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view keptntaskdefinitions. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: keptntaskdefinition-viewer-role +rules: +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions + verbs: + - get + - list + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions/status + verbs: + - get diff --git a/operator/config/rbac/role.yaml b/operator/config/rbac/role.yaml index 565cd59787..9230339f19 100644 --- a/operator/config/rbac/role.yaml +++ b/operator/config/rbac/role.yaml @@ -25,6 +25,17 @@ rules: - create - delete - get + - list +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - get + - list + - update + - watch - apiGroups: - "" resources: @@ -84,6 +95,58 @@ rules: - get - patch - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions/finalizers + verbs: + - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntaskdefinitions/status + verbs: + - get + - patch + - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks/finalizers + verbs: + - update +- apiGroups: + - lifecycle.keptn.sh + resources: + - keptntasks/status + verbs: + - get + - patch + - update - apiGroups: - lifecycle.keptn.sh resources: diff --git a/operator/config/samples/function_execution/task_schedule.yaml b/operator/config/samples/function_execution/task_schedule.yaml new file mode 100644 index 0000000000..d54b6a7db4 --- /dev/null +++ b/operator/config/samples/function_execution/task_schedule.yaml @@ -0,0 +1,13 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: scheduled-deployment +spec: + taskDefinition: schedule-upstream-deployment + workload: my-workload + workloadVersion: "1.0" + application: my-app + parameters: + map: + targetDate: "2022-04-16T06:55:31.820Z" + diff --git a/operator/config/samples/function_execution/task_slack.yaml b/operator/config/samples/function_execution/task_slack.yaml new file mode 100644 index 0000000000..fa44bd7e7d --- /dev/null +++ b/operator/config/samples/function_execution/task_slack.yaml @@ -0,0 +1,12 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: slack-deployment-notification +spec: + taskDefinition: slack-notification-inline + workload: my-workload + workloadVersion: "1.0" + application: my-app + secureParameters: + secret: slack-notification + diff --git a/operator/config/samples/function_execution/taskdefinition_function.yaml b/operator/config/samples/function_execution/taskdefinition_function.yaml new file mode 100644 index 0000000000..3c7ccdee6c --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function.yaml @@ -0,0 +1,11 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: slack-notification-deployment-1 +spec: + function: + functionRef: + name: slack-notification + secureParameters: + secret: slack_secret + diff --git a/operator/config/samples/function_execution/taskdefinition_function_configmap.yaml b/operator/config/samples/function_execution/taskdefinition_function_configmap.yaml new file mode 100644 index 0000000000..e5045f321b --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_configmap.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: scheduled-deployment +spec: + function: + configMapRef: + name: scheduled-deployment-cm-1 + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: scheduled-deployment-1 +data: + code: | + let text = Deno.env.get("DATA"); + let data; + if (text != "") { + data = JSON.parse(text); + } + + let targetDate = new Date(data.targetDate) + let dateTime = new Date(); + + if(targetDate < dateTime){ + console.log("Date has passed - ok"); + Deno.exit(0); + } else { + console.log("It's too early - failing"); + Deno.exit(1); + } + + console.log(targetDate); + + diff --git a/operator/config/samples/function_execution/taskdefinition_function_inline.yaml b/operator/config/samples/function_execution/taskdefinition_function_inline.yaml new file mode 100644 index 0000000000..0c118968b2 --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_inline.yaml @@ -0,0 +1,28 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: scheduled-deployment +spec: + function: + inlineRef: + code: | + let text = Deno.env.get("DATA"); + let data; + if (text != "") { + data = JSON.parse(text); + } + + let targetDate = new Date(data.targetDate) + let dateTime = new Date(); + + if(targetDate < dateTime){ + console.log("Date has passed - ok"); + Deno.exit(0); + } else { + console.log("It's too early - failing"); + Deno.exit(1); + } + + console.log(targetDate); + + diff --git a/operator/config/samples/function_execution/taskdefinition_function_inline_slack.yaml b/operator/config/samples/function_execution/taskdefinition_function_inline_slack.yaml new file mode 100644 index 0000000000..3e89ab63e5 --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_inline_slack.yaml @@ -0,0 +1,23 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: slack-notification-inline +spec: + function: + inlineRef: + code: | + let text = Deno.env.get("SECURE_DATA"); + let data; + if (text != undefined) { + data = JSON.parse(text); + } + + const body = `{"text": "${data.text}"}`; + console.log(body) + let resp = await fetch("https://hooks.slack.com/services/" + data.slack_hook, { + method: "POST", + body, + }); + + console.log(resp) + diff --git a/operator/config/samples/function_execution/taskdefinition_function_upstream.yaml b/operator/config/samples/function_execution/taskdefinition_function_upstream.yaml new file mode 100644 index 0000000000..29af0756d8 --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_upstream.yaml @@ -0,0 +1,9 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: slack-notification +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn-sandbox/lifecycle-controller/main/functions-runtime/samples/ts/slack.ts + diff --git a/operator/config/samples/function_execution/taskdefinition_function_upstream_scheduler.yaml b/operator/config/samples/function_execution/taskdefinition_function_upstream_scheduler.yaml new file mode 100644 index 0000000000..d32f9c1b74 --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_upstream_scheduler.yaml @@ -0,0 +1,9 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: schedule-upstream-deployment +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn-sandbox/lifecycle-controller/main/functions-runtime/samples/ts/schedule.ts + diff --git a/operator/config/samples/function_execution/taskdefinition_function_upstream_slack.yaml b/operator/config/samples/function_execution/taskdefinition_function_upstream_slack.yaml new file mode 100644 index 0000000000..29af0756d8 --- /dev/null +++ b/operator/config/samples/function_execution/taskdefinition_function_upstream_slack.yaml @@ -0,0 +1,9 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: slack-notification +spec: + function: + httpRef: + url: https://raw.githubusercontent.com/keptn-sandbox/lifecycle-controller/main/functions-runtime/samples/ts/slack.ts + diff --git a/operator/config/samples/lifecycle_v1alpha1_application.yaml b/operator/config/samples/lifecycle_v1alpha1_application.yaml index 7faf08cd6e..654c36b512 100644 --- a/operator/config/samples/lifecycle_v1alpha1_application.yaml +++ b/operator/config/samples/lifecycle_v1alpha1_application.yaml @@ -2,5 +2,8 @@ apiVersion: lifecycle.keptn.sh/v1alpha1 kind: Application metadata: name: application-sample + annotations: + keptn.sh/pre-deployment-tasks: scheduled-deployment + keptn.sh/post-deployment-tasks: slack-notification-deployment-1 spec: # TODO(user): Add fields here diff --git a/operator/config/samples/lifecycle_v1alpha1_keptntask.yaml b/operator/config/samples/lifecycle_v1alpha1_keptntask.yaml new file mode 100644 index 0000000000..e2efbbf2a6 --- /dev/null +++ b/operator/config/samples/lifecycle_v1alpha1_keptntask.yaml @@ -0,0 +1,6 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTask +metadata: + name: keptntask-sample +spec: + # TODO(user): Add fields here diff --git a/operator/config/samples/lifecycle_v1alpha1_keptntaskdefinition.yaml b/operator/config/samples/lifecycle_v1alpha1_keptntaskdefinition.yaml new file mode 100644 index 0000000000..fc8ab6f6db --- /dev/null +++ b/operator/config/samples/lifecycle_v1alpha1_keptntaskdefinition.yaml @@ -0,0 +1,6 @@ +apiVersion: lifecycle.keptn.sh/v1alpha1 +kind: KeptnTaskDefinition +metadata: + name: keptntaskdefinition-sample +spec: + # TODO(user): Add fields here diff --git a/operator/controllers/keptntask/controller.go b/operator/controllers/keptntask/controller.go new file mode 100644 index 0000000000..f1c6040318 --- /dev/null +++ b/operator/controllers/keptntask/controller.go @@ -0,0 +1,84 @@ +/* +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 keptntask + +import ( + "context" + "github.com/go-logr/logr" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "time" +) + +// KeptnTaskReconciler reconciles a KeptnTask object +type KeptnTaskReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder + Log logr.Logger +} + +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntasks,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntasks/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntasks/finalizers,verbs=update +//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=create;get;update;list;watch +//+kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=get;list + +func (r *KeptnTaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + r.Log.Info("Reconciling KeptnTask") + task := &klcv1alpha1.KeptnTask{} + + if err := r.Client.Get(ctx, req.NamespacedName, task); err != nil { + if errors.IsNotFound(err) { + // taking down all associated K8s resources is handled by K8s + r.Log.Info("KeptnTask resource not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil + } + r.Log.Error(err, "Failed to get the KeptnTask") + return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil + } + + if task.Status.JobName == "" { + err := r.createJob(ctx, req, task) + if err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}, err + } + } + + if task.Status.Status != klcv1alpha1.TaskSucceeded { + err := r.updateJob(ctx, req, task) + if err != nil { + return ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}, err + } + } + + r.Log.Info("Finished Reconciling KeptnTask") + return ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *KeptnTaskReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&klcv1alpha1.KeptnTask{}). + Owns(&batchv1.Job{}). + Complete(r) +} diff --git a/operator/controllers/keptntask/function_utils.go b/operator/controllers/keptntask/function_utils.go new file mode 100644 index 0000000000..6d5d609528 --- /dev/null +++ b/operator/controllers/keptntask/function_utils.go @@ -0,0 +1,144 @@ +package keptntask + +import ( + "encoding/json" + "fmt" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "math/rand" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +type FunctionExecutionParams struct { + ConfigMap string + Parameters map[string]string + SecureParameters string + URL string +} + +func (r *KeptnTaskReconciler) generateFunctionJob(task *klcv1alpha1.KeptnTask, params FunctionExecutionParams) (*batchv1.Job, error) { + randomId := rand.Intn(99999-10000) + 10000 + jobId := fmt.Sprintf("klc-%s-%s-%s-%d", task.Spec.Application, task.Spec.Workload, task.Spec.WorkloadVersion, randomId) + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: jobId, + Namespace: task.Namespace, + Annotations: map[string]string{ + "keptn.sh/app": task.Spec.Application, + "keptn.sh/workload": task.Spec.Workload, + "keptn.sh/version": task.Spec.WorkloadVersion, + }, + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + RestartPolicy: "OnFailure", + }, + }, + }, + } + err := controllerutil.SetControllerReference(task, job, r.Scheme) + if err != nil { + r.Log.Error(err, "could not set controller reference:") + } + + container := corev1.Container{ + Name: "keptn-function-runner", + Image: "ghcr.io/keptn-sandbox/functions-runtime:main.202209211413", + } + + var envVars []corev1.EnvVar + + if len(params.Parameters) > 0 { + jsonParams, err := json.Marshal(params.Parameters) + if err != nil { + return job, fmt.Errorf("could not marshal parameters") + } + envVars = append(envVars, corev1.EnvVar{Name: "DATA", Value: string(jsonParams)}) + } + + if params.SecureParameters != "" { + envVars = append(envVars, corev1.EnvVar{ + Name: "SECURE_DATA", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: params.SecureParameters}, + Key: "SECURE_DATA", + }, + }, + }) + } + + // Mount the function code if a ConfigMap is provided + // The ConfigMap might be provided manually or created by the TaskDefinition controller + if params.ConfigMap != "" { + envVars = append(envVars, corev1.EnvVar{Name: "SCRIPT", Value: "/var/data/function.ts"}) + + job.Spec.Template.Spec.Volumes = []corev1.Volume{ + { + Name: "function-mount", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: params.ConfigMap, + }, + }, + }, + }, + } + container.VolumeMounts = []corev1.VolumeMount{ + { + Name: "function-mount", + ReadOnly: true, + MountPath: "/var/data/function.ts", + SubPath: "code", + }, + } + } else { + envVars = append(envVars, corev1.EnvVar{Name: "SCRIPT", Value: params.URL}) + } + + container.Env = envVars + job.Spec.Template.Spec.Containers = []corev1.Container{ + container, + } + return job, nil +} + +func (r *KeptnTaskReconciler) parseFunctionTaskDefinition(definition *klcv1alpha1.KeptnTaskDefinition) (FunctionExecutionParams, bool, error) { + params := FunctionExecutionParams{} + + // Firstly check if this task definition has a parent object + hasParent := false + if definition.Spec.Function.FunctionReference != nil { + hasParent = true + } + + if definition.Status.Function.ConfigMap != "" && definition.Spec.Function.HttpReference.Url != "" { + r.Log.Info(fmt.Sprintf("The JobDefinition contains a ConfigMap and a HTTP Reference, ConfigMap is used / Namespace: %s, Name: %s ", definition.Namespace, definition.Name)) + } + + // Check if there is a ConfigMap with the function for this object + if definition.Status.Function.ConfigMap != "" { + params.ConfigMap = definition.Status.Function.ConfigMap + } else { + // If not, check if it has an HTTP reference. If this is also not the case and the object has no parent, something is wrong + if definition.Spec.Function.HttpReference.Url == "" && !hasParent { + return params, false, fmt.Errorf("No ConfigMap specified or HTTP source specified in TaskDefinition) / Namespace: %s, Name: %s ", definition.Namespace, definition.Name) + } + params.URL = definition.Spec.Function.HttpReference.Url + } + + // Check if there are parameters provided + if len(definition.Spec.Function.Parameters.Inline) > 0 { + params.Parameters = definition.Spec.Function.Parameters.Inline + } + + // Check if there is a secret for secret params provided + if definition.Spec.Function.SecureParameters.Secret != "" { + params.SecureParameters = definition.Spec.Function.SecureParameters.Secret + } + return params, hasParent, nil +} diff --git a/operator/controllers/keptntask/job_utils.go b/operator/controllers/keptntask/job_utils.go new file mode 100644 index 0000000000..8af5412a04 --- /dev/null +++ b/operator/controllers/keptntask/job_utils.go @@ -0,0 +1,115 @@ +package keptntask + +import ( + "context" + "fmt" + "github.com/imdario/mergo" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/types" + "reflect" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *KeptnTaskReconciler) createJob(ctx context.Context, req ctrl.Request, task *klcv1alpha1.KeptnTask) error { + jobName := "" + definition, err := r.getTaskDefinition(ctx, task.Spec.TaskDefinition, req.Namespace) + if err != nil { + r.Recorder.Event(task, "Warning", "TaskDefinitionNotFound", fmt.Sprintf("Could not find KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + return err + } + + if !reflect.DeepEqual(definition.Spec.Function, klcv1alpha1.FunctionSpec{}) { + jobName, err = r.createFunctionJob(ctx, req, task, definition) + if err != nil { + return err + } + } + + task.Status.JobName = jobName + task.Status.Status = klcv1alpha1.TaskPending + err = r.Client.Status().Update(ctx, task) + if err != nil { + r.Log.Error(err, "could not update KeptnTask status reference for: "+ task.Name) + } + r.Log.Info("updated configmap status reference for: " + definition.Name) + return nil +} + +func (r *KeptnTaskReconciler) createFunctionJob(ctx context.Context, req ctrl.Request, task *klcv1alpha1.KeptnTask, definition *klcv1alpha1.KeptnTaskDefinition) (string, error) { + params, hasParent, err := r.parseFunctionTaskDefinition(definition) + var parentJobParams FunctionExecutionParams + if err != nil { + return "", err + } + if hasParent { + parentDefinition, err := r.getTaskDefinition(ctx, definition.Spec.Function.FunctionReference.Name, req.Namespace) + if err != nil { + r.Recorder.Event(task, "Warning", "TaskDefinitionNotFound", fmt.Sprintf("Could not find KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + return "", err + } + parentJobParams, _, err = r.parseFunctionTaskDefinition(parentDefinition) + if err != nil { + return "", err + } + err = mergo.Merge(¶ms, parentJobParams) + if err != nil { + r.Recorder.Event(task, "Warning", "TaskDefinitionMergeFailure", fmt.Sprintf("Could not merge KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + return "", err + } + } + + if len(task.Spec.Parameters.Inline) > 0 { + err = mergo.Merge(¶ms.Parameters, task.Spec.Parameters.Inline) + if err != nil { + r.Recorder.Event(task, "Warning", "TaskDefinitionMergeFailure", fmt.Sprintf("Could not merge KeptnTaskDefinition / Namespace: %s, Name: %s ", task.Namespace, task.Spec.TaskDefinition)) + return "", err + } + } + + if task.Spec.SecureParameters.Secret != "" { + params.SecureParameters = task.Spec.SecureParameters.Secret + } + + job, _ := r.generateFunctionJob(task, params) + if err != nil { + return "", err + } + err = r.Client.Create(ctx, job) + if err != nil { + r.Log.Error(err, "could not create job") + r.Recorder.Event(task, "Warning", "JobNotCreated", fmt.Sprintf("Could not create Job / Namespace: %s, Name: %s ", task.Namespace, task.Name)) + return job.Name, err + } + r.Recorder.Event(task, "Normal", "JobCreated", fmt.Sprintf("Created Job / Namespace: %s, Name: %s ", task.Namespace, task.Name)) + return job.Name, nil +} + +func (r *KeptnTaskReconciler) updateJob(ctx context.Context, req ctrl.Request, task *klcv1alpha1.KeptnTask) error { + job, err := r.getJob(ctx, task.Status.JobName, req.Namespace) + if err != nil { + task.Status.JobName = "" + r.Recorder.Event(task, "Warning", "JobReferenceRemoved", fmt.Sprintf("Removed Job Reference as Job could not be found / Namespace: %s, TaskName: %s ", task.Namespace, task.Name)) + err = r.Client.Status().Update(ctx, task) + if err != nil { + r.Log.Error(err, "could not remove job reference for: "+task.Name) + } + return err + } + if job.Status.Succeeded > 0 { + task.Status.Status = klcv1alpha1.TaskSucceeded + err = r.Client.Status().Update(ctx, task) + if err != nil { + r.Log.Error(err, "could not update job status for: "+task.Name) + } + } + return nil +} +func (r *KeptnTaskReconciler) getJob(ctx context.Context, jobName string, namespace string) (*batchv1.Job, error) { + job := &batchv1.Job{} + err := r.Client.Get(ctx, types.NamespacedName{Name: jobName, Namespace: namespace}, job) + if err != nil { + return job, err + } + return job, nil +} diff --git a/operator/controllers/keptntask/task_utils.go b/operator/controllers/keptntask/task_utils.go new file mode 100644 index 0000000000..790705ec02 --- /dev/null +++ b/operator/controllers/keptntask/task_utils.go @@ -0,0 +1,16 @@ +package keptntask + +import ( + "context" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + "k8s.io/apimachinery/pkg/types" +) + +func (r *KeptnTaskReconciler) getTaskDefinition(ctx context.Context, definitionName string, namespace string) (*klcv1alpha1.KeptnTaskDefinition, error) { + definition := &klcv1alpha1.KeptnTaskDefinition{} + err := r.Client.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: namespace}, definition) + if err != nil { + return definition, err + } + return definition, nil +} diff --git a/operator/controllers/keptntaskdefinition/controller.go b/operator/controllers/keptntaskdefinition/controller.go new file mode 100644 index 0000000000..cc33558b76 --- /dev/null +++ b/operator/controllers/keptntaskdefinition/controller.go @@ -0,0 +1,78 @@ +/* +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 keptntaskdefinition + +import ( + "context" + "github.com/go-logr/logr" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/tools/record" + "reflect" + "time" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// KeptnTaskDefinitionReconciler reconciles a KeptnTaskDefinition object +type KeptnTaskDefinitionReconciler struct { + client.Client + Scheme *runtime.Scheme + Log logr.Logger + Recorder record.EventRecorder +} + +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntaskdefinitions,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntaskdefinitions/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=lifecycle.keptn.sh,resources=keptntaskdefinitions/finalizers,verbs=update +//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=create;get;update;list;watch + +func (r *KeptnTaskDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + r.Log.Info("Reconciling KeptnTaskDefinition") + + definition := &klcv1alpha1.KeptnTaskDefinition{} + + if err := r.Client.Get(ctx, req.NamespacedName, definition); err != nil { + if errors.IsNotFound(err) { + // taking down all associated K8s resources is handled by K8s + r.Log.Info("KeptnTaskDefinition resource not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil + } + r.Log.Error(err, "Failed to get the KeptnTaskDefinition") + return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil + } + + if !reflect.DeepEqual(definition.Spec.Function, klcv1alpha1.FunctionSpec{}) { + err := r.reconcileFunction(ctx, req, definition) + if err != nil { + return ctrl.Result{}, nil + } + } + r.Log.Info("Finished Reconciling KeptnTaskDefinition") + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *KeptnTaskDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&klcv1alpha1.KeptnTaskDefinition{}). + Owns(&corev1.ConfigMap{}). + Complete(r) +} diff --git a/operator/controllers/keptntaskdefinition/reconcile_function.go b/operator/controllers/keptntaskdefinition/reconcile_function.go new file mode 100644 index 0000000000..0104260940 --- /dev/null +++ b/operator/controllers/keptntaskdefinition/reconcile_function.go @@ -0,0 +1,109 @@ +package keptntaskdefinition + +import ( + "context" + "fmt" + klcv1alpha1 "github.com/keptn-sandbox/lifecycle-controller/operator/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "reflect" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func (r *KeptnTaskDefinitionReconciler) reconcileFunction(ctx context.Context, req ctrl.Request, definition *klcv1alpha1.KeptnTaskDefinition) error { + if definition.Spec.Function.Inline != nil { + err := r.reconcileFunctionInline(ctx, req, definition) + if err != nil { + return err + } + } + if definition.Spec.Function.ConfigMapReference != nil { + err := r.reconcileFunctionConfigMap(ctx, req, definition) + if err != nil { + return err + } + } + return nil +} + +func (r *KeptnTaskDefinitionReconciler) reconcileFunctionInline(ctx context.Context, req ctrl.Request, definition *klcv1alpha1.KeptnTaskDefinition) error { + cmIsNew := false + functionSpec := definition.Spec.Function + functionName := "keptnfn-" + definition.Name + + cm, err := r.getFunctionConfigMap(ctx, functionName, req.Namespace) + if err != nil { + if errors.IsNotFound(err) { + cmIsNew = true + } else { + return fmt.Errorf("could not get function configMap: %w", err) + } + } + + functionCm := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: functionName, + Namespace: definition.Namespace, + }, + Data: map[string]string{ + "code": functionSpec.Inline.Code, + }, + } + err = controllerutil.SetControllerReference(definition, &functionCm, r.Scheme) + if err != nil { + r.Log.Error(err, "could not set controller reference for ConfigMap: "+functionCm.Name) + } + + if cmIsNew { + err := r.Client.Create(ctx, &functionCm) + if err != nil { + r.Recorder.Event(definition, "Warning", "ConfigMapNotCreated", fmt.Sprintf("Could not create configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + return err + } + r.Recorder.Event(definition, "Normal", "ConfigMapCreated", fmt.Sprintf("Created configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + + } else { + if !reflect.DeepEqual(cm, functionCm) { + err := r.Client.Update(ctx, &functionCm) + if err != nil { + r.Recorder.Event(definition, "Warning", "ConfigMapNotUpdated", fmt.Sprintf("Could not update configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + return err + } + r.Recorder.Event(definition, "Normal", "ConfigMapUpdated", fmt.Sprintf("Updated configmap / Namespace: %s, Name: %s ", functionCm.Namespace, functionCm.Name)) + } + } + + definition.Status.Function.ConfigMap = functionCm.Name + err = r.Client.Status().Update(ctx, definition) + if err != nil { + r.Log.Error(err, "could not update configmap status reference for: "+definition.Name) + return err + } + r.Log.Info("updated configmap status reference for: " + definition.Name) + return nil +} + +func (r *KeptnTaskDefinitionReconciler) reconcileFunctionConfigMap(ctx context.Context, req ctrl.Request, definition *klcv1alpha1.KeptnTaskDefinition) error { + if definition.Spec.Function.ConfigMapReference.Name != definition.Status.Function.ConfigMap { + definition.Status.Function.ConfigMap = definition.Spec.Function.ConfigMapReference.Name + err := r.Client.Status().Update(ctx, definition) + if err != nil { + r.Log.Error(err, "could not update configmap status reference for: "+definition.Name) + return err + } + r.Log.Info("updated configmap status reference for: " + definition.Name) + } + return nil +} + +func (r *KeptnTaskDefinitionReconciler) getFunctionConfigMap(ctx context.Context, functionName string, namespace string) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{} + err := r.Client.Get(ctx, types.NamespacedName{Name: functionName, Namespace: namespace}, cm) + if err != nil { + return cm, err + } + return cm, nil +} diff --git a/operator/go.mod b/operator/go.mod index 7fd00da828..dde5523bc7 100644 --- a/operator/go.mod +++ b/operator/go.mod @@ -3,6 +3,7 @@ module github.com/keptn-sandbox/lifecycle-controller/operator go 1.18 require ( + github.com/go-logr/logr v1.2.0 github.com/google/uuid v1.1.2 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.18.1 @@ -29,7 +30,6 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v1.2.0 // indirect github.com/go-logr/zapr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect diff --git a/operator/main.go b/operator/main.go index 200cd1f977..8295dbcb86 100644 --- a/operator/main.go +++ b/operator/main.go @@ -18,6 +18,8 @@ package main import ( "flag" + "github.com/keptn-sandbox/lifecycle-controller/operator/controllers/keptntask" + "github.com/keptn-sandbox/lifecycle-controller/operator/controllers/keptntaskdefinition" "os" @@ -47,7 +49,6 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(lifecyclev1alpha1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -130,6 +131,24 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ServiceRun") os.Exit(1) } + if err = (&keptntask.KeptnTaskReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("KeptnTask Controller"), + Recorder: mgr.GetEventRecorderFor("keptntask-controller"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KeptnTask") + os.Exit(1) + } + if err = (&keptntaskdefinition.KeptnTaskDefinitionReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.Log.WithName("KeptnTaskDefinition Controller"), + Recorder: mgr.GetEventRecorderFor("keptntaskdefinition-controller"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KeptnTaskDefinition") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {