diff --git a/.github/.kubescape/controls-inputs.json b/.github/.kubescape/controls-inputs.json index 8ba5106cf2..fd2e21e9a3 100644 --- a/.github/.kubescape/controls-inputs.json +++ b/.github/.kubescape/controls-inputs.json @@ -5,7 +5,7 @@ "*.ghcr.io", ".*azurecr.io", "docker.io", - "ghcr.keptn.sh" + "ghcr.io" ], "max_critical_vulnerabilities": [ "5" diff --git a/.github/actions/deploy-klt-on-cluster/action.yml b/.github/actions/deploy-klt-on-cluster/action.yml index 35df992e44..aa64cd65d6 100644 --- a/.github/actions/deploy-klt-on-cluster/action.yml +++ b/.github/actions/deploy-klt-on-cluster/action.yml @@ -61,7 +61,7 @@ runs: run: | echo "Installing KLT using manifests" sed -i 's/imagePullPolicy: Always/imagePullPolicy: Never/g' ~/download/artifacts/lifecycle-operator-manifest-test/release.yaml - sed -i 's/ghcr.keptn.sh\/keptn\/functions-runtime:.*/localhost:5000\/keptn\/functions-runtime:${{ inputs.functions_runtime_tag }}/g' \ + sed -i 's/ghcr.io\/keptn\/functions-runtime:.*/localhost:5000\/keptn\/functions-runtime:${{ inputs.functions_runtime_tag }}/g' \ ~/download/artifacts/lifecycle-operator-manifest-test/release.yaml kubectl create namespace keptn-lifecycle-toolkit-system kubectl apply -f ~/download/artifacts/lifecycle-operator-manifest-test diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 181a5d69e3..806961b7ec 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -17,7 +17,7 @@ on: env: GO_VERSION: "~1.20" # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools - CONTROLLER_TOOLS_VERSION: "v0.11.4" + CONTROLLER_TOOLS_VERSION: "v0.12.0" ENVTEST_K8S_VERSION: "1.24.2" SCHEDULER_COMPATIBLE_K8S_VERSION: "v0.24.3" defaults: @@ -238,7 +238,7 @@ jobs: - name: Generate helm charts env: - RELEASE_REGISTRY: ghcr.keptn.sh/keptn + RELEASE_REGISTRY: ghcr.io/keptn run: make helm-package - name: Copy charts from klt to helm repo diff --git a/.github/workflows/helm-checks.yaml b/.github/workflows/helm-checks.yaml index ce11ee01dc..91085570e5 100644 --- a/.github/workflows/helm-checks.yaml +++ b/.github/workflows/helm-checks.yaml @@ -7,7 +7,7 @@ on: env: GO_VERSION: "~1.20" # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools - CONTROLLER_TOOLS_VERSION: "v0.11.4" + CONTROLLER_TOOLS_VERSION: "v0.12.0" ENVTEST_K8S_VERSION: "1.24.2" SCHEDULER_COMPATIBLE_K8S_VERSION: "v0.24.3" defaults: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 938892936f..a85b6920fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ defaults: env: GO_VERSION: "~1.20" # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools - CONTROLLER_TOOLS_VERSION: "v0.11.4" + CONTROLLER_TOOLS_VERSION: "v0.12.0" SCHEDULER_COMPATIBLE_K8S_VERSION: "v0.24.3" jobs: @@ -79,7 +79,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Cosign - uses: sigstore/cosign-installer@v3.0.2 + uses: sigstore/cosign-installer@v3.0.5 - name: Build Docker Image id: docker_build_image @@ -112,7 +112,7 @@ jobs: ${{ env.IMAGE_NAME }}@${{ env.IMAGE_DIGEST }} - name: Generate SBOM - uses: anchore/sbom-action@v0.14.1 + uses: anchore/sbom-action@v0.14.2 with: image: ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} artifact-name: sbom-${{ matrix.config.name }} @@ -162,7 +162,7 @@ jobs: - name: Create manifests env: - RELEASE_REGISTRY: ghcr.keptn.sh/keptn + RELEASE_REGISTRY: ghcr.io/keptn CHART_APPVERSION: ${{ needs.release-please.outputs.tag_name }} run: | cd scheduler diff --git a/.github/workflows/security-scans.yml b/.github/workflows/security-scans.yml index 81d9349d70..dd88c12b46 100644 --- a/.github/workflows/security-scans.yml +++ b/.github/workflows/security-scans.yml @@ -123,7 +123,7 @@ jobs: - name: KICS Scan if: matrix.tool == 'kics' - uses: Checkmarx/kics-github-action@v1.6.3 + uses: Checkmarx/kics-github-action@v1.7.0 with: path: scans config_path: .github/kics-config.yml diff --git a/.github/workflows/validate-helm-chart.yml b/.github/workflows/validate-helm-chart.yml index e0e69dca62..2edce0b758 100644 --- a/.github/workflows/validate-helm-chart.yml +++ b/.github/workflows/validate-helm-chart.yml @@ -26,7 +26,7 @@ jobs: - name: Generate helm charts env: - RELEASE_REGISTRY: ghcr.keptn.sh/keptn + RELEASE_REGISTRY: ghcr.io/keptn run: make helm-package - name: Install readme generator @@ -71,7 +71,7 @@ jobs: - name: Generate helm charts env: - RELEASE_REGISTRY: ghcr.keptn.sh/keptn + RELEASE_REGISTRY: ghcr.io/keptn run: make helm-package - name: Compare YAML file changes @@ -81,7 +81,7 @@ jobs: echo "There are no changes in the manifests" else echo "" - echo "Helm charts were not re-generated. Please regenerate them using make helm-package RELEASE_REGISTRY=ghcr.keptn.sh/keptn" + echo "Helm charts were not re-generated. Please regenerate them using make helm-package RELEASE_REGISTRY=ghcr.io/keptn" echo "" echo "=========== Diff ===========" git diff diff --git a/Makefile b/Makefile index 9c8906dea1..729f3ec10c 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ # Image URL to use all building/pushing image targets # renovate: datasource=github-tags depName=kubernetes-sigs/kustomize -KUSTOMIZE_VERSION?=v5.0.1 +KUSTOMIZE_VERSION?=v5.0.3 # renovate: datasource=github-tags depName=helm/helm -HELM_VERSION ?= v3.11.3 +HELM_VERSION ?= v3.12.0 CHART_APPVERSION ?= v0.7.1 # x-release-please-version # renovate: datasource=docker depName=cytopia/yamllint diff --git a/docs/.htmltest.yml b/docs/.htmltest.yml index 3764f4c826..df24819fb8 100644 --- a/docs/.htmltest.yml +++ b/docs/.htmltest.yml @@ -8,4 +8,5 @@ IgnoreDirs: IgnoreURLs: - "linkedin.com" - "localhost" + - "twitter.com" StripQueryString: false diff --git a/docs/content/en/docs/concepts/apps/_index.md b/docs/content/en/docs/concepts/apps/_index.md deleted file mode 100644 index 6a058b22b8..0000000000 --- a/docs/content/en/docs/concepts/apps/_index.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Apps -description: Learn what Keptn Apps are and how to use them -icon: concepts -layout: quickstart -weight: 10 -hidechildren: true # this flag hides all sub-pages in the sidebar-multicard.html ---- - -An App contains information about all workloads and checks associated with an application. -It will use the following structure for the specification of the pre/post deployment and pre/post evaluations checks -that should be executed at app level: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnApp -metadata: - name: podtato-head - namespace: podtato-kubectl -spec: - version: "1.3" - workloads: - - name: podtato-head-left-arm - version: 0.1.0 - - name: podtato-head-left-leg - version: 1.2.3 - postDeploymentTasks: - - post-deployment-hello - preDeploymentEvaluations: - - my-prometheus-definition -``` - -While changes in the workload version will affect only workload checks, a change in the app version will also cause a -new execution of app level checks. - -## Automatic App Discovery - -The Keptn Lifecycle Toolkit also provides the option to automatically discover `KeptnApp`s, based on the -recommended Kubernetes labels `app.kubernetes.io/part-of`, `app.kubernetes.io/name` `app.kubernetes.io/version`. -This allows users to enable the observability features provided by the Lifecycle Toolkit for -their existing applications, without the need for creating any Keptn-related custom resources. - -To enable the automatic discovery of `KeptnApp`s for your existing applications, the following steps will -be required: - -1. Make sure the namespace of your application is enabled to be managed by the Keptn Lifecycle Toolkit, -by adding the annotation `keptn.sh/lifecycle-toolkit: "enabled"` to your namespace. -2. Make sure the following labels and/or annotations are present in the pod template -specs of your Workloads (i.e. `Deployments`/`StatefulSets`/`DaemonSets`/`ReplicaSets`) within your application: - - `app.kubernetes.io/name`: Determines the name of the generated `KeptnWorkload` representing the - Workload. - - `app.kubernetes.io/version`: Determines the version of the `KeptnWorkload` representing the Workload. - - `app.kubernetes.io/part-of`: Determines the name of the generated `KeptnApp` representing your - Application. - All Workloads that share the same value for this label will be consolidated into the same `KeptnApp`. - -As an example, consider the following application, consisting of several deployments, which is going to be -deployed into a KLT-enabled namespace: - -```yaml -apiVersion: v1 -kind: Namespace -metadata: - name: podtato-kubectl - annotations: - keptn.sh/lifecycle-toolkit: "enabled" - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podtato-head-frontend - namespace: podtato-kubectl -spec: - template: - metadata: - labels: - app.kubernetes.io/name: podtato-head-frontend - app.kubernetes.io/part-of: podtato-head - app.kubernetes.io/version: 0.1.0 - spec: - containers: - - name: podtato-head-frontend - image: podtato-head-frontend ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podtato-head-hat - namespace: podtato-kubectl -spec: - replicas: 1 - template: - metadata: - labels: - app.kubernetes.io/name: podtato-head-hat - app.kubernetes.io/part-of: podtato-head - app.kubernetes.io/version: 0.1.1 - spec: - containers: - - name: podtato-head-hat - image: podtato-head-hat -``` - -Applying these resources will then result in the creation of the following `KeptnApp`: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnApp -metadata: - name: podtato-head - namespace: podtato-kubectl - annotations: - app.kubernetes.io/managed-by: "klt" -spec: - version: "" - workloads: - - name: podtato-head-frontend - version: 0.1.0 - - name: podtato-head-hat - version: 1.1.1 -``` - -Due to the creation of this resource, you will now get observability of your application's deployments due to -the OpenTelemetry tracing features provided by the Keptn Lifecycle Toolkit: - -![Application deployment trace](assets/trace.png) diff --git a/docs/content/en/docs/concepts/evaluations/_index.md b/docs/content/en/docs/concepts/evaluations/_index.md deleted file mode 100644 index 7bc275fe64..0000000000 --- a/docs/content/en/docs/concepts/evaluations/_index.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Evaluations -description: Learn what Keptn Evaluations are and how to use them -icon: concepts -layout: quickstart -weight: 10 -hidechildren: true # this flag hides all sub-pages in the sidebar-multicard.html ---- - - -### Keptn Evaluation Definition - -A `KeptnEvaluationDefinition` is a CRD used to define evaluation tasks that can be run by the Keptn Lifecycle Toolkit -as part of pre- and post-analysis phases of a workload or application. -`KeptnEvaluationDefinition` resource can be created in the namespace where the application is running, or -in the default KLT namespace, which will be the fallback option for the system to search. - -A Keptn evaluation definition looks like the following: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha3 -kind: KeptnEvaluationDefinition -metadata: - name: my-prometheus-evaluation - namespace: example -spec: - source: prometheus - objectives: - - keptnMetricRef: - name: available-cpus - namespace: example - evaluationTarget: ">1" - - keptnMetricRef: - name: cpus-throttling - namespace: example - evaluationTarget: "<0.01" -``` - -A `KeptnEvaluationDefinition` references one or more [`KeptnMetric`s](../metrics/). -If multiple `KeptnMetric`s are used, the Keptn Lifecycle Toolkit will consider the -evaluation successful if **all** metrics are respecting their `evaluationTarget`. diff --git a/docs/content/en/docs/concepts/tasks/_index.md b/docs/content/en/docs/concepts/tasks/_index.md deleted file mode 100644 index b49f8ee3c3..0000000000 --- a/docs/content/en/docs/concepts/tasks/_index.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Tasks -description: Learn what Keptn Tasks are and how to use them -icon: concepts -layout: quickstart -weight: 10 -hidechildren: true # this flag hides all sub-pages in the sidebar-multicard.html ---- - -### Keptn Task Definition - -A `KeptnTaskDefinition` is a CRD used to define tasks that can be run by the Keptn Lifecycle Toolkit -as part of pre- and post-deployment phases of a deployment. -`KeptnTaskDefinition` resource can be created in the namespace where the application is running, or -in the default KLT namespace, which will be the fallback option for the system to search. -The task definition is a [Deno](https://deno.land/) script -Please, refer to the [function runtime](https://github.com/keptn/lifecycle-toolkit/tree/main/functions-runtime) for more -information about the runtime. -In the future, we also intend to support other runtimes, especially running a container image directly. - -A task definition can be configured in three different ways: - -- inline -- referring to an HTTP script -- referring to another `KeptnTaskDefinition` - -An inline task definition looks like the following: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: deployment-hello -spec: - function: - inline: - code: | - console.log("Deployment Task has been executed"); -``` - -In the code section, it is possible to define a full-fletched Deno script. - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: hello-keptn-inline -spec: - function: - inline: - code: | - let text = Deno.env.get("DATA"); - let data; - let name; - data = JSON.parse(text); - - name = data.name - console.log("Hello, " + name + " new"); -``` - -The runtime can also fetch the script on the fly from a remote webserver. -For this, the CRD should look like the -following: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: hello-keptn-http -spec: - function: - httpRef: - url: -``` - -An example is -available [here](https://github.com/keptn-sandbox/lifecycle-toolkit-examples/blob/main/sample-app/version-1/app-pre-deploy.yaml) -. - -Finally, `KeptnTaskDefinition` can build on top of other `KeptnTaskDefinition`s. -This is a common use case where a general function can be re-used in multiple places with different parameters. - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: slack-notification-dev -spec: - function: - functionRef: - name: slack-notification - parameters: - map: - textMessage: "This is my configuration" - secureParameters: - secret: slack-token -``` - -## Context - -A context environment variable is available via `Deno.env.get("CONTEXT")`. -It can be used like this: - -```javascript -let context = Deno.env.get("CONTEXT"); - -if (contextdata.objectType == "Application") { - let application_name = contextdata.appName; - let application_version = contextdata.appVersion; -} - -if (contextdata.objectType == "Workload") { - let application_name = contextdata.appName; - let workload_name = contextdata.workloadName; - let workload_version = contextdata.workloadVersion; -} -``` - -## Input Parameters and Secret Handling - -As you might have noticed, Task Definitions also have the possibility to use input parameters. -The Lifecycle Toolkit passes the values defined inside the `map` field as a JSON object. -At the moment, multi-level maps are not supported. -The JSON object can be read through the environment variable `DATA` using `Deno.env.get("DATA");`. -K8s secrets can also be passed to the function using the `secureParameters` field. -Currently only one secret can be passed. -The secret must have a `key` called `SECURE_DATA`. -It can be accessed via the environment variable `Deno.env.get("SECURE_DATA")`. - -For example: - -```yaml -# kubectl create secret generic my-secret --from-literal=SECURE_DATA=foo - -apiVersion: lifecycle.keptn.sh/v1alpha1 -kind: KeptnTaskDefinition -metadata: - name: dummy-task - namespace: "default" -spec: - function: - secureParameters: - secret: my-secret - inline: - code: | - let secret_text = Deno.env.get("SECURE_DATA"); - // secret_text = "foo" -``` - -This methodology supports multiple variables by creating a K8s secret with a JSON string: - -```yaml -# kubectl create secret generic my-secret \ -# --from-literal=SECURE_DATA="{\"foo\": \"bar\", \"foo2\": \"bar2\"}" - -apiVersion: lifecycle.keptn.sh/v1alpha1 -kind: KeptnTaskDefinition -metadata: - name: dummy-task - namespace: "default" -spec: - function: - secureParameters: - secret: my-secret - inline: - code: | - let secret_text = Deno.env.get("SECURE_DATA"); - let secret_text_obj = JSON.parse(secret_text); - // secret_text_obj["foo"] = "bar" - // secret_text_obj["foo2"] = "bar2" -``` - -### Keptn Task - -A Task is responsible for executing the TaskDefinition of a workload. -The execution is done spawning a K8s Job to handle a single Task. -In its state, it keeps track of the current status of the K8s Job created. diff --git a/docs/content/en/docs/concepts/workloads/_index.md b/docs/content/en/docs/concepts/workloads/_index.md index 541be40c1a..f410b9e9bc 100644 --- a/docs/content/en/docs/concepts/workloads/_index.md +++ b/docs/content/en/docs/concepts/workloads/_index.md @@ -7,21 +7,33 @@ weight: 10 hidechildren: true # this flag hides all sub-pages in the sidebar-multicard.html --- -A Workload contains information about which tasks should be performed during the `preDeployment` as well as -the `postDeployment` -phase of a deployment. -In its state it keeps track of the currently active `Workload Instances`, which are responsible -for doing those checks for -a particular instance of a Deployment/StatefulSet/ReplicaSet (e.g. a Deployment of a certain version). +A `KeptnWorkload`resource contains information about +which tasks should be performed during the `preDeployment` +or `postDeployment` phase of a deployment. +In its state, +it keeps track of the currently active `Workload Instances`, +which are responsible for doing those checks +for a particular instance of a Deployment/StatefulSet/ReplicaSet +(e.g. a Deployment of a certain version). -### Keptn Workload Instance +## KeptnWorkload -A Workload Instance is responsible for executing the pre- and post deployment checks of a workload. -In its state, it -keeps track of the current status of all checks, as well as the overall state of -the Pre Deployment phase, which can be used by the scheduler to tell that a pod can be allowed to be placed on a node. -Workload Instances have a reference to the respective Deployment/StatefulSet/ReplicaSet, to check if it has reached the -desired state. -If it detects that the referenced object has reached -its desired state (e.g. all pods of a deployment are up and running), it will be able to tell that -a `PostDeploymentCheck` can be triggered. +A `KeptnWorkload` resource augments a Kubernetes +[Workload](https://kubernetes.io/docs/concepts/workloads/) +with the ability to handle extra phases. +KLT generates the `KeptnWorkload` resource +from metadata information; +it is not necessary to manually create a YAML file that defines it. + +A `KeptnWorkload` instance is responsible for executing +the pre- and post deployment checks of a workload. +In its state, it keeps track of the current status of all checks, +as well as the overall state of the Pre Deployment phase, +which the scheduler can use to determine +whether the deployment should proceed. +`KeptnWorkload` instances refer +to the respective Pod/DeamonSet/StatefulSet/ReplicaSet, +to check whether it has reached the desired state. +If it detects that the referenced object has reached its desired state +(e.g. all pods of a deployment are up and running), +it knows that a `PostDeploymentCheck` can be triggered. diff --git a/docs/content/en/docs/crd-ref/lifecycle/v1alpha3/_index.md b/docs/content/en/docs/crd-ref/lifecycle/v1alpha3/_index.md index 6756faec8a..3aa8e00baa 100644 --- a/docs/content/en/docs/crd-ref/lifecycle/v1alpha3/_index.md +++ b/docs/content/en/docs/crd-ref/lifecycle/v1alpha3/_index.md @@ -47,7 +47,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `name` _string_ | | +| `name` _string_ | Name is the name of the referenced ConfigMap. | @@ -63,8 +63,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `value` _string_ | | -| `message` _string_ | | +| `value` _string_ | Value represents the value of the KeptnMetric being evaluated. | +| `message` _string_ | Message contains additional information about the evaluation of an objective. This can include explanations about why an evaluation has failed (e.g. due to a missed objective), or if there was any error during the evaluation of the objective. | #### FunctionReference @@ -78,7 +78,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `name` _string_ | | +| `name` _string_ | Name is the name of the referenced KeptnTaksDefinition. | #### FunctionSpec @@ -92,12 +92,12 @@ _Appears in:_ | Field | Description | | --- | --- | -| `functionRef` _[FunctionReference](#functionreference)_ | | -| `inline` _[Inline](#inline)_ | | -| `httpRef` _[HttpReference](#httpreference)_ | | -| `configMapRef` _[ConfigMapReference](#configmapreference)_ | | -| `parameters` _[TaskParameters](#taskparameters)_ | | -| `secureParameters` _[SecureParameters](#secureparameters)_ | | +| `functionRef` _[FunctionReference](#functionreference)_ | FunctionReference allows to reference another KeptnTaskDefinition which contains the source code of the function to be executes for KeptnTasks based on this KeptnTaskDefinition. This can be useful when you have multiple KeptnTaskDefinitions that should execute the same logic, but each with different parameters. | +| `inline` _[Inline](#inline)_ | Inline allows to specify the code that should be executed directly in the KeptnTaskDefinition, as a multi-line string. | +| `httpRef` _[HttpReference](#httpreference)_ | HttpReference allows to point to an HTTP URL containing the code of the function. | +| `configMapRef` _[ConfigMapReference](#configmapreference)_ | ConfigMapReference allows to reference a ConfigMap containing the code of the function. When referencing a ConfigMap, the code of the function must be available as a value of the 'code' key of the referenced ConfigMap. | +| `parameters` _[TaskParameters](#taskparameters)_ | Parameters contains parameters that will be passed to the job that executes the task. | +| `secureParameters` _[SecureParameters](#secureparameters)_ | SecureParameters contains secure parameters that will be passed to the job that executes the task. These will be stored and accessed as secrets in the cluster. | #### FunctionStatus @@ -111,7 +111,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `configMap` _string_ | INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file | +| `configMap` _string_ | ConfigMap indicates the ConfigMap in which the function code is stored. | #### HttpReference @@ -125,7 +125,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `url` _string_ | | +| `url` _string_ | Url is the URL containing the code of the function. | #### Inline @@ -139,7 +139,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `code` _string_ | | +| `code` _string_ | Code contains the code of the function. | #### ItemStatus @@ -156,8 +156,8 @@ _Appears in:_ | --- | --- | | `definitionName` _string_ | DefinitionName is the name of the EvaluationDefinition/TaskDefiniton | | `name` _string_ | Name is the name of the Evaluation/Task | -| `startTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta)_ | | -| `endTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta)_ | | +| `startTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta)_ | StartTime represents the time at which the Item (Evaluation/Task) started. | +| `endTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta)_ | EndTime represents the time at which the Item (Evaluation/Task) started. | #### KeptnApp @@ -174,7 +174,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnApp` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnAppSpec](#keptnappspec)_ | | +| `spec` _[KeptnAppSpec](#keptnappspec)_ | Spec describes the desired state of the KeptnApp. | #### KeptnAppCreationRequest @@ -191,7 +191,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnAppCreationRequest` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnAppCreationRequestSpec](#keptnappcreationrequestspec)_ | | +| `spec` _[KeptnAppCreationRequestSpec](#keptnappcreationrequestspec)_ | Spec describes the desired state of the KeptnAppCreationRequest. | #### KeptnAppCreationRequestList @@ -254,13 +254,13 @@ _Appears in:_ | Field | Description | | --- | --- | -| `version` _string_ | | -| `revision` _integer_ | | -| `workloads` _[KeptnWorkloadRef](#keptnworkloadref) array_ | | -| `preDeploymentTasks` _string array_ | | -| `postDeploymentTasks` _string array_ | | -| `preDeploymentEvaluations` _string array_ | | -| `postDeploymentEvaluations` _string array_ | | +| `version` _string_ | Version defines the version of the application. For automatically created KeptnApps, the version is a function of all KeptnWorkloads that are part of the KeptnApp. | +| `revision` _integer_ | Revision can be modified to trigger another deployment of a KeptnApp of the same version. This can be used for restarting a KeptnApp which failed to deploy, e.g. due to a failed preDeploymentEvaluation/preDeploymentTask. | +| `workloads` _[KeptnWorkloadRef](#keptnworkloadref) array_ | Workloads is a list of all KeptnWorkloads that are part of the KeptnApp. | +| `preDeploymentTasks` _string array_ | PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentTasks` _string array_ | PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `preDeploymentEvaluations` _string array_ | PreDeploymentEvaluations is a list of all evaluations to be performed during the pre-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentEvaluations` _string array_ | PostDeploymentEvaluations is a list of all evaluations to be performed during the post-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | @@ -279,7 +279,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnAppVersion` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnAppVersionSpec](#keptnappversionspec)_ | | +| `spec` _[KeptnAppVersionSpec](#keptnappversionspec)_ | Spec describes the desired state of the KeptnAppVersion. | #### KeptnAppVersionList @@ -309,16 +309,16 @@ _Appears in:_ | Field | Description | | --- | --- | -| `version` _string_ | | -| `revision` _integer_ | | -| `workloads` _[KeptnWorkloadRef](#keptnworkloadref) array_ | | -| `preDeploymentTasks` _string array_ | | -| `postDeploymentTasks` _string array_ | | -| `preDeploymentEvaluations` _string array_ | | -| `postDeploymentEvaluations` _string array_ | | -| `appName` _string_ | | -| `previousVersion` _string_ | | -| `traceId` _object (keys:string, values:string)_ | | +| `version` _string_ | Version defines the version of the application. For automatically created KeptnApps, the version is a function of all KeptnWorkloads that are part of the KeptnApp. | +| `revision` _integer_ | Revision can be modified to trigger another deployment of a KeptnApp of the same version. This can be used for restarting a KeptnApp which failed to deploy, e.g. due to a failed preDeploymentEvaluation/preDeploymentTask. | +| `workloads` _[KeptnWorkloadRef](#keptnworkloadref) array_ | Workloads is a list of all KeptnWorkloads that are part of the KeptnApp. | +| `preDeploymentTasks` _string array_ | PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentTasks` _string array_ | PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `preDeploymentEvaluations` _string array_ | PreDeploymentEvaluations is a list of all evaluations to be performed during the pre-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentEvaluations` _string array_ | PostDeploymentEvaluations is a list of all evaluations to be performed during the post-deployment phase of the KeptnApp. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `appName` _string_ | AppName is the name of the KeptnApp. | +| `previousVersion` _string_ | PreviousVersion is the version of the KeptnApp that has been deployed prior to this version. | +| `traceId` _object (keys:string, values:string)_ | TraceId contains the OpenTelemetry trace ID. | @@ -337,7 +337,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnEvaluation` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnEvaluationSpec](#keptnevaluationspec)_ | | +| `spec` _[KeptnEvaluationSpec](#keptnevaluationspec)_ | Spec describes the desired state of the KeptnEvaluation. | #### KeptnEvaluationDefinition @@ -354,7 +354,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnEvaluationDefinition` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnEvaluationDefinitionSpec](#keptnevaluationdefinitionspec)_ | | +| `spec` _[KeptnEvaluationDefinitionSpec](#keptnevaluationdefinitionspec)_ | Spec describes the desired state of the KeptnEvaluationDefinition. | #### KeptnEvaluationDefinitionList @@ -384,7 +384,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `objectives` _[Objective](#objective) array_ | | +| `objectives` _[Objective](#objective) array_ | Objectives is a list of objectives that have to be met for a KeptnEvaluation referencing this KeptnEvaluationDefinition to be successful. | @@ -466,15 +466,15 @@ _Appears in:_ | Field | Description | | --- | --- | -| `workload` _string_ | | -| `workloadVersion` _string_ | | -| `appName` _string_ | | -| `appVersion` _string_ | | -| `evaluationDefinition` _string_ | | -| `retries` _integer_ | | -| `retryInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | | +| `workload` _string_ | Workload defines the KeptnWorkload for which the KeptnEvaluation is done. | +| `workloadVersion` _string_ | WorkloadVersion defines the version of the KeptnWorkload for which the KeptnEvaluation is done. | +| `appName` _string_ | AppName defines the KeptnApp for which the KeptnEvaluation is done. | +| `appVersion` _string_ | AppVersion defines the version of the KeptnApp for which the KeptnEvaluation is done. | +| `evaluationDefinition` _string_ | EvaluationDefinition refers to the name of the KeptnEvaluationDefinition which includes the objectives for the KeptnEvaluation. The KeptnEvaluationDefinition can be located in the same namespace as the KeptnEvaluation, or in the KLT namespace. | +| `retries` _integer_ | Retries indicates how many times the KeptnEvaluation can be attempted in the case of an error or missed evaluation objective, before considering the KeptnEvaluation to be failed. | +| `retryInterval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | RetryInterval specifies the interval at which the KeptnEvaluation is retried in the case of an error or a missed objective. | | `failAction` _string_ | | -| `checkType` _CheckType_ | | +| `checkType` _CheckType_ | Type indicates whether the KeptnEvaluation is part of the pre- or postDeployment phase. | @@ -490,8 +490,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `name` _string_ | | -| `namespace` _string_ | | +| `name` _string_ | Name is the name of the referenced KeptnMetric. | +| `namespace` _string_ | Namespace is the namespace where the referenced KeptnMetric is located. | #### KeptnTask @@ -508,7 +508,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnTask` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnTaskSpec](#keptntaskspec)_ | | +| `spec` _[KeptnTaskSpec](#keptntaskspec)_ | Spec describes the desired state of the KeptnTask. | #### KeptnTaskDefinition @@ -525,7 +525,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnTaskDefinition` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnTaskDefinitionSpec](#keptntaskdefinitionspec)_ | | +| `spec` _[KeptnTaskDefinitionSpec](#keptntaskdefinitionspec)_ | Spec describes the desired state of the KeptnTaskDefinition. | #### KeptnTaskDefinitionList @@ -555,9 +555,9 @@ _Appears in:_ | Field | Description | | --- | --- | -| `function` _[FunctionSpec](#functionspec)_ | | -| `retries` _integer_ | | -| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | | +| `function` _[FunctionSpec](#functionspec)_ | Function contains the definition for the function that is to be executed in KeptnTasks based on the KeptnTaskDefinitions. | +| `retries` _integer_ | Retries specifies how many times a job executing the KeptnTaskDefinition should be restarted in the case of an unsuccessful attempt. | +| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | Timeout specifies the maximum time to wait for the task to be completed successfully. If the task does not complete successfully within this time frame, it will be considered to be failed. | @@ -589,17 +589,17 @@ _Appears in:_ | Field | Description | | --- | --- | -| `workload` _string_ | | -| `workloadVersion` _string_ | | -| `app` _string_ | | -| `appVersion` _string_ | | -| `taskDefinition` _string_ | | -| `context` _[TaskContext](#taskcontext)_ | | -| `parameters` _[TaskParameters](#taskparameters)_ | | -| `secureParameters` _[SecureParameters](#secureparameters)_ | | -| `checkType` _CheckType_ | | -| `retries` _integer_ | | -| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | | +| `workload` _string_ | Workload defines the KeptnWorkload for which the KeptnTask is executed. | +| `workloadVersion` _string_ | WorkloadVersion defines the version of the KeptnWorkload for which the KeptnTask is executed. | +| `app` _string_ | AppName defines the KeptnApp for which the KeptnTask is executed. | +| `appVersion` _string_ | AppVersion defines the version of the KeptnApp for which the KeptnTask is executed. | +| `taskDefinition` _string_ | TaskDefinition refers to the name of the KeptnTaskDefinition which includes the specification for the task to be performed. The KeptnTaskDefinition can be located in the same namespace as the KeptnTask, or in the KLT namespace. | +| `context` _[TaskContext](#taskcontext)_ | Context contains contextual information about the task execution. | +| `parameters` _[TaskParameters](#taskparameters)_ | Parameters contains parameters that will be passed to the job that executes the task. | +| `secureParameters` _[SecureParameters](#secureparameters)_ | SecureParameters contains secure parameters that will be passed to the job that executes the task. These will be stored and accessed as secrets in the cluster. | +| `checkType` _CheckType_ | Type indicates whether the KeptnTask is part of the pre- or postDeployment phase. | +| `retries` _integer_ | Retries indicates how many times the KeptnTask can be attempted in the case of an error before considering the KeptnTask to be failed. | +| `timeout` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#duration-v1-meta)_ | Timeout specifies the maximum time to wait for the task to be completed successfully. If the task does not complete successfully within this time frame, it will be considered to be failed. | @@ -618,7 +618,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnWorkload` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnWorkloadSpec](#keptnworkloadspec)_ | | +| `spec` _[KeptnWorkloadSpec](#keptnworkloadspec)_ | Spec describes the desired state of the KeptnWorkload. | #### KeptnWorkloadInstance @@ -635,7 +635,7 @@ _Appears in:_ | `apiVersion` _string_ | `lifecycle.keptn.sh/v1alpha3` | `kind` _string_ | `KeptnWorkloadInstance` | `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[KeptnWorkloadInstanceSpec](#keptnworkloadinstancespec)_ | | +| `spec` _[KeptnWorkloadInstanceSpec](#keptnworkloadinstancespec)_ | Spec describes the desired state of the KeptnWorkloadInstance. | #### KeptnWorkloadInstanceList @@ -665,16 +665,16 @@ _Appears in:_ | Field | Description | | --- | --- | -| `app` _string_ | | -| `version` _string_ | | -| `preDeploymentTasks` _string array_ | | -| `postDeploymentTasks` _string array_ | | -| `preDeploymentEvaluations` _string array_ | | -| `postDeploymentEvaluations` _string array_ | | -| `resourceReference` _[ResourceReference](#resourcereference)_ | | -| `workloadName` _string_ | | -| `previousVersion` _string_ | | -| `traceId` _object (keys:string, values:string)_ | | +| `app` _string_ | AppName is the name of the KeptnApp containing the KeptnWorkload. | +| `version` _string_ | Version defines the version of the KeptnWorkload. | +| `preDeploymentTasks` _string array_ | PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentTasks` _string array_ | PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `preDeploymentEvaluations` _string array_ | PreDeploymentEvaluations is a list of all evaluations to be performed during the pre-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `postDeploymentEvaluations` _string array_ | PostDeploymentEvaluations is a list of all evaluations to be performed during the post-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `resourceReference` _[ResourceReference](#resourcereference)_ | ResourceReference is a reference to the Kubernetes resource (Deployment, DaemonSet, StatefulSet or ReplicaSet) the KeptnWorkload is representing. | +| `workloadName` _string_ | WorkloadName is the name of the KeptnWorkload. | +| `previousVersion` _string_ | PreviousVersion is the version of the KeptnWorkload that has been deployed prior to this version. | +| `traceId` _object (keys:string, values:string)_ | TraceId contains the OpenTelemetry trace ID. | @@ -699,7 +699,7 @@ KeptnWorkloadList contains a list of KeptnWorkload - +KeptnWorkloadRef refers to a KeptnWorkload that is part of a KeptnApp _Appears in:_ - [KeptnAppSpec](#keptnappspec) @@ -708,8 +708,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `name` _string_ | | -| `version` _string_ | | +| `name` _string_ | Name is the name of the KeptnWorkload. | +| `version` _string_ | Version is the version of the KeptnWorkload. | #### KeptnWorkloadSpec @@ -724,13 +724,13 @@ _Appears in:_ | Field | Description | | --- | --- | -| `app` _string_ | | -| `version` _string_ | | -| `preDeploymentTasks` _string array_ | | -| `postDeploymentTasks` _string array_ | | -| `preDeploymentEvaluations` _string array_ | | -| `postDeploymentEvaluations` _string array_ | | -| `resourceReference` _[ResourceReference](#resourcereference)_ | | +| `app` _string_ | AppName is the name of the KeptnApp containing the KeptnWorkload. | +| `version` _string_ | Version defines the version of the KeptnWorkload. | +| `preDeploymentTasks` _string array_ | PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnApp, or in the KLT namespace. | +| `postDeploymentTasks` _string array_ | PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnTaskDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `preDeploymentEvaluations` _string array_ | PreDeploymentEvaluations is a list of all evaluations to be performed during the pre-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `postDeploymentEvaluations` _string array_ | PostDeploymentEvaluations is a list of all evaluations to be performed during the post-deployment phase of the KeptnWorkload. The items of this list refer to the names of KeptnEvaluationDefinitions located in the same namespace as the KeptnWorkload, or in the KLT namespace. | +| `resourceReference` _[ResourceReference](#resourcereference)_ | ResourceReference is a reference to the Kubernetes resource (Deployment, DaemonSet, StatefulSet or ReplicaSet) the KeptnWorkload is representing. | @@ -746,8 +746,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `keptnMetricRef` _[KeptnMetricReference](#keptnmetricreference)_ | | -| `evaluationTarget` _string_ | | +| `keptnMetricRef` _[KeptnMetricReference](#keptnmetricreference)_ | KeptnMetricRef references the KeptnMetric that should be evaluated. | +| `evaluationTarget` _string_ | EvaluationTarget specifies the target value for the references KeptnMetric. Needs to start with either '<' or '>', followed by the target value (e.g. '<10'). | #### ResourceReference @@ -779,7 +779,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `secret` _string_ | | +| `secret` _string_ | Secret contains the parameters that will be made available to the job executing the KeptnTask via the 'SECRET_DATA' environment variable. The 'SECRET_DATA' environment variable's content will the same as value of the 'SECRET_DATA' key of the referenced secret. | #### TaskContext @@ -793,12 +793,12 @@ _Appears in:_ | Field | Description | | --- | --- | -| `workloadName` _string_ | | -| `appName` _string_ | | -| `appVersion` _string_ | | -| `workloadVersion` _string_ | | -| `taskType` _string_ | | -| `objectType` _string_ | | +| `workloadName` _string_ | WorkloadName the name of the KeptnWorkload the KeptnTask is being executed for. | +| `appName` _string_ | AppName the name of the KeptnApp the KeptnTask is being executed for. | +| `appVersion` _string_ | AppVersion the version of the KeptnApp the KeptnTask is being executed for. | +| `workloadVersion` _string_ | WorkloadVersion the version of the KeptnWorkload the KeptnTask is being executed for. | +| `taskType` _string_ | TaskType indicates whether the KeptnTask is part of the pre- or postDeployment phase. | +| `objectType` _string_ | ObjectType indicates whether the KeptnTask is being executed for a KeptnApp or KeptnWorkload. | #### TaskParameters @@ -813,7 +813,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `map` _object (keys:string, values:string)_ | | +| `map` _object (keys:string, values:string)_ | Inline contains the parameters that will be made available to the job executing the KeptnTask via the 'DATA' environment variable. The 'DATA' environment variable's content will be a json encoded string containing all properties of the map provided. | #### WorkloadStatus @@ -827,6 +827,6 @@ _Appears in:_ | Field | Description | | --- | --- | -| `workload` _[KeptnWorkloadRef](#keptnworkloadref)_ | | +| `workload` _[KeptnWorkloadRef](#keptnworkloadref)_ | Workload refers to a KeptnWorkload that is part of the KeptnAppVersion. | diff --git a/docs/content/en/docs/getting-started/_index.md b/docs/content/en/docs/getting-started/_index.md index 2f6b5ad331..f6ebc7704c 100644 --- a/docs/content/en/docs/getting-started/_index.md +++ b/docs/content/en/docs/getting-started/_index.md @@ -5,25 +5,47 @@ weight: 15 hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html --- -> **Note** -This section is under development. -We welcome your input!** - This section provides excercises to introduce you to the Keptn Lifecycle Toolkit. -Choose the exercise that interest you the most. +Choose the exercise that interests you the most. + +## Introducing the Keptn Lifecycle Toolkit -The [Introducing Keptn Lifecycle Toolkit](https://youtu.be/449HAFYkUlY) -video discusses three common use cases for the Lifecycle Toolkit +This exercise introduces three common use cases for the Lifecycle Toolkit and walks you through the implementation process for each. -The following exercises provide more detailed instructions -for implementing these use cases: +We recommend that you do all three exercises in order +but you can do any of them independently. * [Getting started with Keptn metrics](metrics) * [Standardize access to observability data](observability) -* [Orchestrate deployment checks](orchestrate) +* [Manage the release cycle](orchestrate) + +These exercises are based on the +[simplenode-dev](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd) +example. +You can clone that repo to access it locally +or just look at it for examples +as you implement the functionality "from scratch" +on your local Kubernetes cluster. +The +[README](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/setup/observability/README.md) +file for that repo contains additional information. + +You can run these exercises on an existing Kubernetes cluster +or you can create a new cluster. +For personal study and demonstrations, +these exercises run well on a local Kubernetes cluster. +See [Create Local Kubernetes Cluster](../install/k8s.md/#create-local-kubernetes-cluster). + +Two videos are available to introduce these exercises: + +* [Introducing Keptn Lifecycle Toolkit](https://youtu.be/449HAFYkUlY) +* [Use SLOs and get DORA the Native K8s way!](https://www.youtube.com/watch?v=zeEC0475SOU) + +## Getting started with Lifecycle Toolkit -If you prefer, -[Getting started with Lifecycle Toolkit](generic-gs) -provides an exercise that introduces you to -all the things KLT can do. +The +[KLT End-to-end exercise](generic-gs) +exercise provides a more detailed exercise +for managing the deployment lifecycle +with pre- and post-deployment evaluations and tasks. diff --git a/docs/content/en/docs/getting-started/metrics/_index.md b/docs/content/en/docs/getting-started/metrics/_index.md index df78e07f70..edeac97ea5 100644 --- a/docs/content/en/docs/getting-started/metrics/_index.md +++ b/docs/content/en/docs/getting-started/metrics/_index.md @@ -1,6 +1,291 @@ --- -title: Getting started with Keptn metrics -description: Learn how Keptn metrics enhances your deployment +title: Custom Keptn metrics +description: Enhance your deployment with custom Keptn metrics weight: 25 --- +The Custom Keptn metrics component of the Keptn Lifecycle Toolkit +allows you to define any type of metric +from multiple instances +of any type of data source in your Kubernetes cluster. +You may have deployment tools like Argo, Flux, KEDA, HPA, or Keptn +that need observability data to make automated decisions +such as whether a rollout is good, or whether to scale up or down. + +Your observability data may come +from multiple observability solutions -- +Prometheus, Dynatrace, Datadog and others -- +or may be data that comes directly +from your cloud provider such as AWS, Google, or Azure. +The Keptn Metrics Server unifies and standardizes access to all this data. +Minimal configuration is required +because the Keptn Lifecycle Toolkit hooks directly into Kubernetes primitives. + +The +[Kubernetes metric server](https://github.com/kubernetes-sigs/metrics-server) +requires that you maintain point-to-point integrations +from Argo Rollouts, Flux, KEDA, and HPA. +Each has plugins but it is difficult to maintain them, +especially if you are using multiple tools, +and multible observability platforms, +and multiple instances of some tools or observability platforms. +The Custom Keptn metrics feature unites all these metrics +integrates metrics from all these sources into a single set of metrics. + +## Using this exercise + +This exercise is based on the +[simplenode-dev](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd) +example. +You can clone that repo to access it locally +or just look at it for examples +as you implement the functionality "from scratch" +on your local Kubernetes deployment cluster. + +This is the first of three exercises in the +[Introducing the Keptn Lifecycle Toolkit](../#introducing-the-keptn-lifecycle-toolkit) +series. +After completing this exercise, +you may want to do the other exercises: + +- In [Standardize observability](../observability), + you learn how to standardize access + to the observability data for your cluster. +- In + [Manage release lifecycle](../orchestrate), + you learn how to implement + pre- and post-deployment tasks and evaluations + to orchestrate the flow of all the `workloads` + that are part of your `application`. + +The steps to implement metrics in an existing cluster are: + +1. [Install the Keptn Lifecycle Toolkit](../../install/install.md) +1. Configure the metrics you want to use: + - [Define metrics providers](#define-metrics-providers) + - [Define KeptnMetric information](#define-keptnmetric-information) + - [View available metrics](#view-available-metrics) + +If you want to create your own cluster to run this exercise, +follow the instructions in [Installation](../../install). + +## Define metrics to use + +You need to define the external observability platforms +from which you want to pull data +and then the specific data you want to pull. +This data is pulled and fetched continuously +at an interval you specify for each specific bit of data. +Data is available through the resource and through the data provider itself, +as well as the Kubernetes CLI. + +### Define metrics providers + +Populate a +[KeptnMetricsProvider](../../yaml-crd-ref/metricsprovider.md) +resource for each external observability platform you want to use. + +For our example, we define two observability platforms: + +- `dev-prometheus` +- `dev-dynatrace` + +You can specify a virtually unlimited number of providers, +including multiple instances of each observability platform. +Each one must be assigned a unique name, +identified by the type of platform it is +and the URL of the target server. +If the target server is protected by a `secret`, +provide information about the token and key. + +> Note: The video and example application use an older syntax + of the `KeptnMetricsProvider` and `KeptnMetric` resources. + The syntax shown in this document and the reference page + is correct for v0.7.1 and later. + +Definition of +[dev-prometheus](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-prometheus-provider.yaml) +data source: + +```yaml +kind: KeptnMetricsProvider +metadata: + name: dev-prometheus + namespace: simplenode-dev +spec: + type: prometheus + targetserver: "http://prometheus-k8s-monitoring-svc.cluster.local:9090" +``` + +Definition of the +[dev-dynatrace](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/dynatrace-provider.yaml.tmp) +data source. +Note that the `dev-dynatrace` server is protected by a secret key +so that information is included in the provider definition: + +```yaml +kind: KeptnMetricsProvider +metadata: + name: dev-dynatrace + namespace: simplenode-dev +spec: + type: dynatrace + targetServer: "https://hci34192.live.dynatrace.com" + secretKeyRef + name: dynatrace + key: DT_TOKEN +... +``` + +### Define KeptnMetric information + +The [KeptnMetric](../../yaml-crd-ref/metric.md) resource +defines the information you want to gather, +specified as a query for the particular observability platform +you are using. +You can define any type of metric from any data source. + +In our example, we define two bits of information to retrieve: + +- Number of CPUs, derived from the `dev-prometheus` data platform +- `availability` SLO, derived from the `dev-dynatrace` data platform + +Each of these are configured to fetch data every 10 seconds +but you could configure a different `fetchIntervalSeconds` value +for each metric. + +The +[keptn-metric.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-metric.yaml) +file for our example looks like: + +```yaml +apiVersion: metrics.keptn.sh/v1alpha2 +kind: Keptnmetric +metadata: + name: available-cpus + namespace: simplenode-dev +spec: + provider: + name: dev-prometheus + query: "sum(kube_node_status_cvapacity{resources`cpu`})" + fetchIntervalSeconds: 10 +--- +apiVersion: metrics.keptn.sh/v1alpha2 +kind: Keptnmetric +metadata: + name: availability-slo + namespace: simplenode-dev +spec: + provider: + name: dev-dynatrace + query: "func:slo.availability_simplenodeservice" + fetchIntervalSeconds: 10 +``` + +Note the following: + +- Populate one YAML file per metric + then apply all of them. +- Each metric is assigned a unique `name`. +- The value of the `spec.provider.name` field + must correspond to the name assigned in + the `metadata.name` field of a `KeptnMetricsProvider` resource. +- Information is fetched in on a continuous basis + at a rate specified + by the value of the `spec.fetchIntervalSeconds` field. + +### View available metrics + +Use the following command to view +the metrics that are configured in your cluster. +This example displays the two metrics we configured above: + +```shell +kubectl get KeptnMetrics -A +``` + +```shell +NAMESPACE NAME PROVIDER QUERY +simplenode-dev availability-slo dev-dynatrace func:slo.availability_simplenodeservice +simplenode-dev available-cpus dev-prometheus sum(kube_node_status_capacity{resource=`cpu`}) +``` + +## Run the metrics + +As soon as you define your `KeptnMetricsProvider` and `KeptnMetric` resources, +the Lifecycle Toolkit begins collecting the metrics you defined. +You do not need to do anything else. + +## Observing the metrics + +The metrics can be retrieved +through CRs and through the Kubernetes Metric API. + +The syntax to retrieve metrics from the CR is: + +```shell +kubectl get keptnmetrics.metrics.keptn.sh -n +``` + +For example, the output for the `available-cpus` metric looks like: + +```shell +$ kubectl get keptnmetrics.metrics.keptn.sh -n simplenode-dev available-cpus + +NAME PROVIDER QUERY VALUE +cpu-throttling my-provider sum(kube_node_status_capacity{resource=`cpu`}) 6.000 +``` + +The syntax to retrieve metrics through the Kubernetes API is: + +```yaml +kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces//keptnmetrics.metrics.sh//" +``` + +For example, the output for the `available-cpus` looks like: + +```yaml +$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/simplenode-dev/keptnmetrics.metrics.sh/available-cpus/available-cpus" + +{ + "kind": "MetricValueList", + "apiVersion": "custom.metrics.k8s.io/v1beta2", + "metadata": {}, + "items": [ + { + "describedObject": { + "kind": "KeptnMetric", + "namespace": "simplenode-dev", + "name": "available-cpus", + "apiVersion": "metrics.keptn.sh/v1alpha2" + }, + "metric": { + "name": "available-cpus", + "selector": {} + }, + "timestamp": "2023-05-11T08:05:36Z", + "value": "6" + } + ] +} +``` + +You can also display the metrics graphically using a dashboard such as Grafana. + +## Implementing autoscaling with HPA + +The Kubernetes HorizontalPodAutoscaler (HPA) +uses metrics to provide autoscaling for the cluster. +HPA can retrieve KeptnMetrics and use those metrics to implement HPA. +See +Using the [HorizontalPodAutoscaler](../../implementing/evaluatemetrics.md/#using-the-horizontalpodautoscaler) +for detailed information. + +## Learn more + +To learn more about the Keptn Metrics Server, see: + +- Architecture: + [Keptn Metrics Operator](../../concepts/architecture/components/metrics-operator/) +- More information about implementing Keptn Metrics: + [Keptn Metrics](../../implementing/evaluatemetrics.md/) diff --git a/docs/content/en/docs/getting-started/observability/_index.md b/docs/content/en/docs/getting-started/observability/_index.md index 0072031e09..274dcb2d88 100644 --- a/docs/content/en/docs/getting-started/observability/_index.md +++ b/docs/content/en/docs/getting-started/observability/_index.md @@ -1,6 +1,184 @@ --- -title: Standardize access to observability data -description: Learn how the Keptn Lifecycle Toolkit standardizes access to observability data. +title: Standardize observability +description: How the KLT standardizes access to observability data for Kubernetes deployments weight: 45 --- +The Keptn Lifecycle Toolkit (KLT) makes any Kubernetes deployment observable. +In other words, it creates a distributed, end-to-end trace +of everything Kubernetes does in the context of a Deployment. +It provides this information +for all applications running in your cluster, +and includes information about +everything Kubernetes does in the context of a deployment. +To do this, +Keptn introduces the concept of an `application`, +which is an abstraction that connects multiple +Workloads that logically belong together, +even if they use different deployment strategies. + +This means that: + +- You can readily see why a deployment takes so long + or why it fails, even when using multiple deployment strategies. +- KLT can capture DORA metrics and expose them as OpenTelemetry metrics + +The observability data is an amalgamation of the following: + +- DORA metrics are collected out of the box + when the Lifecycle Toolkit is enabled +- OpenTelemetry runs traces that show + everything that happens in the Kubernetes cluster +- Custom Keptn metrics that you can use to monitor + information from all the data providers configured in your cluster + +All this information can be displayed with dashboard tools +such as Grafana. + +## Using this exercise + +This exercise shows how to standardize access +to the observability data for your cluster. +It is based on the +[simplenode-dev](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd) +example. + +This is the second of three exercises in the +[Introducing the Keptn Lifecycle Toolkit](../#introducing-the-keptn-lifecycle-toolkit) +series: + +- In the + [Getting started with Keptn metrics](../metrics) + exercise, you learn how to define and use Keptn metrics. + You may want to complete that exercise before doing this exercise + although that is not required. +- In + [Manage release lifecycle](../orchestrate), + you learn how to implement + pre- and post-deployment tasks and evaluations + to orchestrate the flow of all the `workloads` + that are part of your `application`. + +This exercise shows how to standardize access +to the observability data for your cluster. + +If you are installing the Keptn Lifecycle Toolkit on an existing cluster +or on a local cluster you are creating for this exercise, +you need to do the following: + +1. Follow the instructions in + [Install and update](../../install) + to install and enable KLT on your cluster. +1. Follow the instructions in + [Integrate KLT with your applications](../../implementing/integrate) + to integrate KLT with your Kubernetes cluster. + This requires the following: + + - Follow the instructions in + [Annotate workload](../../implementing/integrate/#basic-annotations) + to integrate the Lifecycle Toolkit into your Kubernetes cluster + by applying basic annotations to your `Deployment` resource. + - Follow the instructions in + [Define a Keptn application](../../implementing/integrate/#define-a-keptn-application) + to create a Keptn application that aggragates + all the `workloads` for your deployment into a single + [KeptnApp](../../yaml-crd-ref/app.md) resource. + For this exercise, we recommend that you use + [Keptn automatic app discovery](../../implementing/integrate/#use-keptn-automatic-app-discovery) + to automatically generate a Keptn Application. + +## DORA metrics + +DORA metrics are an industry-standard set of measurements; +see the following for a description: + +- [What are DORA Metrics and Why Do They Matter?](https://codeclimate.com/blog/dora-metrics) +- [Are you an Elite DevOps Performer? + Find out with the Four Keys Project](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance) + +DORA metrics provide information such as: + +- How many deployments happened in the last six hours? +- Time between deployments +- Deployment time between versions +- Average time between versions. + +The Keptn Lifecycle Toolkit starts collecting these metrics +as soon as you annotate the `Deployment` resource. +Metrics are collected only for the `Deployment` resources +that are annotated. + +To view DORA metrics, run the following command: + +```shell +kubectl port-forward -n keptn-lifecycle-toolkit-system \ + svc/lifecycle-operator-metrics-service 2222 +``` + +Then view the metrics at: + +```shell +http://localhost:2222/metrics +``` + +DORA metrics are also displayed on Grafana +or whatever dashboard application you choose. +For example: + +![DORA metrics](assets/dynatrace_dora_dashboard.png) + +## Using OpenTelemetry + +The Keptn Lifecycle Toolkit extends the Kubernetes +primitives to create OpenTelemetry data +that connects all your deployment and observability tools +without worrying about where it is stored and where it is managed. +OpenTelemetry traces collect data as Kubernetes is deploying the changes, +which allows you to trace everything done in the context of that deployment. + +- You must have an OpenTelemetry collector installed on your cluster. + See + [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) + for more information. +- Follow the instructions in + [OpenTelemetry observability](../../implementing/otel.md) + to configure where your OpenTelemetry data is sent. + This requires you to define a [KeptnConfig](../../yaml-crd-ref/config.md) resource + that defines the URL and port of the OpenTelemetry collector. + For our example, this is in the + [keptnconfig.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/setup/keptn/keptnconfig.yaml) + file. + +## Keptn metrics + +You can supplement the DORA Metrics and OpenTelemetry information +with information you explicitly define using Keptn metrics. +The +[Getting started with Keptn metrics](../metrics) +exercise discusses how to define Keptn metrics. + +## View the results + +To start feeding observability data for your deployments +onto a dashboard of your choice: + +1. Modify either your `Deployment` or `KeptnApp` resource yaml file + to increment the version number +1. Commit that change to your repository. + +Note that, from the `KeptnApp` YAML file, +you can either increment the version number of the application +(which causes all workloads to be rerun and produce observability data) +or you can increment the version number of a single workload, +(which causes just that workload to be rerun and produce observability data). + +The videos that go with this exercise show how the +DORA, OpenTelemetry, and Keptn metrics information +appears on a Grafana dashboard with +[Jaeger](https://grafana.com/docs/grafana-cloud/data-configuration/metrics/prometheus-config-examples/the-jaeger-authors-jaeger/). + +If you also have the Jaeger extension for Grafana installed on your cluster, +you can view the full end-to-end trace for everything +that happens in your deployment. +For more information, see +[Monitoring Jaeger](https://www.jaegertracing.io/docs/1.45/monitoring/). diff --git a/docs/content/en/docs/getting-started/observability/assets/dynatrace_dora_dashboard.png b/docs/content/en/docs/getting-started/observability/assets/dynatrace_dora_dashboard.png new file mode 100644 index 0000000000..3318a4303f Binary files /dev/null and b/docs/content/en/docs/getting-started/observability/assets/dynatrace_dora_dashboard.png differ diff --git a/docs/content/en/docs/getting-started/orchestrate/_index.md b/docs/content/en/docs/getting-started/orchestrate/_index.md index b87b7241bd..c4ebafb1fa 100644 --- a/docs/content/en/docs/getting-started/orchestrate/_index.md +++ b/docs/content/en/docs/getting-started/orchestrate/_index.md @@ -1,6 +1,185 @@ --- -title: Orchestrate deployment checks -description: Learn how the Keptn Lifecycle Toolkit can orchestrate deployment checks. +title: Manage release lifecycle +description: How KLT orchestrates pre- and post-deployment evaluations and tasks weight: 55 --- +In this exercise, we will configure the Keptn Lifecyle Toolkit +to run deployment checks as part of your deployment. +Whether you are deploying your software with +Argo, Flux, another deployment engine, or even `kubectl apply`, +the Lifecycle Toolkit can do the following: + +* Pre-deploy: Validate external dependencies, + confirm that images are scanned, and so forth + +* Post-deply: Execute tests, notify stakeholders, + promote to the next stage + +* Automatically validate against your SLO (Service Level Objectives) + +KLT sits on top of the Kubernetes scheduler +and can do the following: + +* Trace the deployment from start to end +* KLT is application aware, + so can extend the deployment with tasks and evaluations that + are run either before or after your whole application starts the deployment + or at the individual workload level. +* Validate any Keptn metric, + either pre- or post-deployment, + using the metrics from the Keptn Metrics Server introduced in + [Getting started with Keptn metrics](../metrics). + +This means that you can be sure that the environment is healthy +and has adequate resources before you begin the deployment. +After the deployment succeeds, +use Keptn metrics to confirm that your deployed software is really healthy -- +not just that the pods are running but validate against SLOs +such as performance and user experience. +You can also check for new logs that came in from a log monitoring solution. + +## Using this exercise + +This exercise shows how to implement +pre- and post-deployment evaluations and tasks +for your application. +It is based on the +[simplenode-dev](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd) +example. + +The steps to implement pre- and post-deployment orchestration are: + +1. [Define evaluations to be performed pre- and post-deployment](#define-evaluations-to-be-performed-pre--and-post-deployment) +1. [Define tasks to be performed pre- and post-deployment](#define-tasks-to-be-performed-pre--and-post-deployment) +1. [Integrate evaluations and tasks into the cluster](#integrate-evaluations-and-tasks-into-the-cluster) + +This is the third of three exercises in the +[Introducing the Keptn Lifecycle Toolkit](../#introducing-the-keptn-lifecycle-toolkit) +series. +You may want to complete the other exercises before doing this exercise +although that is not required: + +* In the + [Getting started with Keptn metrics](../metrics) + exercise, you learn how to define and use Keptn metrics. +* In [Standardize observability](../observability), + you learn how to standardize access + to the observability data for your cluster. + +If you are installing the Keptn Lifecycle Toolkit on an existing cluster +or in a local cluster you are creating for this exercise +and did not previously set up your cluster for the +[Standardize observability](../observability) exercise, +you need to do the following: + +1. Follow the instructions in + [Install and update](../../install) + to install and enable KLT on your cluster. +1. Follow the instructions in + [Integrate KLT with your applications](../../implementing/integrate) + to integrate KLT with your Kubernetes cluster: + + * Follow the instructions in + [Annotate workload](../../implementing/integrate/#basic-annotations) + to integrate the Lifecycle Toolkit into your Kubernetes cluster + by applying basic annotations to your `Deployment` resource. + * Follow the instructions in + [Define a Keptn application](../../implementing/integrate/#define-a-keptn-application) + to create a + [KeptnApp](../../yaml-crd-ref/app.md) resource + that includes all workloads on the cluster, + regardless of the tools being used. + For this exercise, we recommend that you + [Use Keptn automatic app discovery](../../implementing/integrate/#use-keptn-automatic-app-discovery) + to automatically generate a Keptn Application. + +## Define evaluations to be performed pre- and post-deployment + +An `evaluation` is a KeptnMetric that has a defined target value. +Evaluations are resources that are defined in a +[KeptinEvaluationDefinition](../../yaml-crd-ref/evaluationdefinition.md) +yaml file. +In our example, evaluations are defined in the +[keptn-evaluations.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-evaluations.yaml) +file. +For example, the definition of the `evaluate-dependencies` evaluation +looks like this: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnEvaluationDefinition +metadata: + name: evaluate-dependencies + namespace: simplenode-dev +spec: + objectives: + - keptnMetricRef: + name: available-cpus + namespace: simplenode-dev + evaluationTarget: ">4" +``` + +You see that the `available-cpus` metric is defined in the +[keptn-metric.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-metric.yaml) +file. +The `evaluationTarget` is set to be `>4`, +so this evaluation makes sure that more than 4 CPUs are available. +You could include objectives and additional metrics in this evaluation. + +## Define tasks to be performed pre- and post-deployment + +Tasks are resources that are defined in a +[KeptnTaskDefinition](../../yaml-crd-ref/taskdefinition.md) +file. +In our example, the tasks are defined in the +[keptn-tasks.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-tasks.yaml) +file +As an example, +we have a `notify` task that composes some markdown text +to be sent as Slack notifications +The `KeptnTaskDefinition` looks like this: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: notify +spec: + function: + inline: + code: | + + secureParameters: + secret: slack-notification +``` + +For more information about sending Slack notifications with KLT, see +[Implement Slack notifications](../../implementing/slack.md). +The code to be executed is expressed as a +[Deno](https://deno.land/) +script, which uses JavaScript syntax. +It can be embedded in the definition file +or pulled in from a remote webserver that is specified. +For this example, the code to be executed is embedded in this file +although, in practice, +this script would probably be located on a remote webserver. + +You can view the actual JavaScript code for the task in the repository. +You see that "context" is important in this code. +This refers to the context in which this code executes -- +for which application, for which version, for which Workload. + +Because the slack server that is required to execute this task +is protected by a secret, the task definition also specifies that secret. + +## Integrate evaluations and tasks into the cluster + +Follow the instructions in +[Annotate workload](../../implementing/integrate/#pre--and-post-deployment-checks) +to integrate the evaluations and tasks you defined +into the cluster +by applying annotations to the `Deployment` resource. +See the +[simplenode-dev-deployment.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/simplenode-dev-deployment.yaml) +file for an example. diff --git a/docs/content/en/docs/implementing/evaluatemetrics.md b/docs/content/en/docs/implementing/evaluatemetrics.md index 0c70c43172..88dfb807d1 100644 --- a/docs/content/en/docs/implementing/evaluatemetrics.md +++ b/docs/content/en/docs/implementing/evaluatemetrics.md @@ -1,5 +1,145 @@ --- -title: Evaluate metrics -description: Define all workloads and checks associated with an application +title: Keptn Metrics +description: Implement Keptn metrics weight: 130 --- + +The Keptn Metrics Operator provides a single entry point +to all metrics in the cluster +and allows you to define metrics based on multiple data platforms +and multiple instances of any data platform. +Metrics are fetched independently +and can be used for an evaluation at workload- and application-level, or for scaling your workloads. + +This data can be displayed on Grafana +or another standard dashboard application that you configure +or can be retrieved using standard Kubernetes commands. + +For an introduction to Keptn metrics, see +[Getting started with Keptn metrics](../getting-started/metrics). + +## Keptn metric basics + +Keptn metrics are implemented with two resources: + +* [KeptnMetric](../yaml-crd-ref/metric.md) -- + define the metric to report +* [KeptnMetricsProvider](../yaml-crd-ref/metricsprovider.md) -- + define the configuration for a data provider + +## Accessing Metrics via the Kubernetes Custom Metrics API + +`KeptnMetrics` can also be retrieved via the Kubernetes Custom Metrics API. + +### Retrieve KeptnMetric values with kubectl + +Use the `kubectl get --raw` command +to retrieve the values of a `KeptnMetric`, as in the following example: + +```shell +$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/podtato-kubectl/keptnmetrics.metrics.sh/keptnmetric-sample/keptnmetric-sample" | jq . + +{ + "kind": "MetricValueList", + "apiVersion": "custom.metrics.k8s.io/v1beta2", + "metadata": {}, + "items": [ + { + "describedObject": { + "kind": "KeptnMetric", + "namespace": "podtato-kubectl", + "name": "keptnmetric-sample", + "apiVersion": "metrics.keptn.sh/v1alpha1" + }, + "metric": { + "name": "keptnmetric-sample", + "selector": { + "matchLabels": { + "app": "frontend" + } + } + }, + "timestamp": "2023-01-25T09:26:15Z", + "value": "10" + } + ] +} +``` + +### Filter on matching labels + +You can filter based on matching labels. +For example, to retrieve all metrics +that are labelled with `app=frontend`, +use the following command: + +```shell +$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2/namespaces/podtato-kubectl/keptnmetrics.metrics.sh/*/*?labelSelector=app%3Dfrontend" | jq . + +{ + "kind": "MetricValueList", + "apiVersion": "custom.metrics.k8s.io/v1beta2", + "metadata": {}, + "items": [ + { + "describedObject": { + "kind": "KeptnMetric", + "namespace": "keptn-lifecycle-toolkit-system", + "name": "keptnmetric-sample", + "apiVersion": "metrics.keptn.sh/v1alpha3" + }, + "metric": { + "name": "keptnmetric-sample", + "selector": { + "matchLabels": { + "app": "frontend" + } + } + }, + "timestamp": "2023-01-25T09:26:15Z", + "value": "10" + } + ] +} +``` + +## Using the HorizontalPodAutoscaler + +Use the Kubernetes Custom Metrics API +to refer to `KeptnMetric` via the +[Kubernetes HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) +(HPA), +as in the following example: + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: podtato-head-entry + namespace: podtato-kubectl +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: podtato-head-entry + minReplicas: 1 + maxReplicas: 10 + metrics: + - type: Object + object: + metric: + name: keptnmetric-sample + describedObject: + apiVersion: metrics.keptn.sh/v1alpha1 + kind: KeptnMetric + name: keptnmetric-sample + target: + type: Value + value: "10" +``` + +See the +[Scaling Kubernetes Workloads based on Dynatrace Metrics](https://www.linkedin.com/pulse/scaling-kubernetes-workloads-based-dynatrace-metrics-keptnproject/) +blog post +for a detailed discussion of doing this with Dynatrace metrics. +The same approach could be used to implement HPA with other data providers. diff --git a/docs/content/en/docs/implementing/evaluations.md b/docs/content/en/docs/implementing/evaluations.md index 982f80b3b2..f35f2005c3 100644 --- a/docs/content/en/docs/implementing/evaluations.md +++ b/docs/content/en/docs/implementing/evaluations.md @@ -1,5 +1,5 @@ --- title: Evaluations description: Understand Keptn evaluations and how to use them -weight: 110 +weight: 150 --- diff --git a/docs/content/en/docs/implementing/integrate.md b/docs/content/en/docs/implementing/integrate.md deleted file mode 100644 index 5aa34f2f98..0000000000 --- a/docs/content/en/docs/implementing/integrate.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Integrate KLT with your applications -description: How to integrate the Keptn Lifecycle Toolkit into your Kubernetes cluster -icon: concepts -layout: quickstart -weight: 45 -hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html ---- - -Use Kubernetes annotations and labels -to integrate the Keptn Lifecycle Toolkit into your Kubernetes cluster. - -The Keptn Lifecycle Toolkit monitors manifests -that have been applied against the Kubernetes API -and reacts if it finds a workload with special annotations/labels. -This is a four-step process: - -* Annotate your workload(s) -* Create a `KeptnApp` custom resource that references those workloads -* Create the `KeptnTaskDefinition`s you need -* Enable the target namespace by annotating it - -## Annotate workload(s) - -For this, you should annotate your -[Workload](https://kubernetes.io/docs/concepts/workloads/) -with (at least) the following annotations: - -```yaml -keptn.sh/app: myAwesomeAppName -keptn.sh/workload: myAwesomeWorkload -keptn.sh/version: myAwesomeWorkloadVersion -``` - -Alternatively, you can use Kubernetes -[Recommended Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) -to annotate your workload: - -```yaml -app.kubernetes.io/part-of: myAwesomeAppName -app.kubernetes.io/name: myAwesomeWorkload -app.kubernetes.io/version: myAwesomeWorkloadVersion -``` - -Note the following: - -* The Keptn Annotations/Labels take precedence - over the Kubernetes recommended labels. -* If the Workload has no version annotation/labels - and the pod has only one container, - the Lifecycle Toolkit takes the image tag as version - (if it is not "latest"). - -This process is demonstrated in the -[Keptn Lifecycle Toolkit: Installation and KeptnTask Creation in Mintes](https://www.youtube.com/watch?v=Hh01bBwZ_qM) -video. - -## Pre- and post-deployment checks - -Further annotations are necessary -to run pre- and post-deployment checks: - -```yaml -keptn.sh/pre-deployment-tasks: verify-infrastructure-problems -keptn.sh/post-deployment-tasks: slack-notification,performance-test -``` - -The value of these annotations are -Keptn [CRDs](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) -called `KeptnTaskDefinition`s. -These CRDs contain re-usable "functions" -that can execute before and after the deployment. -In this example, before the deployment starts, -a check for open problems in your infrastructure is performed. -If everything is fine, the deployment continues and afterward, -a slack notification is sent with the result of the deployment -and a pipeline to run performance tests is invoked. -Otherwise, the deployment is kept in a pending state -until the infrastructure is capable of accepting deployments again. - -A more comprehensive example can be found in our -[examples folder](https://github.com/keptn/lifecycle-toolkit/tree/main/examples/sample-app), -where we use [Podtato-Head](https://github.com/podtato-head/podtato-head) -to run some simple pre-deployment checks. - -To run the example, use the following commands: - -```shell -cd ./examples/podtatohead-deployment/ -kubectl apply -f . -``` - -Afterward, you can monitor the status of the deployment using - -```shell -kubectl get keptnworkloadinstance -n podtato-kubectl -w -``` - -The deployment for a Workload stays in a `Pending` -state until the respective pre-deployment check is completed. -Afterwards, the deployment starts and when it is marked `Succeeded`, -the post-deployment checks start. diff --git a/docs/content/en/docs/implementing/integrate/_index.md b/docs/content/en/docs/implementing/integrate/_index.md new file mode 100644 index 0000000000..d674ecd612 --- /dev/null +++ b/docs/content/en/docs/implementing/integrate/_index.md @@ -0,0 +1,299 @@ +--- +title: Integrate KLT with your applications +description: How to integrate the Keptn Lifecycle Toolkit into your Kubernetes cluster +icon: concepts +layout: quickstart +weight: 45 +hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html +--- + +The Keptn Lifecycle Toolkit works +on top of the default scheduler for the cluster +so it can trace all activities of all deployment workloads on the cluster, +no matter what tool is used for the deployment. +This same mechanism allows KLT to inject pre- and post-deployment checks +into all deployment workloads. +KLT monitors resources +that have been applied into the Kubernetes cluster +and reacts if it finds a workload with special annotations/labels. +The Keptn Lifecycle Toolkit uses metadata +to identify the workloads of interest. + +To integrate KLT with your applications, +you need to populate the metadata it needs +with either Keptn or Kubernetes annotations and labels. + +This requires two steps: + +* [Annotate your workload(s)](#annotate-workloads) +* Define a Keptn application that references those workloads. + You have two options: + + * [Define KeptnApp manually](#define-keptnapp-manually) + for the application + * [Use the Keptn automatic app discovery capability](#use-keptn-automatic-app-discovery) + that enables the observability features provided by the Lifecycle Toolkit + for existing applications, + without requiring you to manually create any KeptnApp resources. + +## Annotate workload(s) + +To annotate your +[Workload](https://kubernetes.io/docs/concepts/workloads/), +you need to set annotations in your Kubernetes +[Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) resource. + +Note that you do not need to explicitly create a `KeptnWorkload`. +KLT monitors your `Deployments`, +[StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/), +and +[ReplicaSets](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/), +and +[DaemonSets](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) +in the namespaces where KLT is enabled. +If KLT finds any of hese resources and the resource has either +the keptn.sh or the kubernetes recommended labels, +it creates a `KeptnWorkload` resource for the version it detects. + +> Note: Annotations are not required if you are only using the + `metrics-operator` component of KLT + to observe Keptn metrics. + +### Basic annotations + +The basic keptn.sh annotations are: + +```yaml +keptn.sh/app: myAwesomeAppName +keptn.sh/workload: myAwesomeWorkload +keptn.sh/version: myAwesomeWorkloadVersion +``` + +Alternatively, you can use Kubernetes +[Recommended Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) +to annotate your workload: + +```yaml +app.kubernetes.io/part-of: myAwesomeAppName +app.kubernetes.io/name: myAwesomeWorkload +app.kubernetes.io/version: myAwesomeWorkloadVersion +``` + +Note the following: + +* The Keptn Annotations/Labels take precedence + over the Kubernetes recommended labels. +* If the Workload has no version annotation/labels + and the pod has only one container, + the Lifecycle Toolkit takes the image tag as version + (if it is not "latest"). + +This process is demonstrated in the +[Keptn Lifecycle Toolkit: Installation and KeptnTask Creation in Mintes](https://www.youtube.com/watch?v=Hh01bBwZ_qM) +video. + +### Pre- and post-deployment checks + +Further annotations are necessary +to run pre- and post-deployment checks: + +```yaml +keptn.sh/pre-deployment-tasks: verify-infrastructure-problems +keptn.sh/post-deployment-tasks: slack-notification,performance-test +``` + +The value of these annotations are +Keptn [resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) +called `KeptnTaskDefinition`s. +These resources contain re-usable "functions" +that can execute before and after the deployment. +In this example, before the deployment starts, +a check for open problems in your infrastructure is performed. +If everything is fine, the deployment continues and afterward, +a slack notification is sent with the result of the deployment +and a pipeline to run performance tests is invoked. +Otherwise, the deployment is kept in a pending state +until the infrastructure is capable of accepting deployments again. + +A more comprehensive example can be found in our +[examples folder](https://github.com/keptn/lifecycle-toolkit/tree/main/examples/sample-app), +where we use [Podtato-Head](https://github.com/podtato-head/podtato-head) +to run some simple pre-deployment checks. + +To run the example, use the following commands: + +```shell +cd ./examples/podtatohead-deployment/ +kubectl apply -f . +``` + +Afterward, you can monitor the status of the deployment using + +```shell +kubectl get keptnworkloadinstance -n podtato-kubectl -w +``` + +The deployment for a Workload stays in a `Pending` +state until the respective pre-deployment check is successfully completed. +Afterwards, the deployment starts and when the workload is deployed, +the post-deployment checks start. + +## Define a Keptn application + +A Keptn application defines the workloads +to be included in your Keptn Application. +It does this by aggregating multiple workloads +that belong to a logical app into a single +[KeptnApp](../../yaml-crd-ref/app.md) +resource. + + You have two options: + +* Create a [KeptnApp](../../yaml-crd-ref/app.md) resource + that references the workloads that should be included + along with any + [KeptnTaskDefinition](../../yaml-crd-ref/taskdefinition.md) + and [KeptnEvaluationDefinition](../../yaml-crd-ref/evaluationdefinition.md) + resources that you want +* Use the Keptn automatic app discovery capability + that enables the observability features provided by the Lifecycle Toolkit + for existing applications, + without requiring you to manually create any KeptnApp resources + +### Define KeptnApp manually + +You can manually create a YAML file for the +[KeptnApp](../../yaml-crd-ref/app.md) resource +that references the workloads to be included +along with any +[KeptnTaskDefinition](../../yaml-crd-ref/taskdefinition.md) +and [KeptnEvaluationDefinition](../../yaml-crd-ref/evaluationdefinition.md) +resources that you want. + +See the +[keptn-app.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-app.yaml.tmp) +file for an example. +You see the `metadata` that names this `KeptnApp` +and identifies the namespace where it lives: + +```yaml +metadata: + name: simpleapp + namespace: simplenode-dev +``` + +You can also see the `spec.workloads` list +that defines the workloads to be included +and any pre-/post-deployment +tasks and evaluations to be performed. +In this simple example, +we only have one workload and one evaluation defined +but most production apps will have multiple workloads, +multiple tasks, and multiple evaluations defined. + +### Use Keptn automatic app discovery + +The Keptn Lifecycle Toolkit provides the option +to automatically discover `KeptnApp`s, +based on the recommended Kubernetes labels `app.kubernetes.io/part-of`, +`app.kubernetes.io/name` `app.kubernetes.io/version`. +Because of the OpenTelemetry tracing features +provided by the Keptn Lifecycle Toolkit, +this enables the observability features for existing applications, +without creating any Keptn-related custom resources. + +To enable the automatic discovery of `KeptnApp`s for your existing applications, +the following steps are required: + +1. Enable KLT for the namespace where your application runs + following the instructions above +1. Make sure the following Kubernetes labels and/or annotations are present + in the pod template specs of your Workloads + (`Deployments`, `StatefulSets`, `DaemonSets`, and `ReplicaSets`) + within your application: + + * `app.kubernetes.io/name`: Determines the name + of the generated `KeptnWorkload` representing the Workload. + * `app.kubernetes.io/version`: Determines the version + of the `KeptnWorkload` representing the Workload. + * `app.kubernetes.io/part-of`: Determines the name + of the generated `KeptnApp` representing your Application. + + All Workloads that share the same value for this label + are consolidated into the same `KeptnApp`. + +As an example, consider the following application, +consisting of several deployments, +which is going to be deployed into a KLT-enabled namespace: + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: podtato-kubectl + annotations: + keptn.sh/lifecycle-toolkit: "enabled" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: podtato-head-frontend + namespace: podtato-kubectl +spec: + template: + metadata: + labels: + app.kubernetes.io/name: podtato-head-frontend + app.kubernetes.io/part-of: podtato-head + app.kubernetes.io/version: 0.1.0 + spec: + containers: + - name: podtato-head-frontend + image: podtato-head-frontend +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: podtato-head-hat + namespace: podtato-kubectl +spec: + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: podtato-head-hat + app.kubernetes.io/part-of: podtato-head + app.kubernetes.io/version: 0.1.1 + spec: + containers: + - name: podtato-head-hat + image: podtato-head-hat +``` + +Applying these resources results in the creation +of the following `KeptnApp` resource: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha2 +kind: KeptnApp +metadata: + name: podtato-head + namespace: podtato-kubectl + annotations: + app.kubernetes.io/managed-by: "klt" +spec: + version: "" + workloads: + - name: podtato-head-frontend + version: 0.1.0 + - name: podtato-head-hat + version: 1.1.1 +``` + +With the `KeptnApp` resource created, +you get observability of your application's deployments +by using the OpenTelemetry tracing features +that are provided by the Keptn Lifecycle Toolkit: + +![Application deployment trace](assets/trace.png) diff --git a/docs/content/en/docs/implementing/integrate/assets/trace.png b/docs/content/en/docs/implementing/integrate/assets/trace.png new file mode 100644 index 0000000000..84254826c9 Binary files /dev/null and b/docs/content/en/docs/implementing/integrate/assets/trace.png differ diff --git a/docs/content/en/docs/implementing/observe.md b/docs/content/en/docs/implementing/observe.md deleted file mode 100644 index b230f42349..0000000000 --- a/docs/content/en/docs/implementing/observe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Implement Observability -description: Learn how to implement observability in your application -weight: 80 ---- diff --git a/docs/content/en/docs/implementing/otel.md b/docs/content/en/docs/implementing/otel.md index 73c883120a..88037c198e 100644 --- a/docs/content/en/docs/implementing/otel.md +++ b/docs/content/en/docs/implementing/otel.md @@ -1,5 +1,41 @@ --- -title: Integrating OpenTelemetry -description: How to integrate OpenTelemetry into your application +title: OpenTelemetry observability +description: How to standardize access to OpenTelemetry observability data weight: 140 --- + +To access OpenTelemetry metrics with the Keptn Lifecycle Toolkit, +you must: + +- Install an OpenTelemetry collector on your cluster. + See + [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) + for more information. +- Apply + [basic annotations](../implementing/integrate/#basic-annotations) + for your `Deployment` resource + to integrate the Lifecycle Toolkit into your Kubernetes cluster. + +KLT begins to collect OpenTelemetry metrics +as soon as the `Deployment` resource +has the basic annotations to integrate KLT in the cluster. + +To expose OpenTelemetry metrics, +define a [KeptnConfig](../yaml-crd-ref/config.md) resource +that has the `spec.OTelCollectorUrl` field populated +with the URL of the OpenTelemetry collector. + +Keptn metrics can be exposed as OpenTelemetry (OTel) metrics +via port `9999` of the KLT metrics-operator. + +To access the metrics, use the following command: + +```shell +kubectl port-forward deployment/metrics-operator 9999 -n keptn-lifecycle-toolkit-system +``` + +You can access the metrics from your browser at: `http://localhost:9999` + +For an introduction to using OpenTelemetry with Keptn metrics, see the +[Standardize observability](../getting-started/observability) +getting started guide. diff --git a/docs/content/en/docs/implementing/slack.md b/docs/content/en/docs/implementing/slack.md index c5e913dadd..c1635b7791 100644 --- a/docs/content/en/docs/implementing/slack.md +++ b/docs/content/en/docs/implementing/slack.md @@ -1,5 +1,5 @@ --- title: Implement Slack notifications description: Learn how to implement Slack notification as a post-deployment task -weight: 80 +weight: 325 --- diff --git a/docs/content/en/docs/implementing/tasks/_index.md b/docs/content/en/docs/implementing/tasks/_index.md new file mode 100644 index 0000000000..d896143eee --- /dev/null +++ b/docs/content/en/docs/implementing/tasks/_index.md @@ -0,0 +1,189 @@ +--- +title: Working with Keptn tasks +description: Learn how to work with Keptn tasks +weight: 90 +hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html +--- + +Keptn tasks are defined in a +[KeptnTaskDefinition](../../yaml-crd-ref/taskdefinition.md/) +resource. +A task definition includes a function +that defines the action taken by that task. +It can be configured in one of three different ways: + +- inline +- referring to an HTTP script +- referring to another `KeptnTaskDefinition` +- referring to a + [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) + resource that is populated with the function to execute + +### Context + +A Kubernetes context is a set of access parameters +that contains a Kubernetes cluster, a user, a namespace, +the application name, workload name, and version. +For more information, see +[Configure Access to Multiple Clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/). + +You may need to include context information in the `function` code +included in the YAML file that defines a +[KeptnTaskDefinition](../../yaml-crd-ref/taskdefinition.md) +resource. +For an example of how to do this, see the +[keptn-tasks.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/keptn-tasks.yaml) +file. + +A context environment variable is available via `Deno.env.get("CONTEXT")`. +It can be used like this: + +```javascript +let context = Deno.env.get("CONTEXT"); + +if (context.objectType == "Application") { + let application_name = contextdata.appName; + let application_version = contextdata.appVersion; +} + +if (context.objectType == "Workload") { + let application_name = contextdata.appName; + let workload_name = contextdata.workloadName; + let workload_version = contextdata.workloadVersion; +} +``` + +## Parameterized functions + +`KeptnTaskDefinition`s can use input parameters. +Simple parameters are passed as a single map of key values, +while the `secret` parameters refer to a single Kubernetes `secret`. + +Consider the following example: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha2 +kind: KeptnTaskDefinition +metadata: + name: slack-notification-dev +spec: + function: + functionRef: + name: slack-notification + parameters: + map: + textMessage: "This is my configuration" + secureParameters: + secret: slack-token +``` + +Note the following about using parameters with functions: + +- The Lifecycle Toolkit passes the values + defined inside the `map` field as a JSON object. +- Multi-level maps are not currently supported. +- The JSON object can be read through the environment variable `DATA` + using `Deno.env.get("DATA");`. +- Currently only one secret can be passed. + The secret must have a `key` called `SECURE_DATA`. + It can be accessed via the environment variable `Deno.env.get("SECURE_DATA")`. + +## Create secret text + +To create a secret to use in a `KeptnTaskDefinition`, +execute this command: + +```shell +kubectl create secret generic my-secret --from-literal=SECURE_DATA=foo +``` + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: dummy-task + namespace: "default" +spec: + function: + secureParameters: + secret: my-secret + inline: + code: | + let secret_text = Deno.env.get("SECURE_DATA"); + // secret_text = "foo" +``` + +To pass multiple variables +you can create a Kubernetes secret using a JSON string: + +```shell +kubectl create secret generic my-secret \ +--from-literal=SECURE_DATA="{\"foo\": \"bar\", \"foo2\": \"bar2\"}" +``` + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: dummy-task + namespace: "default" +spec: + function: + secureParameters: + secret: my-secret + inline: + code: | + let secret_text = Deno.env.get("SECURE_DATA"); + let secret_text_obj = JSON.parse(secret_text); + // secret_text_obj["foo"] = "bar" + // secret_text_obj["foo2"] = "bar2" +``` + +## Pass secrets to a function + +In the previous example, you see that +Kubernetes +[secrets](https://kubernetes.io/docs/concepts/configuration/secret/) +can be passed to the function +using the `secureParameters` field. + +Here, the `secret` value is the name of the Kubernetes secret, +which contains a field with the key `SECURE_DATA`. +The value of that field is then available to the function's runtime +via an environment variable called `SECURE_DATA`. + +For example, if you have a task function that should make use of secret data, +you must first ensure that the secret containing the `SECURE_DATA` key exists +For example: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: deno-demo-secret + namespace: default +type: Opaque +data: + SECURE_DATA: YmFyCg== # base64 encoded string, e.g. 'bar' +``` + +Then, you can make use of that secret as follows: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: deployment-hello + namespace: "default" +spec: + function: + secureParameters: + secret: deno-demo-secret + inline: + code: | + console.log("Deployment Hello Task has been executed"); + + let foo = Deno.env.get('SECURE_DATA'); + console.log(foo); + Deno.exit(0); +``` diff --git a/docs/content/en/docs/install/_index.md b/docs/content/en/docs/install/_index.md index ae2e110d72..70c56ff443 100644 --- a/docs/content/en/docs/install/_index.md +++ b/docs/content/en/docs/install/_index.md @@ -1,26 +1,56 @@ --- -title: Installation +title: Installation and upgrade description: Learn how to install, configure, and upgrade the Keptn Lifecycle Toolkit -icon: concepts -layout: quickstart weight: 15 hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html --- -> **Note** This section is under development. -All material presented here has been reviewed for accuracy -but the content is not yet complete. - This section provides details about how to install and configure the components of the Keptn Lifecycle Toolkit either as a local cluster you use for study, testing, and demonstrations or as part of an existing production cluster. +The steps are: 1. Understand the [Software versions and resources](reqs.md) - that are required -1. [Bring or create your Kubernetes cluster](k8s.md) -1. [Replace the default cert-manager](cert-manager.md) (optional) - This step is only required if you want to replace the default KLT cert-manager - with another cert-manager. -1. [Install the Keptn Lifecycle Controller](install.md) -1. [Upgrade](upgrade.md) to a new version of the Keptn Lifecycle Toolkit + that are required. +1. Be sure that your cluster includes the components discussed in + [Prepare your cluster for KLT](k8s.md/#prepare-your-cluster-for-klt). +1. [Bring or create your Kubernetes cluster](k8s.md). +1. [Replace the default cert-manager](cert-manager.md) (optional). + This step is only required if you want to replace + the default KLT cert-manager with another cert-manager. +1. [Install the Keptn Lifecycle Toolkit](install.md). +1. [Enable Keptn Lifecycle Toolkit](install.md/#enable-klt-for-your-cluster). + This step is not required if you only want to run Keptn Metrics + but is required for all other KLT features. + +1. Run the following command to ensure that your Kuberetes cluster + is ready to implement the Lifecycle Toolkit: + + ```shell + kubectl get pods -n keptn-lifecycle-toolkit-system + ``` + + You should see pods for the following components: + - certificate-operator (or another cert manager) + - lifecycle-operator + - scheduler + - metrics-operator + +Unless you are only using the customized Keptn metrics feature, +you now need to: + +- Follow the instructions in + [Annotate workload](../implementing/integrate/#basic-annotations) + to integrate the Lifecycle Toolkit into your Kubernetes cluster + by applying basic annotations to your `Deployment` resource. +- Follow the instructions in + [Define a Keptn application](../implementing/integrate/#define-a-keptn-application) + to create a Keptn application that aggragates + all the `workloads` for your deployment into a single + [KeptnApp](../yaml-crd-ref/app.md) resource. + +This section also includes: + +1. How to [Upgrade](upgrade.md) + to a new version of the Keptn Lifecycle Toolkit diff --git a/docs/content/en/docs/install/install.md b/docs/content/en/docs/install/install.md index 66499f7d67..f021780190 100644 --- a/docs/content/en/docs/install/install.md +++ b/docs/content/en/docs/install/install.md @@ -1,13 +1,19 @@ --- -title: Install KLT +title: Install and enable KLT description: Install the Keptn Lifecycle Toolkit -icon: concepts -layout: quickstart weight: 35 hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html --- -Two methods are supported for installing the Keptn Lifecycle Toolkit: +The Keptn Lifecycle Toolkit must be installed, enabled, and integrated +into each cluster you want to monitor. +This is because KLT communicates with the Kubernetes scheduler +for tasks such as enforcing checks natively, +stopping a deployment from proceeding when criteria are not met, +doing post-deployment evaluations +and tracing all activities of all deployment workloads on the cluster. + +Two methods are supported for installing the Keptn Lifecycle Toolkit (KLT): * Releases v0.7.0 and later can be installed using the [Helm Chart](#use-helm-chart). @@ -17,6 +23,13 @@ Two methods are supported for installing the Keptn Lifecycle Toolkit: the [manifests](#use-manifests). This is the less-preferred way because it does not support customization. +After KLT is installed, you must +[Enable KLT for your cluster](#enable-klt-for-your-cluster) +in order to run some KLT functionality. + +You are then ready to +[Integrate KLT with your applications](../implementing/integrate). + ## Use Helm Chart Version v0.7.0 and later of the Lifecycle Toolkit @@ -33,15 +46,33 @@ helm upgrade --install keptn klt/klt \ Note that the `helm repo update` command is used for fresh installs as well as for upgrades. -Use the `--version ` flag on the -`helm upgrade --install` command line to specify a different KLT version. +Some helpful hints: -Use the following command sequence to see a list of available versions: +* Use the `--version ` flag on the + `helm upgrade --install` command line to specify a different KLT version. -```shell -helm repo update -helm search repo klt -``` +* Use the following command sequence to see a list of available versions: + + ```shell + helm repo update + helm search repo klt + ``` + +* To verify that the KLT components are installed in your cluster, + run the following command: + + ```shell + kubectl get pods -n keptn-lifecycle-toolkit-system + ``` + + The output shows all components that are running on your system. + +### Modify Helm configuration options + +Helm chart values can be modified before the installation. +This is useful if you want to install only the `metrics-operator` +rather than the full Toolkit +or if you need to change the size of the installation. To modify configuration options, download a copy of the [helm/chart/values.yaml](https://github.com/keptn/lifecycle-toolkit/blob/main/helm/chart/values.yaml) @@ -107,3 +138,25 @@ kubectl wait --for=condition=Available deployment/lifecycle-operator \ ``` The Lifecycle Toolkit and its dependencies are now installed and ready to use. + +## Enable KLT for your cluster + +To enable the Keptn Lifecycle Controller in your cluster, +annotate the Kubernetes +[Namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) +resource. +For an example of this, see +[simplenode-dev-ns.yaml](https://github.com/keptn-sandbox/klt-on-k3s-with-argocd/blob/main/simplenode-dev/simplenode-dev-ns.yaml) +file, which looks like this: + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: simplenode-dev + annotations: + keptn.sh/lifecycle-toolkit: "enabled" +``` + +You see the annotation line `keptn.sh/lifecycle-toolkit: "enabled"`. +This annotation tells the webhook to handle the namespace. diff --git a/docs/content/en/docs/install/k8s.md b/docs/content/en/docs/install/k8s.md index 029a38af2a..a1a5195fda 100644 --- a/docs/content/en/docs/install/k8s.md +++ b/docs/content/en/docs/install/k8s.md @@ -13,13 +13,9 @@ that runs your deployment software. See [Requirements](reqs.md) for information about supported releases and advice about resources required. -You can also create a local cluster using packages -such as KinD, Minikube, K3s, and K3d -that can be used for testing, study, and demonstration purposes. - ## Create local Kubernetes cluster -You can use tools such as +You can also create a local cluster using packages such as [KinD](https://kind.sigs.k8s.io/), [k3d](https://k3d.io/), [k3s](https://k3s.io/), @@ -27,6 +23,9 @@ and [Minikube](https://minikube.sigs.k8s.io/docs/) to set up a local, lightweight Kubernetes cluster where you can install the Keptn Lifecycle Toolkit for personal study, demonstrations, and testing. +For more information, see the Kubernetes +[Install Tools](https://kubernetes.io/docs/tasks/tools/) +documentation. The [Keptn Lifecycle Toolkit: Installation and KeptnTask Creation in Minutes](https://www.youtube.com/watch?v=Hh01bBwZ_qM) video demonstrates how to create a KinD cluster. @@ -41,6 +40,10 @@ The basic steps are: kind create cluster ``` + See the + [KinD Quick Start Guide](https://kind.sigs.k8s.io/docs/user/quick-start/) + for more information + 1. When the cluster has been created, run the following to verify that the cluster is working and that it is running a supported version of Kubernetes @@ -49,3 +52,51 @@ The basic steps are: ```shell kubectl version --short ``` + +## Prepare your cluster for KLT + +The Keptn Lifecycle Toolkit installs into an existing Kubernetes cluster. +When setting up a local Kubernetes cluster +to study or demonstrate the Lifecycle Toolkit, +you need to provide these components. + +Your cluster should include the following: + +* A supported version of Kubernetes. + See [Supported Kubernetes versions](reqs.md/#supported-kubernetes-versions) + for details. + +* [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) + +* Metric provider such as + [Prometheus](https://prometheus.io/), + [Dynatrace](https://www.dynatrace.com/), + or [Datadog](https://www.datadoghq.com/). + This is used for the metrics used for the observability features. + +* Deployment tools of your choice, + such as + [Argo CD](https://argo-cd.readthedocs.io/en/stable/) or + [Flux](https://fluxcd.io/). + Alternatively, KLT also works with just `kubctl apply` for deployment. + +* If you want to use the standardized observability feature, + install an OpenTelemetry collector on your cluster. + See + [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) + for more information. + +* If you want a dashboard for reviewing metrics and traces, + Install [Grafana](https://grafana.com/) + or the dashboard of your choice. + +* For traces, install [Jaeger](https://jaegertracing.io) + or a similar tool. + +Also note that the Keptn Lifecycle Toolkit includes +a light-weight cert-manager that, by default, is installed +as part of the KLT software. +If you are using another cert-manager in the cluster, +you can configure KLT to instead use your cert-manager. +See [Use your own cert-manager](cert-manager.md) +for detailed instructions. diff --git a/docs/content/en/docs/install/reqs.md b/docs/content/en/docs/install/reqs.md index e9ec16d46b..8f735090ba 100644 --- a/docs/content/en/docs/install/reqs.md +++ b/docs/content/en/docs/install/reqs.md @@ -1,8 +1,6 @@ --- title: Requirements description: Supported software versions and information about resources required -icon: concepts -layout: quickstart weight: 15 hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.html --- @@ -11,6 +9,21 @@ hidechildren: false # this flag hides all sub-pages in the sidebar-multicard.htm The Keptn Lifecycle Controller requires Kubernetes v1.24.0 or later. +Run the following to ensure that both client and server versions +are running Kubernetes versions greater than or equal to v1.24. +In this example, both client and server are at v1.24.0 +so the Keptn Lifecycle Toolkit will work. + +```shell +kubectl version --short +``` + +```shell +Client Version: v1.24.0 +Kustomize Version: v4.5.4 +Server Version: v1.24.0 +``` + ## Resource requirements ## cert-manager diff --git a/docs/content/en/docs/tasks/write-tasks/_index.md b/docs/content/en/docs/tasks/write-tasks/_index.md deleted file mode 100644 index 88bed96f31..0000000000 --- a/docs/content/en/docs/tasks/write-tasks/_index.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Write Keptn Tasks -description: Learn how to use the Keptn Lifecycle Toolkit and explore basic features. -icon: concepts -layout: quickstart -weight: 20 -hidechildren: true # this flag hides all sub-pages in the sidebar-multicard.html ---- - -## Keptn Task Definition - -A `KeptnTaskDefinition` is a CRD used to define tasks that can be run by the Keptn Lifecycle Toolkit -as part of pre- and post-deployment phases of a deployment. -The task definition is a [Deno](https://deno.land/) script. -In the future, we also intend to support other runtimes, especially running a container image directly. - -A task definition can be configured in three different ways: - -- inline -- referring to an HTTP script -- referring to another `KeptnTaskDefinition` - -An inline task definition looks like the following: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: deployment-hello -spec: - function: - inline: - code: | - console.log("Deployment Task has been executed"); -``` - -In the code section, it is possible to define a full-fletched Deno script. - -The runtime can also fetch the script on the fly from a remote webserver. -For this, the CRD should look like the -following: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: hello-keptn-http -spec: - function: - httpRef: - url: -``` - -Finally, `KeptnTaskDefinition` can build on top of other `KeptnTaskDefinition`s. -This is a common use case where a general function can be re-used in multiple places with different parameters. - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha2 -kind: KeptnTaskDefinition -metadata: - name: slack-notification-dev -spec: - function: - functionRef: - name: slack-notification - parameters: - map: - textMessage: "This is my configuration" - secureParameters: - secret: slack-token -``` - -As you might have noticed, Task Definitions also have the possibility to use input parameters. -The Lifecycle Toolkit passes the values defined inside the `map` field as a JSON object. -At the moment, multi-level maps are not supported. -The JSON object can be read through the environment variable `DATA` using `Deno.env.get("DATA");`. -Kubernetes secrets can also be passed to the function using the `secureParameters` field. - -Here, the `secret` value is the name of the K8s secret containing a field with the key `SECURE_DATA`. -The value of that field will then be available to the functions runtime via an environment variable called `SECURE_DATA`. - -For example, if you have a task function that should make use of secret data, you must first ensure that the secret -containing the `SECURE_DATA` key exists, as e.g.: - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: deno-demo-secret - namespace: default -type: Opaque -data: - SECURE_DATA: YmFyCg== # base64 encoded string, e.g. 'bar' -``` - -Then, you can make use of that secret as follows: - -```yaml -apiVersion: lifecycle.keptn.sh/v1alpha3 -kind: KeptnTaskDefinition -metadata: - name: deployment-hello - namespace: "default" -spec: - function: - secureParameters: - secret: deno-demo-secret - inline: - code: | - console.log("Deployment Hello Task has been executed"); - - let foo = Deno.env.get('SECURE_DATA'); - console.log(foo); - Deno.exit(0); -``` diff --git a/docs/content/en/docs/yaml-crd-ref/app.md b/docs/content/en/docs/yaml-crd-ref/app.md index 38644caec7..e4d0d790d0 100644 --- a/docs/content/en/docs/yaml-crd-ref/app.md +++ b/docs/content/en/docs/yaml-crd-ref/app.md @@ -3,3 +3,137 @@ title: KeptnApp description: Define all workloads and checks associated with an application weight: 10 --- + +`KeptnApp` defines a list of workloads +that together constitute a logical application. +It contains information about all workloads and checks +that are associated with a Keptn application +and a list of tasks and evaluations to be executed +pre- and post-deployment. + +## Synopsis + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnApp +metadata: + name: + namespace: +spec: + version: "x.y" + revision: x + workloads: + - name: + version: x.y.z + - name: + version: x.y.z + preDeploymentTasks: + - + postDeploymentTasks: + - + preDeploymentEvaluations: + - + postDeploymentEvaluations: + - +``` + +## Fields + +* **apiVersion** -- API version being used. +* **kind** -- Resource type. + Must be set to `KeptnApp` + +* **metadata** + * **name** -- Unique name of this application. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + +* **spec** + * **version** -- version of the Keptn application. + Changing this version number causes a new execution + of all application-level checks + * **revision** -- revision of a `version`. + The value is an integer that can be modified + to trigger another deployment of a `KeptnApp` of the same version. + For example, increment this number to restart a `KeptnApp` version + that failed to deploy, perhaps because a + `preDeploymentEvaluation` or `preDeploymentTask` failed. + * **workloads** + * **name** - name of this Kubernetes + [workload](https://kubernetes.io/docs/concepts/workloads/). + Use the same naming rules listed above for the application name. + Provide one entry for each workload + associated with this Keptn application. + * **version** -- version number for this workload. + Changing this number causes a new execution + of checks for the Keptn application and the new version of the workload. + * **preDeploymentTasks** -- list each task to be run + as part of the pre-deployment stage. + Task names must match the value of the `name` field + for the associated [KeptnTaskDefinition](taskdefinition.md) resource. + * **postDeploymentTasks** -- list each task to be run + as part of the post-deployment stage. + Task names must match the value of the `name` field + for the associated [KeptnTaskDefinition](taskdefinition.md) resource. + * **preDeploymentEvaluations** -- list each evaluation to be run + as part of the pre-deployment stage. + Evaluation names must match the value of the `name` field + for the associated [KeptnEvaluationDefinition](evaluationdefinition.md) + resource. + * **postDeploymentEvaluations** -- list each evaluation to be run + as part of the post-deployment stage. + Evaluation names must match the value of the `name` field + for the associated [KeptnEvaluationDefinition](evaluationdefinition.md) + resource. + +## Usage + +Kubernetes defines +[workloads](https://kubernetes.io/docs/concepts/workloads/) +but does not define applications. +The Keptn Lifecycle Toolkit adds the concept of applications +defined as a set of workloads that can be executed. +A `KeptnApp` resource is added +into the repository of the deployment engine +(ArgoCD, Flux, etc) +and is then deployed by that deployment engine. + +You can create a `KeptnApp` resource as a standard YAML manifest +or you can use the +[automatic application discovery](../implementing/integrate/#use-keptn-automatic-app-discovery) +feature to automatically generate a `KeptnApp` resource +based on Keptn or [recommended Kubernetes labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/). +This allows you to use the KLT observability features for existing resources +without manually populating any Keptn related resources. + +## Example + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnApp +metadata: + name: podtato-head + namespace: podtato-kubectl +spec: + version: "1.3" + workloads: + - name: podtato-head-left-arm + version: 0.1.0 + - name: podtato-head-left-leg + version: 1.2.3 + postDeploymentTasks: + - post-deployment-hello + preDeploymentEvaluations: + - my-prometheus-definition +``` + +## Files + +## Differences between versions + +The `spec.Revision` field is introduced in v1alpha2. + +## See also + +[Use Keptn automatic app discovery](../implementing/integrate/#use-keptn-automatic-app-discovery) diff --git a/docs/content/en/docs/yaml-crd-ref/config.md b/docs/content/en/docs/yaml-crd-ref/config.md index 75a054c5af..0e308a3968 100644 --- a/docs/content/en/docs/yaml-crd-ref/config.md +++ b/docs/content/en/docs/yaml-crd-ref/config.md @@ -3,3 +3,74 @@ title: KeptnConfig description: Define configuration values weight: 20 --- + +`KeptnConfig` defines configuration values for the Keptn Lifecycle Toolkit. + +## Yaml Synopsis + +```yaml +apiVersion: options.keptn.sh/v?alpha? +kind: KeptnConfig +metadata: + name: +spec: + OTelCollectorUrl: '' + keptnAppCreationRequestTimeoutSeconds: <#-seconds> +``` + +## Fields + +* **apiVersion** -- API version being used. +* **kind** -- Resource type. + Must be set to `KeptnConfig`. + +* **metadata** + * **name** -- Unique name of this set of configurations. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + +* **spec** + * **OTelCollectorUrl** -- The URL and port of the OpenTelemetry collector + This field must be populated + in order to export traces to the OpenTelemetry Collector. + * **keptnAppCreationRequestTimeoutSeconds** -- + interval in which automatic app discovery searches for workloads + to put into the same auto-generated [KeptnApp](app.md). + The default value is 30 (seconds). + +## Usage + +Each cluster should have a single `KeptnConfig` CRD +that describes all configurations for that cluster. + +## Example + +### OTel example + +This example specifies the URL of the OpenTelemetry collector +and that the automatic app discovery should be run every 40 seconds: + +```yaml +apiVersion: options.keptn.sh/v1alpha2 +kind: KeptnConfig +metadata: + name: klt-config +spec: + OTelCollectorUrl: 'otel-collector:4317' + keptnAppCreationRequestTimeoutSeconds: 40 +``` + +## Files + +API Reference: + +* [KeptnTaskDefinition](../crd-ref/lifecycle/v1alpha3/_index.md#keptntaskdefinition) + +## Differences between versions + +## See also + +* [KeptnApp](../yaml-crd-ref/app.md) +* [OpenTelemetry observability](../implementing/otel.md) +* [Keptn automatic app discovery](../implementing/integrate/_index.md/#use-keptn-automatic-app-discovery) diff --git a/docs/content/en/docs/yaml-crd-ref/evaluationdefinition.md b/docs/content/en/docs/yaml-crd-ref/evaluationdefinition.md index 187ac50908..b16a1e072b 100644 --- a/docs/content/en/docs/yaml-crd-ref/evaluationdefinition.md +++ b/docs/content/en/docs/yaml-crd-ref/evaluationdefinition.md @@ -1,5 +1,126 @@ --- title: KeptnEvaluationDefinition -description: Define all workloads and checks associated with an application -weight: 25 +description: Define an evaluation query + +weight: 20 --- + +A `KeptnEvaluationDefinition` assigns target values +to [KeptnMetric](metric.md) queries. +These are used as part of evaluation tasks +that can be run by the Keptn Lifecycle Toolkit +as part of pre- and post-analysis phases of a workload or application. + +## Yaml Synopsis + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnEvaluationDefinition +metadata: + name: +spec: + objectives: + - evaluationTarget: "" + keptnMetricRef: + name: available-cpus + namespace: some-namespace +``` + +## Fields + +* **apiVersion** -- API version being used. + Must be `v1alpha3` or later for this syntax. +* **kind** -- Resource type. + Must be set to `KeptnEvaluationDefinition` + +* **metadata** + * **name** -- Unique name of this evaluation + such as `pre-deploy-eval` or `post-deploy-resource-eval`. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + +* **spec** + + * **objectives** -- define the evaluations to be performed. + Each objective is expressed as a `keptnMetricRef` + and an `evaluationTarget` value. + + * **KeptnMericRef** -- A reference to the + [KeptnMetric](metric.md) object that contains the value, + identified by `name` and `namespace` + * **evaluationTarget** -- Desired value of the query, + expressed as an arithmatic formula, + usually less than (`<`) or greater than (`>`) + This is used to define success or failure criteria + for the referenced `KeptnMetric` in order to pass or fail + the pre- and post-evaluation stages + +## Usage + +A `KeptnEvaluationDefinition` references one or more +[KeptnMetric](metric.md) CRDs. +When multiple `KeptnMetric`s are used, +the Keptn Lifecycle Toolkit considers the evaluation successful +if **all** metrics meet their `evaluationTarget`. + +## Example + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnEvaluationDefinition +metadata: + name: my-prometheus-evaluation + namespace: example +spec: + source: prometheus + objectives: + - keptnMetricRef: + name: available-cpus + namespace: example + evaluationTarget: ">1" + - keptnMetricRef: + name: cpus-throttling + namespace: example + evaluationTarget: "<0.01" +``` + +## Files + +API Reference: + +## Differences between versions + +In the `v1alpha1` and `v1alpha2` API versions, +`KeptnEvaluationDefinition` referenced the `KeptnEvaluationProvider` CR +to identify the data source associated with this definition +and itself contained the queries +that are now taken from the specified [KeptnMetric](metric.md) CRD. +The synopsis was: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha2 +kind: KeptnEvaluationDefinition +metadata: + name: +spec: + source: prometheus | dynatrace | datadog + objectives: + - name: query-1 + query: "xxxx" + evaluationTarget: <20 + - name: query-2 + query: "yyyy" + evaluationTarget: >4 +``` + +Beginning with `v1alpha3` API version, +`KeptnEvaluationDefinition` references a `keptnMetricRef` +that points to a [KeptnMetric](metric.md) CR, +that defines the data source, the query and the namespace to use. +The `KeptnEvaluationDefinition` merely specifies the evaluation target. + +## See also + +* [KeptnMetricsProvider](metricsprovider.md) +* [KeptnMetric](metric.md) diff --git a/docs/content/en/docs/yaml-crd-ref/metric.md b/docs/content/en/docs/yaml-crd-ref/metric.md index 7d48b8222f..0dc11c75a1 100644 --- a/docs/content/en/docs/yaml-crd-ref/metric.md +++ b/docs/content/en/docs/yaml-crd-ref/metric.md @@ -3,3 +3,119 @@ title: KeptnMetric description: Define all workloads and checks associated with an application weight: 50 --- + +A `KeptnMetric` represents a metric that is collected from a provider. +Providing the metrics as a CR in a Kubernetes cluster +facilitates the reusability of this data across multiple components +and allows using multiple observability platforms +for different metrics at the same time. + +`KeptnMetric` CRs are also used as targets for +[EvaluationDefinition](evaluationdefinition.md) CRs. + +## Yaml Synopsis + +```yaml +apiVersion: metrics.keptn.sh/v1alpha3 +kind: KeptnMetric +metadata: + name: + namespace: +spec: + provider: + name: "" + query: "" + fetchIntervalSeconds: <#-seconds> +``` + +## Fields + +* **apiVersion** -- API version being used. +` +* **kind** -- Resource type. + Must be set to `KeptnMetric`. + +* **metadata** + * **name** -- Unique name of this metric. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + * **namespace** -- Namespace of the application using this metric. + +* **spec** + * **provider.name** -- + Name of this instance of the data source + from which the metric is collected. + This value must match the value of the `metadata.name` field + of the corresponding [KeptnMetricsProvider](metricsprovider.md) CRD. + + Assigning your own name to the provider + rather than just the type of provider + enables you to support multiple instances of a data provider. + For example, you might have `dev-prometheus` + as the name of the Prometheus server that monitors the dev deployment + and `prod-prometheus` as the name of the Prometheus server + that monitors the production deployment. + * **query** -- String in the provider-specific query language, + used to obtain a metric. + * **fetchIntervalSeconds** -- Number of seconds between updates of the metric. + +## Usage + +## Example + +This example pulls metrics from the data provider +defined as `my-provider` in the `spec.provider.name` field +of the corresponding `KeptnMetricsProvider` CR. + +```yaml +apiVersion: metrics.keptn.sh/v1alpha3 +kind: KeptnMetric +metadata: + name: keptnmetric-sample + namespace: podtato-kubectl +spec: + provider: + name: "my-provider" + query: "sum(kube_pod_container_resource_limits{resource='cpu'})" + fetchIntervalSeconds: 5 +``` + +## Files + +API Reference: + +## Differences between versions + +Beginning with the `v1alpha3` API version, +Keptn allows you to define multiple instances of the same data source. +In earlier versions, you could use multiple data sources +but only one instance of each. +Consequently the `v1alpha1` and `v1alpha2` API versions +define the `provider` field with the type of the data provider +(`prometheus`, `dynatrace`, or `dql`) +rather than the particular name assigned +to the instance of the data provider +that is assigned in the +[KeptnMetricsProvider](metricsprovider.md) CR. + +So the `v1alpha1` and `v1alpha2` synopsis +of the `spec` field is: + +```yaml +... +spec: + provider: + name: "prometheus | dynatrace | dql" + fetchIntervalSeconds: + query: >- + "" +``` + +## See also + +* [KeptnEvaluationDefinition](evaluationdefinition.md) +* [KeptnMetricsProvider](metricsprovider.md) +* Implementing [Keptn Metrics](../implementing/evaluatemetrics.md) +* [Getting started with Keptn metrics](../getting-started/metrics) +* Architecture of the [Keptn Metrics Operator](../concepts/architecture/components/metrics-operator/_index.md) diff --git a/docs/content/en/docs/yaml-crd-ref/metricsprovider.md b/docs/content/en/docs/yaml-crd-ref/metricsprovider.md index 0ca9078a11..98f70bfc49 100644 --- a/docs/content/en/docs/yaml-crd-ref/metricsprovider.md +++ b/docs/content/en/docs/yaml-crd-ref/metricsprovider.md @@ -1,5 +1,130 @@ --- title: KeptnMetricsProvider -description: Define all workloads and checks associated with an application +description: Define a data provider used for metrics and evaluations weight: 55 --- + +`KeptnMetricsProvider` defines an instance of the data provider +(such as Prometheus, Dynatrace, or Datadog) +that is used by the [KeptnMetric](metric.md) resource. +One Keptn application can perform evaluations based on metrics +from more than one data provider +and, beginning with the v1alpha3 API version, +can use more than one instance of each data provider. +To implement this, create a `KeptnMetricsProvider` resource +for each instance of each data provider being used, +then reference the appropriate provider +for each metric definition by its name. + +## Yaml Synopsis + +```yaml +apiVersion: metrics.keptn.sh/v1alpha3 +kind: KeptnMetricsProvider +metadata: + name: + namespace: +spec: + type: prometheus | dynatrace | dql | datadog + targetServer: "" + secretKeyRef: + name: + key: +``` + +## Fields + +* **apiVersion** -- API version being used. +` +* **kind** -- Resource type. + Must be set to `KeptnMetricsProvider` + +* **metadata** + * **name** -- Unique name of this provider, + used to reference the provider for the + [KeptnEvaluationDefinition](evaluationdefinition.md) + and [KeptnMetric](metric.md) resources. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + + For example, you might define `dev-prometheus` + for the Prometheus instance that monitors the development deployment, + and `qa-prometheus` for the Prometheus instance + that monitors the Prometheus instance that monitors the QA deployment, + and `prod-dynatrace` for the Dynatrace instance + that monitors the production deployment. + + * **namespace** -- Namespace where this provider is used. + +* **spec** + + * **type** -- The type of data provider for this instance + * **targetServer** -- URL of the data provider, enclosed in double quotes + * **secretKeyRef** + * **name:** -- Name of the token for this data provider + * **key:** -- Key for this data provider + +## Usage + +## Examples + +### Example 1: Dynatrace data provider + +```yaml +apiVersion: metrics.keptn.sh/v1alpha3 +kind: KeptnMetricsProvider +metadata: + name: dynatrace + namespace: podtato-kubectl +spec: + targetServer: "" + secretKeyRef: + name: dt-api-token + key: DT_TOKEN +``` + +## Files + +API Reference: + +* [KeptnEvaluationDefinition](../crd-ref/lifecycle/v1alpha3/_index.md#keptnevaluationdefinition) + +## Differences between versions + +For the `v1alpha2` API version, +Keptn did not support +using more than one instance of a particular data provider +in the same namespace. +In other words, one namespace could support one instance each +of Prometheus, Dynatrace, and Datadog +but could not support, for example, two instances of Prometheus. + +The synopsis in those older API versions +only specified the `metadata.name` field +that identified the data provider (`prometheus`, `dynatrace`, or `dql`): + +```yaml +apiVersion: metrics.keptn.sh/v1alpha2 +kind: KeptnMetricsProvider +metadata: + name: prometheus | dynatrace |dql + namespace: +spec: + targetServer: "" + secretKeyRef: + name: dt-api-token + key: DT_TOKEN +``` + +Also note that, for the v1alpha1 and v1alpha2 API versions, +`KeptnMetricsProvider` only specifies the provider +for the `KeptnMetrics` resource. +Beginning with `v1alpha3` API version, +`KeptnMetricsProvider` is also used to specify the provider +for the `KeptnEvaluationDefinition` resource. + +## See also + +* [KeptnEvaluationDefinition](evaluationdefinition.md) +* [KeptnMetric](metric.md) diff --git a/docs/content/en/docs/yaml-crd-ref/taskdefinition.md b/docs/content/en/docs/yaml-crd-ref/taskdefinition.md index 877444e902..b4e50388b9 100644 --- a/docs/content/en/docs/yaml-crd-ref/taskdefinition.md +++ b/docs/content/en/docs/yaml-crd-ref/taskdefinition.md @@ -3,3 +3,310 @@ title: KeptnTaskDefinition description: Define tasks that can be run pre- or post-deployment weight: 89 --- + + +A `KeptnTaskDefinition` defines tasks +that are run by the Keptn Lifecycle Toolkit +as part of the pre- and post-deployment phases of a +[KeptnApp](./app.md) or +[KeptnWorkload](../concepts/workloads/). + +## Yaml Synopsis + +```yaml +apiVersion: lifecycle.keptn.sh/v?alpha? +kind: KeptnTaskDefinition +metadata: + name: +spec: + function: + inline | httpRef | functionRef | ConfigMapRef + parameters: + map: + textMessage: "This is my configuration" + secureParameters: + secret: slack-token +``` + +## Fields + +* **apiVersion** -- API version being used. +` +* **kind** -- Resource type. + Must be set to `KeptnTaskDefinition` + +* **metadata** + * **name** -- Unique name of this task. + Names must comply with the + [Kubernetes Object Names and IDs](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + specification. + +* **spec** + * **function** -- Code to be executed, + expressed as a [Deno](https://deno.land/) script. + Refer to [function runtime](https://github.com/keptn/lifecycle-toolkit/tree/main/functions-runtime) + for more information about the runtime. + + The `function` can be defined as one of the following: + + * **inline** - Include the actual executable code to execute. + This can be written as a full-fledged Deno script + that is included in this file. + For example: + + ```yaml + function: + inline: + code: | + console.log("Deployment Task has been executed"); + ``` + + * **httpRef** - Specify a Deno script to be executed at runtime + from the remote webserver that is specified. + For example: + + ```yaml + name: hello-keptn-http + spec: + function: + httpRef: + url: "https://www.example.com/yourscript.js" + ``` + + * **functionRef** -- Execute one or more `KeptnTaskDefinition` resources + that have been defined. + Populate this field with the value(s) of the `name` field + for the `KeptnTaskDefinition`(s) to be called. + This is commonly used to call a general function + that is used in multiple places, possibly with different parameters. + An example is: + + ```yaml + spec: + function: + functionRef: + name: slack-notification + ``` + + This can also be used to group a set of tasks + into a single `KeptnTaskDefinition`, + such as defining a `KeptnTaskDefinition` for testing. + In this case, it calls other, existing `KeptnTaskDefinition`s + for each type of test to be run, + specifying each by the value of the `name` field. + * **ConfigMapRef** - Specify the name of a + [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) + resource that contains the function to be executed. + + * **parameters** - An optional field to supply input parameters to a function. + The Lifecycle Toolkit passes the values defined inside the `map` field + as a JSON object. + For example: + + ```yaml + spec: + parameters: + map: + textMessage: "This is my configuration" + ``` + + See + [Parameterized functions](../implementing/tasks/#parameterized-functions) + for more information. + + * **secureParameters** -- An optional field used to pass a Kubernetes secret. + The `secret` value is the Kubernetes secret name + that is mounted into the runtime and made available to functions + using the `SECURE_DATA` environment variable. + For example: + + ```yaml + secureParameters: + secret: slack-token + ``` + + Note that, currently, only one secret can be passed. + + See [Create secret text](../implementing/tasks/#create-secret-text) + for details. + +## Usage + +A Task executes the TaskDefinition of a +[KeptnApp](app.md) or [KeptnWorkload]. +The execution is done by spawning a Kubernetes +[Job](https://kubernetes.io/docs/concepts/workloads/controllers/job/) +to handle a single Task. +In its state, it tracks the current status of this Kubernetes Job. + +The `function` is coded in JavaScript +and executed in +[Deno](https://deno.com/runtime), +which is a lightweight runtime environment +that executes in your namespace. +Note that Deno has tighter restrictions +for permissions and importing data +so a script that works properly elsewhere +may not function out of the box when run in Deno. + +A task can be executed either pre-deployment or post-deployment +as specified in the `Deployment` resource; +see +[Pre- and post-deployment tasks](../implementing/integrate/#pre--and-post-deployment-checks) +for details. +Note that the annotation identifies the task by `name`. +This means that you can modify the `function` code in the resource definition +and the revised code is picked up without additional changes. + +## Examples + +### Example 1: inline script + +This example defines a full-fledged Deno script +within the `KeptnTaskDefinition` YAML file: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-inline +spec: + function: + inline: + code: | + let text = Deno.env.get("DATA"); + let data; + let name; + data = JSON.parse(text); + + name = data.name + console.log("Hello, " + name + " new"); +``` + +### Example 2: httpRef script + +This example fetches the Deno script from a remote webserver at runtime: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: hello-keptn-http +spec: + function: + httpRef: + url: "https://www.example.com/yourscript.js" +``` + +For another example, see the +[sample-app](https://github.com/keptn-sandbox/lifecycle-toolkit-examples/blob/main/sample-app/version-1/app-pre-deploy.yaml). + +See the +[sample-app/version-1](https://github.com/keptn-sandbox/lifecycle-toolkit-examples/blob/main/sample-app/version-1/app-pre-deploy.yaml) +PodtatoHead example for a more complete example. + +### Example 3: functionRef + +This example calls another defined task, +illustrating how one `KeptnTaskDefinition` can build +on top of other `KeptnTaskDefinition`s. +In this case, it calls `slack-notification-dev`, +passing `parameters` and `secureParameters` to that other task: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: slack-notification-dev +spec: + function: + functionRef: + name: slack-notification + parameters: + map: + textMessage: "This is my configuration" + secureParameters: + secret: slack-token +``` + +### Example 4: ConfigMapRef + +This example references a `ConfigMap` by the name of `dev-configmap` +that contains the code for the function to be executed. + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: keptntaskdefinition-sample +spec: + function: + configMapRef: + name: dev-configmap +``` + +### Example 5: ConfigMap + +This example illustrates the use of both a `ConfigMapRef` and a `ConfigMap`: + +```yaml +apiVersion: lifecycle.keptn.sh/v1alpha2 +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); +``` + +### More examples + +See the [operator/config/samples](https://github.com/keptn/lifecycle-toolkit/tree/main/operator/config/samples/function_execution) +directory for more example `KeptnTaskDefinition` YAML files. + +## Files + +API Reference: + +* [KeptnTaskDefinition](../crd-ref/lifecycle/v1alpha3/_index.md#keptntaskdefinition) +* [KeptnTaskDefinitionList](../crd-ref/lifecycle/v1alpha3/_index.md#keptntaskdefinitionlist) +* [KeptnTaskDefinitionSpec](../crd-ref/lifecycle/v1alpha3/_index.md#keptntaskdefinitionspec) +* [FunctionReference](../crd-ref/lifecycle/v1alpha3/_index.md#functionreference) +* [FunctionSpec](../crd-ref/lifecycle/v1alpha3/_index.md#functionspec) +* [FunctionStatus](../crd-ref/lifecycle/v1alpha3/_index.md#functionstatus) +* [HttpReference](../crd-ref/lifecycle/v1alpha3/_index.md#httpreference) +* [Inline](../crd-ref/lifecycle/v1alpha3/_index.md#inline) + +## Differences between versions + +The `KeptnTaskDefinition` is the same for +all `v1alpha?` library versions. + +## See also + +* [Working with tasks](../implementing/tasks) +* [Pre- and post-deployment tasks](../implementing/integrate/#pre--and-post-deployment-checks) +* [Orchestrate deployment checks](../getting-started/orchestrate) diff --git a/examples/Makefile b/examples/Makefile index 80186a9b6d..12bb35d025 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,5 +1,5 @@ # renovate: datasource=github-tags depName=jaegertracing/jaeger-operator -JAEGER_VERSION ?= v1.43.0 +JAEGER_VERSION ?= v1.44.0 TOOLKIT_NAMESPACE ?= keptn-lifecycle-toolkit-system PODTATO_NAMESPACE ?= podtato-kubectl GRAFANA_PORT_FORWARD ?= 3000 diff --git a/examples/sample-app/Makefile b/examples/sample-app/Makefile index 4427f774cf..5ddcbf2e2c 100644 --- a/examples/sample-app/Makefile +++ b/examples/sample-app/Makefile @@ -1,5 +1,5 @@ # renovate: datasource=github-tags depName=jaegertracing/jaeger -JAEGER_VERSION ?= v1.44.0 +JAEGER_VERSION ?= v1.45.0 LFC_NAMESPACE ?= keptn-lifecycle-controller-system PODTATO_NAMESPACE ?= podtato-kubectl GRAFANA_PORT_FORWARD ?= 3000 diff --git a/examples/sample-app/base/container-task.yaml b/examples/sample-app/base/container-task.yaml new file mode 100644 index 0000000000..b6001490da --- /dev/null +++ b/examples/sample-app/base/container-task.yaml @@ -0,0 +1,12 @@ +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: container-sleep +spec: + container: + name: testy-test + image: busybox:1.36.0 + command: + - 'sh' + - '-c' + - 'sleep 30' diff --git a/examples/sample-app/version-1/app.yaml b/examples/sample-app/version-1/app.yaml index 7358f7fbe4..ded6ef9192 100644 --- a/examples/sample-app/version-1/app.yaml +++ b/examples/sample-app/version-1/app.yaml @@ -16,3 +16,5 @@ spec: version: 0.1.1 preDeploymentEvaluations: - app-pre-deploy-eval-1 + preDeploymentTasks: + - container-sleep diff --git a/examples/sample-app/version-2/app.yaml b/examples/sample-app/version-2/app.yaml index d43ed4a004..1801433737 100644 --- a/examples/sample-app/version-2/app.yaml +++ b/examples/sample-app/version-2/app.yaml @@ -16,3 +16,5 @@ spec: version: 0.1.1 preDeploymentEvaluations: - app-pre-deploy-eval-2 + preDeploymentTasks: + - container-sleep diff --git a/examples/sample-app/version-3/app.yaml b/examples/sample-app/version-3/app.yaml index 3c33d0f0ca..3bb50a1dc5 100644 --- a/examples/sample-app/version-3/app.yaml +++ b/examples/sample-app/version-3/app.yaml @@ -14,3 +14,5 @@ spec: version: 0.1.1 - name: podtato-head-hat version: 0.1.2 + preDeploymentTasks: + - container-sleep diff --git a/examples/support/argo/Makefile b/examples/support/argo/Makefile index 866a64239d..61cd8312c2 100644 --- a/examples/support/argo/Makefile +++ b/examples/support/argo/Makefile @@ -2,7 +2,7 @@ LFC_NAMESPACE ?= keptn-lifecycle-toolkit-system PODTATO_NAMESPACE ?= podtato-kubectl ARGO_NAMESPACE ?= argocd # renovate: datasource=github-tags depName=argoproj/argo-cd -ARGO_VERSION ?= v2.6.7 +ARGO_VERSION ?= v2.7.2 ARGO_SECRET = $(shell kubectl -n ${ARGO_NAMESPACE} get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo) .PHONY: install diff --git a/examples/support/observability/Makefile b/examples/support/observability/Makefile index fe94c7df7d..3d5a365a33 100644 --- a/examples/support/observability/Makefile +++ b/examples/support/observability/Makefile @@ -1,5 +1,5 @@ # renovate: datasource=github-tags depName=jaegertracing/jaeger-operator -JAEGER_VERSION ?= v1.42.0 +JAEGER_VERSION ?= v1.44.0 TOOLKIT_NAMESPACE ?= keptn-lifecycle-toolkit-system PODTATO_NAMESPACE ?= podtato-kubectl GRAFANA_PORT_FORWARD ?= 3000 diff --git a/examples/support/observability/assets/podtatohead-deployment-evaluation/manifest.yaml b/examples/support/observability/assets/podtatohead-deployment-evaluation/manifest.yaml index d4a51c4ae7..ab5f08ebc8 100644 --- a/examples/support/observability/assets/podtatohead-deployment-evaluation/manifest.yaml +++ b/examples/support/observability/assets/podtatohead-deployment-evaluation/manifest.yaml @@ -29,7 +29,7 @@ spec: terminationGracePeriodSeconds: 5 initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] containers: - name: server diff --git a/functions-runtime/README.md b/functions-runtime/README.md index 134c28b638..3d2076d1ef 100644 --- a/functions-runtime/README.md +++ b/functions-runtime/README.md @@ -68,7 +68,7 @@ docker run \ -e SCRIPT=https://raw.githubusercontent.com/keptn/lifecycle-toolkit/main/functions-runtime/samples/ts/prometheus.ts \ -e DATA='{ "url":"http://localhost:9090", "metrics": "up{service=\"kubernetes\"}", "expected_value": "1" }' \ -it \ - ghcr.keptn.sh/keptn/functions-runtime:${VERSION} + ghcr.io/keptn/functions-runtime:${VERSION} ``` diff --git a/helm/chart/README.md b/helm/chart/README.md index 5da5d16f37..20901aa590 100644 --- a/helm/chart/README.md +++ b/helm/chart/README.md @@ -8,23 +8,23 @@ checks ### Keptn Scheduler -| Name | Description | Value | -| -------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------- | -| `scheduler.scheduler.containerSecurityContext` | Sets security context | | -| `scheduler.scheduler.env.otelCollectorUrl` | sets url for open telemetry collector | `otel-collector:4317` | -| `scheduler.scheduler.image.repository` | set image repository for scheduler | `ghcr.keptn.sh/keptn/scheduler` | -| `scheduler.scheduler.image.tag` | set image tag for scheduler | `v0.7.1` | -| `scheduler.scheduler.imagePullPolicy` | set image pull policy for scheduler | `Always` | -| `scheduler.scheduler.livenessProbe` | customizable liveness probe for the scheduler | | -| `scheduler.scheduler.readinessProbe` | customizable readiness probe for the scheduler | | -| `scheduler.scheduler.resources` | sets cpu and memory resurces/limits for scheduler | | -| `schedulerConfig.schedulerConfigYaml.leaderElection.leaderElect` | enables leader election for multiple replicas of the scheduler | `false` | -| `schedulerConfig.schedulerConfigYaml.profiles[0].plugins.permit.enabled[0].name` | enables permit plugin | `KLCPermit` | -| `schedulerConfig.schedulerConfigYaml.profiles[0].schedulerName` | changes scheduler name | `keptn-scheduler` | -| `scheduler.nodeSelector` | adds node selectors for scheduler | `{}` | -| `scheduler.replicas` | modifies replicas | `1` | -| `scheduler.tolerations` | adds tolerations for scheduler | `[]` | -| `scheduler.topologySpreadConstraints` | add topology constraints for scheduler | `[]` | +| Name | Description | Value | +| -------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------- | +| `scheduler.scheduler.containerSecurityContext` | Sets security context | | +| `scheduler.scheduler.env.otelCollectorUrl` | sets url for open telemetry collector | `otel-collector:4317` | +| `scheduler.scheduler.image.repository` | set image repository for scheduler | `ghcr.io/keptn/scheduler` | +| `scheduler.scheduler.image.tag` | set image tag for scheduler | `v0.7.1` | +| `scheduler.scheduler.imagePullPolicy` | set image pull policy for scheduler | `Always` | +| `scheduler.scheduler.livenessProbe` | customizable liveness probe for the scheduler | | +| `scheduler.scheduler.readinessProbe` | customizable readiness probe for the scheduler | | +| `scheduler.scheduler.resources` | sets cpu and memory resurces/limits for scheduler | | +| `schedulerConfig.schedulerConfigYaml.leaderElection.leaderElect` | enables leader election for multiple replicas of the scheduler | `false` | +| `schedulerConfig.schedulerConfigYaml.profiles[0].plugins.permit.enabled[0].name` | enables permit plugin | `KLCPermit` | +| `schedulerConfig.schedulerConfigYaml.profiles[0].schedulerName` | changes scheduler name | `keptn-scheduler` | +| `scheduler.nodeSelector` | adds node selectors for scheduler | `{}` | +| `scheduler.replicas` | modifies replicas | `1` | +| `scheduler.tolerations` | adds tolerations for scheduler | `[]` | +| `scheduler.topologySpreadConstraints` | add topology constraints for scheduler | `[]` | ### Keptn Certificate Operator common @@ -42,17 +42,17 @@ checks ### Keptn Certificate Operator controller -| Name | Description | Value | -| ------------------------------------------------------ | ------------------------------------------------------------------------- | ------------------------------------------ | -| `certificateOperator.manager.containerSecurityContext` | Sets security context for the cert manager | | -| `certificateOperator.manager.image.repository` | specify repo for manager image | `ghcr.keptn.sh/keptn/certificate-operator` | -| `certificateOperator.manager.image.tag` | select tag for manager container | `v0.7.1` | -| `certificateOperator.manager.imagePullPolicy` | select image pull policy for manager container | `Always` | -| `certificateOperator.manager.env.labelSelectorKey` | specify the label selector to find resources to generate certificates for | `keptn.sh/inject-cert` | -| `certificateOperator.manager.env.labelSelectorValue` | specify the value for the label selector | `true` | -| `certificateOperator.manager.livenessProbe` | custom RBAC proxy liveness probe | | -| `certificateOperator.manager.readinessProbe` | custom manager readiness probe | | -| `certificateOperator.manager.resources` | custom limits and requests for manager container | | +| Name | Description | Value | +| ------------------------------------------------------ | ------------------------------------------------------------------------- | ------------------------------------ | +| `certificateOperator.manager.containerSecurityContext` | Sets security context for the cert manager | | +| `certificateOperator.manager.image.repository` | specify repo for manager image | `ghcr.io/keptn/certificate-operator` | +| `certificateOperator.manager.image.tag` | select tag for manager container | `v0.7.1` | +| `certificateOperator.manager.imagePullPolicy` | select image pull policy for manager container | `Always` | +| `certificateOperator.manager.env.labelSelectorKey` | specify the label selector to find resources to generate certificates for | `keptn.sh/inject-cert` | +| `certificateOperator.manager.env.labelSelectorValue` | specify the value for the label selector | `true` | +| `certificateOperator.manager.livenessProbe` | custom RBAC proxy liveness probe | | +| `certificateOperator.manager.readinessProbe` | custom manager readiness probe | | +| `certificateOperator.manager.resources` | custom limits and requests for manager container | | ### Keptn Lifecycle Operator common @@ -71,33 +71,33 @@ checks ### Keptn Lifecycle Operator controller -| Name | Description | Value | -| ----------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------- | -| `lifecycleOperator.manager.containerSecurityContext` | Sets security context privileges | | -| `lifecycleOperator.manager.containerSecurityContext.allowPrivilegeEscalation` | | `false` | -| `lifecycleOperator.manager.containerSecurityContext.capabilities.drop` | | `["ALL"]` | -| `lifecycleOperator.manager.containerSecurityContext.privileged` | | `false` | -| `lifecycleOperator.manager.containerSecurityContext.runAsGroup` | | `65532` | -| `lifecycleOperator.manager.containerSecurityContext.runAsNonRoot` | | `true` | -| `lifecycleOperator.manager.containerSecurityContext.runAsUser` | | `65532` | -| `lifecycleOperator.manager.containerSecurityContext.seccompProfile.type` | | `RuntimeDefault` | -| `lifecycleOperator.manager.env.keptnAppControllerLogLevel` | sets the log level of Keptn App Controller | `0` | -| `lifecycleOperator.manager.env.keptnAppCreationRequestControllerLogLevel` | sets the log level of Keptn App Creation Request Controller | `0` | -| `lifecycleOperator.manager.env.keptnAppVersionControllerLogLevel` | sets the log level of Keptn AppVersion Controller | `0` | -| `lifecycleOperator.manager.env.keptnEvaluationControllerLogLevel` | sets the log level of Keptn Evaluation Controller | `0` | -| `lifecycleOperator.manager.env.keptnTaskControllerLogLevel` | sets the log level of Keptn Task Controller | `0` | -| `lifecycleOperator.manager.env.keptnTaskDefinitionControllerLogLevel` | sets the log level of Keptn TaskDefinition Controller | `0` | -| `lifecycleOperator.manager.env.keptnWorkloadControllerLogLevel` | sets the log level of Keptn Workload Controller | `0` | -| `lifecycleOperator.manager.env.keptnWorkloadInstanceControllerLogLevel` | sets the log level of Keptn WorkloadInstance Controller | `0` | -| `lifecycleOperator.manager.env.optionsControllerLogLevel` | sets the log level of Keptn Options Controller | `0` | -| `lifecycleOperator.manager.env.otelCollectorUrl` | Sets the URL for the open telemetry collector | `otel-collector:4317` | -| `lifecycleOperator.manager.env.functionRunnerImage` | specify image for task runtime | `ghcr.keptn.sh/keptn/functions-runtime:v0.7.1` | -| `lifecycleOperator.manager.image.repository` | specify registry for manager image | `ghcr.keptn.sh/keptn/lifecycle-operator` | -| `lifecycleOperator.manager.image.tag` | select tag for manager image | `v0.7.1` | -| `lifecycleOperator.manager.imagePullPolicy` | specify pull policy for manager image | `Always` | -| `lifecycleOperator.manager.livenessProbe` | custom livenessprobe for manager container | | -| `lifecycleOperator.manager.readinessProbe` | custom readinessprobe for manager container | | -| `lifecycleOperator.manager.resources` | specify limits and requests for manager container | | +| Name | Description | Value | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------- | +| `lifecycleOperator.manager.containerSecurityContext` | Sets security context privileges | | +| `lifecycleOperator.manager.containerSecurityContext.allowPrivilegeEscalation` | | `false` | +| `lifecycleOperator.manager.containerSecurityContext.capabilities.drop` | | `["ALL"]` | +| `lifecycleOperator.manager.containerSecurityContext.privileged` | | `false` | +| `lifecycleOperator.manager.containerSecurityContext.runAsGroup` | | `65532` | +| `lifecycleOperator.manager.containerSecurityContext.runAsNonRoot` | | `true` | +| `lifecycleOperator.manager.containerSecurityContext.runAsUser` | | `65532` | +| `lifecycleOperator.manager.containerSecurityContext.seccompProfile.type` | | `RuntimeDefault` | +| `lifecycleOperator.manager.env.keptnAppControllerLogLevel` | sets the log level of Keptn App Controller | `0` | +| `lifecycleOperator.manager.env.keptnAppCreationRequestControllerLogLevel` | sets the log level of Keptn App Creation Request Controller | `0` | +| `lifecycleOperator.manager.env.keptnAppVersionControllerLogLevel` | sets the log level of Keptn AppVersion Controller | `0` | +| `lifecycleOperator.manager.env.keptnEvaluationControllerLogLevel` | sets the log level of Keptn Evaluation Controller | `0` | +| `lifecycleOperator.manager.env.keptnTaskControllerLogLevel` | sets the log level of Keptn Task Controller | `0` | +| `lifecycleOperator.manager.env.keptnTaskDefinitionControllerLogLevel` | sets the log level of Keptn TaskDefinition Controller | `0` | +| `lifecycleOperator.manager.env.keptnWorkloadControllerLogLevel` | sets the log level of Keptn Workload Controller | `0` | +| `lifecycleOperator.manager.env.keptnWorkloadInstanceControllerLogLevel` | sets the log level of Keptn WorkloadInstance Controller | `0` | +| `lifecycleOperator.manager.env.optionsControllerLogLevel` | sets the log level of Keptn Options Controller | `0` | +| `lifecycleOperator.manager.env.otelCollectorUrl` | Sets the URL for the open telemetry collector | `otel-collector:4317` | +| `lifecycleOperator.manager.env.functionRunnerImage` | specify image for task runtime | `ghcr.io/keptn/functions-runtime:v0.7.1` | +| `lifecycleOperator.manager.image.repository` | specify registry for manager image | `ghcr.io/keptn/lifecycle-operator` | +| `lifecycleOperator.manager.image.tag` | select tag for manager image | `v0.7.1` | +| `lifecycleOperator.manager.imagePullPolicy` | specify pull policy for manager image | `Always` | +| `lifecycleOperator.manager.livenessProbe` | custom livenessprobe for manager container | | +| `lifecycleOperator.manager.readinessProbe` | custom readinessprobe for manager container | | +| `lifecycleOperator.manager.resources` | specify limits and requests for manager container | | ### Keptn Metrics Operator common @@ -135,23 +135,23 @@ checks ### Keptn Metrics Operator controller -| Name | Description | Value | -| --------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------- | -| `metricsOperator.manager.containerSecurityContext` | Sets security context privileges | | -| `metricsOperator.manager.containerSecurityContext.allowPrivilegeEscalation` | | `false` | -| `metricsOperator.manager.containerSecurityContext.capabilities.drop` | | `["ALL"]` | -| `metricsOperator.manager.containerSecurityContext.privileged` | | `false` | -| `metricsOperator.manager.containerSecurityContext.runAsGroup` | | `65532` | -| `metricsOperator.manager.containerSecurityContext.runAsNonRoot` | | `true` | -| `metricsOperator.manager.containerSecurityContext.runAsUser` | | `65532` | -| `metricsOperator.manager.containerSecurityContext.seccompProfile.type` | | `RuntimeDefault` | -| `metricsOperator.manager.image.repository` | specify registry for manager image | `ghcr.keptn.sh/keptn/metrics-operator` | -| `metricsOperator.manager.image.tag` | select tag for manager image | `v0.7.1` | -| `metricsOperator.manager.env.exposeKeptnMetrics` | enable metrics exporter | `true` | -| `metricsOperator.manager.env.metricsControllerLogLevel` | sets the log level of Metrics Controller | `0` | -| `metricsOperator.manager.livenessProbe` | custom livenessprobe for manager container | | -| `metricsOperator.manager.readinessProbe` | custom readinessprobe for manager container | | -| `metricsOperator.manager.resources` | specify limits and requests for manager container | | +| Name | Description | Value | +| --------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------- | +| `metricsOperator.manager.containerSecurityContext` | Sets security context privileges | | +| `metricsOperator.manager.containerSecurityContext.allowPrivilegeEscalation` | | `false` | +| `metricsOperator.manager.containerSecurityContext.capabilities.drop` | | `["ALL"]` | +| `metricsOperator.manager.containerSecurityContext.privileged` | | `false` | +| `metricsOperator.manager.containerSecurityContext.runAsGroup` | | `65532` | +| `metricsOperator.manager.containerSecurityContext.runAsNonRoot` | | `true` | +| `metricsOperator.manager.containerSecurityContext.runAsUser` | | `65532` | +| `metricsOperator.manager.containerSecurityContext.seccompProfile.type` | | `RuntimeDefault` | +| `metricsOperator.manager.image.repository` | specify registry for manager image | `ghcr.io/keptn/metrics-operator` | +| `metricsOperator.manager.image.tag` | select tag for manager image | `v0.7.1` | +| `metricsOperator.manager.env.exposeKeptnMetrics` | enable metrics exporter | `true` | +| `metricsOperator.manager.env.metricsControllerLogLevel` | sets the log level of Metrics Controller | `0` | +| `metricsOperator.manager.livenessProbe` | custom livenessprobe for manager container | | +| `metricsOperator.manager.readinessProbe` | custom readinessprobe for manager container | | +| `metricsOperator.manager.resources` | specify limits and requests for manager container | | ### Global diff --git a/helm/chart/values.yaml b/helm/chart/values.yaml index 3249d7393d..0d3daeb98c 100644 --- a/helm/chart/values.yaml +++ b/helm/chart/values.yaml @@ -14,7 +14,7 @@ certificateOperator: labelSelectorKey: keptn.sh/inject-cert labelSelectorValue: "true" image: - repository: ghcr.keptn.sh/keptn/certificate-operator + repository: ghcr.io/keptn/certificate-operator tag: v0.7.1 imagePullPolicy: Always livenessProbe: @@ -67,7 +67,7 @@ lifecycleOperator: seccompProfile: type: RuntimeDefault env: - functionRunnerImage: ghcr.keptn.sh/keptn/functions-runtime:v0.7.1 + functionRunnerImage: ghcr.io/keptn/functions-runtime:v0.7.1 keptnAppControllerLogLevel: "0" keptnAppCreationRequestControllerLogLevel: "0" keptnAppVersionControllerLogLevel: "0" @@ -79,7 +79,7 @@ lifecycleOperator: optionsControllerLogLevel: "0" otelCollectorUrl: otel-collector:4317 image: - repository: ghcr.keptn.sh/keptn/lifecycle-operator + repository: ghcr.io/keptn/lifecycle-operator tag: v0.7.1 imagePullPolicy: Always livenessProbe: @@ -146,7 +146,7 @@ metricsOperator: exposeKeptnMetrics: "true" metricsControllerLogLevel: "0" image: - repository: ghcr.keptn.sh/keptn/metrics-operator + repository: ghcr.io/keptn/metrics-operator tag: v0.7.1 livenessProbe: httpGet: @@ -209,7 +209,7 @@ scheduler: env: otelCollectorUrl: otel-collector:4317 image: - repository: ghcr.keptn.sh/keptn/scheduler + repository: ghcr.io/keptn/scheduler tag: v0.7.1 imagePullPolicy: Always livenessProbe: diff --git a/klt-cert-manager/Dockerfile b/klt-cert-manager/Dockerfile index 40bff7329e..efb8c7a815 100644 --- a/klt-cert-manager/Dockerfile +++ b/klt-cert-manager/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM --platform=$BUILDPLATFORM golang:1.20.3-alpine3.16 AS builder +FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine3.16 AS builder ENV CGO_ENABLED=0 @@ -13,7 +13,7 @@ RUN go mod download COPY ./ ./ # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -ARG CONTROLLER_TOOLS_VERSION=v0.11.4 +ARG CONTROLLER_TOOLS_VERSION=v0.12.0 RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@$CONTROLLER_TOOLS_VERSION ARG GIT_HASH diff --git a/klt-cert-manager/Makefile b/klt-cert-manager/Makefile index 0531d87b33..22c24843a6 100644 --- a/klt-cert-manager/Makefile +++ b/klt-cert-manager/Makefile @@ -118,9 +118,9 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions # renovate: datasource=github-tags depName=kubernetes-sigs/kustomize -KUSTOMIZE_VERSION?=v5.0.1 +KUSTOMIZE_VERSION?=v5.0.3 # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -CONTROLLER_TOOLS_VERSION?=v0.11.4 +CONTROLLER_TOOLS_VERSION?=v0.12.0 KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize diff --git a/klt-cert-manager/README.md b/klt-cert-manager/README.md index 8842d2bc27..3d279c1dda 100644 --- a/klt-cert-manager/README.md +++ b/klt-cert-manager/README.md @@ -52,6 +52,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/keptn/lifecycle-toolkit/klt-cert-manager/controllers/keptnwebhookcontroller" + "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/certificates" + certCommon "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/common" "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/webhook" // +kubebuilder:scaffold:imports ) @@ -76,7 +78,20 @@ func main() { webhookBuilder := webhook.NewWebhookBuilder(). SetNamespace(env.PodNamespace). SetPodName(env.PodName). - SetConfigProvider(cmdConfig.NewKubeConfigProvider()) + SetConfigProvider(cmdConfig.NewKubeConfigProvider()). + SetManagerProvider( + webhook.NewWebhookManagerProvider( + mgr.GetWebhookServer().CertDir, "tls.key", "tls.crt"), + ). + SetCertificateWatcher( + certificates.NewCertificateWatcher( + mgr.GetAPIReader(), + mgr.GetWebhookServer().CertDir, + env.PodNamespace, + certCommon.SecretName, + setupLog, + ), + ) setupLog.Info("starting webhook and manager") if err := webhookBuilder.Run(mgr, map[string]*admission.Webhook{ diff --git a/klt-cert-manager/controllers/keptnwebhookcontroller/keptnwebhookcertificate_controller.go b/klt-cert-manager/controllers/keptnwebhookcontroller/keptnwebhookcertificate_controller.go index a085394d88..c1414121f4 100644 --- a/klt-cert-manager/controllers/keptnwebhookcontroller/keptnwebhookcertificate_controller.go +++ b/klt-cert-manager/controllers/keptnwebhookcontroller/keptnwebhookcertificate_controller.go @@ -180,7 +180,7 @@ func (r *KeptnWebhookCertificateReconciler) updateConfigurations(ctx context.Con } for i := range validatingWebhookConfigurationList.Items { - r.Log.Info("injecting certificate into validating webhook config", "vwc", mutatingWebhookConfigurationList.Items[i].Name) + r.Log.Info("injecting certificate into validating webhook config", "vwc", validatingWebhookConfigurationList.Items[i].Name) if err := r.updateClientConfigurations(ctx, bundle, validatingWebhookConfigs, &validatingWebhookConfigurationList.Items[i]); err != nil { return err } diff --git a/klt-cert-manager/controllers/keptnwebhookcontroller/webhook_cert_controller_test.go b/klt-cert-manager/controllers/keptnwebhookcontroller/webhook_cert_controller_test.go index bcec3a5e66..3e148b11db 100644 --- a/klt-cert-manager/controllers/keptnwebhookcontroller/webhook_cert_controller_test.go +++ b/klt-cert-manager/controllers/keptnwebhookcontroller/webhook_cert_controller_test.go @@ -146,7 +146,8 @@ func TestReconcile(t *testing.T) { t.Run(`reconcile successfully with mutatingwebhookconfiguration`, func(t *testing.T) { fakeClient := fake.NewClient(crd1, crd2, crd3, &admissionregistrationv1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-mutating-webhook-config", + Name: "my-mutating-webhook-config", + Labels: getMatchLabel(), }, Webhooks: []admissionregistrationv1.MutatingWebhook{ { @@ -167,7 +168,8 @@ func TestReconcile(t *testing.T) { t.Run(`reconcile successfully with validatingwebhookconfiguration`, func(t *testing.T) { fakeClient := fake.NewClient(crd1, crd2, crd3, &admissionregistrationv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-validating-webhook-config", + Name: "my-validating-webhook-config", + Labels: getMatchLabel(), }, Webhooks: []admissionregistrationv1.ValidatingWebhook{ { diff --git a/klt-cert-manager/go.mod b/klt-cert-manager/go.mod index 96316b35ba..616046294c 100644 --- a/klt-cert-manager/go.mod +++ b/klt-cert-manager/go.mod @@ -7,11 +7,11 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/pkg/errors v0.9.1 github.com/spf13/afero v1.9.5 - github.com/stretchr/testify v1.8.2 - k8s.io/api v0.26.4 - k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery v0.26.4 - k8s.io/client-go v0.26.4 + github.com/stretchr/testify v1.8.3 + k8s.io/api v0.26.5 + k8s.io/apiextensions-apiserver v0.26.5 + k8s.io/apimachinery v0.26.5 + k8s.io/client-go v0.26.5 sigs.k8s.io/controller-runtime v0.14.6 ) @@ -51,7 +51,7 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.7.0 // indirect golang.org/x/term v0.7.0 // indirect @@ -63,7 +63,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.26.4 // indirect + k8s.io/component-base v0.26.5 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect diff --git a/klt-cert-manager/go.sum b/klt-cert-manager/go.sum index 0d0b73d95f..0f2baa0abc 100644 --- a/klt-cert-manager/go.sum +++ b/klt-cert-manager/go.sum @@ -38,28 +38,21 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= -github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -67,7 +60,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -75,26 +67,19 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= -github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.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/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -120,10 +105,7 @@ github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXym 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-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -157,14 +139,10 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -180,8 +158,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -195,7 +171,6 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -203,15 +178,12 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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/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= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -235,7 +207,6 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -244,13 +215,9 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -262,11 +229,10 @@ 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-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -281,8 +247,6 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -295,8 +259,6 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -304,16 +266,10 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -321,21 +277,14 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -347,17 +296,12 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -417,8 +361,6 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -453,7 +395,6 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -470,7 +411,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -488,7 +428,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= @@ -550,7 +489,6 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -607,7 +545,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -622,7 +559,6 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -636,12 +572,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -654,23 +587,19 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -690,20 +619,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= -k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= -k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= -k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= -k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= -k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= -k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= -k8s.io/component-base v0.26.4 h1:Bg2xzyXNKL3eAuiTEu3XE198d6z22ENgFgGQv2GGOUk= -k8s.io/component-base v0.26.4/go.mod h1:lTuWL1Xz/a4e80gmIC3YZG2JCO4xNwtKWHJWeJmsq20= +k8s.io/api v0.26.5 h1:Npao/+sMSng6nkEcNydgH3BNo4s5YoBg7iw35HM7Hcw= +k8s.io/api v0.26.5/go.mod h1:O7ICW7lj6+ZQQQ3cxekgCoW+fnGo5kWT0nTHkLZ5grc= +k8s.io/apiextensions-apiserver v0.26.5 h1:VJ946z9RjyCPn3qiz4Kus/UYjCRrdn1xUvEsJFvN5Yo= +k8s.io/apiextensions-apiserver v0.26.5/go.mod h1:Olsde7ZNWnyz9rsL13iXYXmL1h7kWujtKeC3yWVCDPo= +k8s.io/apimachinery v0.26.5 h1:hTQVhJao2piX7vSgCn4Lwd6E0o/+TJIH4NqRf+q4EmE= +k8s.io/apimachinery v0.26.5/go.mod h1:HUvk6wrOP4v22AIYqeCGSQ6xWCHo41J9d6psb3temAg= +k8s.io/client-go v0.26.5 h1:e8Z44pafL/c6ayF/6qYEypbJoDSakaFxhJ9lqULEJEo= +k8s.io/client-go v0.26.5/go.mod h1:/CYyNt+ZLMvWqMF8h1SvkUXz2ujFWQLwdDrdiQlZ5X0= +k8s.io/component-base v0.26.5 h1:nHAzDvXQ4whYpOqrQGWrDIYI/GIeXkuxzqC/iVICfZo= +k8s.io/component-base v0.26.5/go.mod h1:wvfNAS05EtKdPeUxFceo8WNh8bGPcFY8QfPhv5MYjA4= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= diff --git a/metrics-operator/Dockerfile b/metrics-operator/Dockerfile index 1ff6ef5afc..c552f5d3c0 100644 --- a/metrics-operator/Dockerfile +++ b/metrics-operator/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.20.3-alpine3.16 AS builder +FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine3.16 AS builder ENV CGO_ENABLED=0 @@ -8,7 +8,7 @@ COPY go.mod go.sum ./ RUN go mod download # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -ARG CONTROLLER_TOOLS_VERSION=v0.11.4 +ARG CONTROLLER_TOOLS_VERSION=v0.12.0 RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@$CONTROLLER_TOOLS_VERSION # Copy the go source diff --git a/metrics-operator/Makefile b/metrics-operator/Makefile index 5852ff5548..0a101faca3 100644 --- a/metrics-operator/Makefile +++ b/metrics-operator/Makefile @@ -25,9 +25,9 @@ ENVTEST_K8S_VERSION=1.24.2 ## Tool Versions # renovate: datasource=github-tags depName=kubernetes-sigs/kustomize -KUSTOMIZE_VERSION?=v5.0.1 +KUSTOMIZE_VERSION?=v5.0.3 # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -CONTROLLER_TOOLS_VERSION?=v0.11.4 +CONTROLLER_TOOLS_VERSION?=v0.12.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) diff --git a/metrics-operator/api/v1alpha3/keptnmetric_types.go b/metrics-operator/api/v1alpha3/keptnmetric_types.go index d4781566ec..5b4e507609 100644 --- a/metrics-operator/api/v1alpha3/keptnmetric_types.go +++ b/metrics-operator/api/v1alpha3/keptnmetric_types.go @@ -79,3 +79,7 @@ type KeptnMetricList struct { func init() { SchemeBuilder.Register(&KeptnMetric{}, &KeptnMetricList{}) } + +func (s *KeptnMetric) IsStatusSet() bool { + return s.Status.Value != "" +} diff --git a/metrics-operator/api/v1alpha3/keptnmetric_types_test.go b/metrics-operator/api/v1alpha3/keptnmetric_types_test.go new file mode 100644 index 0000000000..a7700f4017 --- /dev/null +++ b/metrics-operator/api/v1alpha3/keptnmetric_types_test.go @@ -0,0 +1,53 @@ +package v1alpha3 + +import ( + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestKeptnMetric_IsStatusSet(t *testing.T) { + type fields struct { + TypeMeta v1.TypeMeta + ObjectMeta v1.ObjectMeta + Spec KeptnMetricSpec + Status KeptnMetricStatus + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "No value set", + fields: fields{ + Status: KeptnMetricStatus{ + Value: "", + }, + }, + want: false, + }, + { + name: "we have a value", + fields: fields{ + Status: KeptnMetricStatus{ + Value: "1.0", + }, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &KeptnMetric{ + TypeMeta: tt.fields.TypeMeta, + ObjectMeta: tt.fields.ObjectMeta, + Spec: tt.fields.Spec, + Status: tt.fields.Status, + } + if got := s.IsStatusSet(); got != tt.want { + t.Errorf("IsStatusSet() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/metrics-operator/cmd/certificates/certificatehandler.go b/metrics-operator/cmd/certificates/certificatehandler.go deleted file mode 100644 index ba9450fad9..0000000000 --- a/metrics-operator/cmd/certificates/certificatehandler.go +++ /dev/null @@ -1,22 +0,0 @@ -package certificates - -import ( - "crypto/x509" - "encoding/pem" -) - -//go:generate moq -pkg fake -skip-ensure -out ./fake/certificatehandler_mock.go . ICertificateHandler -type ICertificateHandler interface { - Decode(data []byte) (p *pem.Block, rest []byte) - Parse(der []byte) (*x509.Certificate, error) -} - -type defaultCertificateHandler struct { -} - -func (c defaultCertificateHandler) Decode(data []byte) (p *pem.Block, rest []byte) { - return pem.Decode(data) -} -func (c defaultCertificateHandler) Parse(der []byte) (*x509.Certificate, error) { - return x509.ParseCertificate(der) -} diff --git a/metrics-operator/cmd/certificates/fake/certificatehandler_mock.go b/metrics-operator/cmd/certificates/fake/certificatehandler_mock.go deleted file mode 100644 index 45a5eacbae..0000000000 --- a/metrics-operator/cmd/certificates/fake/certificatehandler_mock.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "crypto/x509" - "encoding/pem" - "sync" -) - -// ICertificateHandlerMock is a mock implementation of certificates.ICertificateHandler. -// -// func TestSomethingThatUsesICertificateHandler(t *testing.T) { -// -// // make and configure a mocked certificates.ICertificateHandler -// mockedICertificateHandler := &ICertificateHandlerMock{ -// DecodeFunc: func(data []byte) (*pem.Block, []byte) { -// panic("mock out the Decode method") -// }, -// ParseFunc: func(der []byte) (*x509.Certificate, error) { -// panic("mock out the Parse method") -// }, -// } -// -// // use mockedICertificateHandler in code that requires certificates.ICertificateHandler -// // and then make assertions. -// -// } -type ICertificateHandlerMock struct { - // DecodeFunc mocks the Decode method. - DecodeFunc func(data []byte) (*pem.Block, []byte) - - // ParseFunc mocks the Parse method. - ParseFunc func(der []byte) (*x509.Certificate, error) - - // calls tracks calls to the methods. - calls struct { - // Decode holds details about calls to the Decode method. - Decode []struct { - // Data is the data argument value. - Data []byte - } - // Parse holds details about calls to the Parse method. - Parse []struct { - // Der is the der argument value. - Der []byte - } - } - lockDecode sync.RWMutex - lockParse sync.RWMutex -} - -// Decode calls DecodeFunc. -func (mock *ICertificateHandlerMock) Decode(data []byte) (*pem.Block, []byte) { - if mock.DecodeFunc == nil { - panic("ICertificateHandlerMock.DecodeFunc: method is nil but ICertificateHandler.Decode was just called") - } - callInfo := struct { - Data []byte - }{ - Data: data, - } - mock.lockDecode.Lock() - mock.calls.Decode = append(mock.calls.Decode, callInfo) - mock.lockDecode.Unlock() - return mock.DecodeFunc(data) -} - -// DecodeCalls gets all the calls that were made to Decode. -// Check the length with: -// -// len(mockedICertificateHandler.DecodeCalls()) -func (mock *ICertificateHandlerMock) DecodeCalls() []struct { - Data []byte -} { - var calls []struct { - Data []byte - } - mock.lockDecode.RLock() - calls = mock.calls.Decode - mock.lockDecode.RUnlock() - return calls -} - -// Parse calls ParseFunc. -func (mock *ICertificateHandlerMock) Parse(der []byte) (*x509.Certificate, error) { - if mock.ParseFunc == nil { - panic("ICertificateHandlerMock.ParseFunc: method is nil but ICertificateHandler.Parse was just called") - } - callInfo := struct { - Der []byte - }{ - Der: der, - } - mock.lockParse.Lock() - mock.calls.Parse = append(mock.calls.Parse, callInfo) - mock.lockParse.Unlock() - return mock.ParseFunc(der) -} - -// ParseCalls gets all the calls that were made to Parse. -// Check the length with: -// -// len(mockedICertificateHandler.ParseCalls()) -func (mock *ICertificateHandlerMock) ParseCalls() []struct { - Der []byte -} { - var calls []struct { - Der []byte - } - mock.lockParse.RLock() - calls = mock.calls.Parse - mock.lockParse.RUnlock() - return calls -} diff --git a/metrics-operator/cmd/certificates/watcher.go b/metrics-operator/cmd/certificates/watcher.go deleted file mode 100644 index 3621679ccc..0000000000 --- a/metrics-operator/cmd/certificates/watcher.go +++ /dev/null @@ -1,132 +0,0 @@ -package certificates - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/go-logr/logr" - "github.com/spf13/afero" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - certificateRenewalInterval = 6 * time.Hour - ServerKey = "tls.key" - ServerCert = "tls.crt" - CertThreshold = 5 * time.Minute -) - -type CertificateWatcher struct { - apiReader client.Reader - fs afero.Fs - certificateDirectory string - namespace string - certificateSecretName string - certificateTreshold time.Duration - ICertificateHandler - Log logr.Logger -} - -func NewCertificateWatcher(reader client.Reader, certDir string, namespace string, secretName string, log logr.Logger) *CertificateWatcher { - return &CertificateWatcher{ - apiReader: reader, - fs: afero.NewOsFs(), - certificateDirectory: certDir, - namespace: namespace, - certificateSecretName: secretName, - ICertificateHandler: defaultCertificateHandler{}, - certificateTreshold: CertThreshold, - Log: log, - } -} - -func (watcher *CertificateWatcher) watchForCertificatesSecret() { - for { - <-time.After(certificateRenewalInterval) - watcher.Log.Info("checking for new certificates") - if err := watcher.updateCertificatesFromSecret(); err != nil { - watcher.Log.Error(err, "failed to update certificates") - } else { - watcher.Log.Info("updated certificate successfully") - } - } -} - -func (watcher *CertificateWatcher) updateCertificatesFromSecret() error { - var secret corev1.Secret - - err := watcher.apiReader.Get(context.TODO(), - client.ObjectKey{Name: watcher.certificateSecretName, Namespace: watcher.namespace}, &secret) - if err != nil { - return err - } - - watcher.Log.Info("checking dir", "watcher.certificateDirectory ", watcher.certificateDirectory) - if _, err = watcher.fs.Stat(watcher.certificateDirectory); os.IsNotExist(err) { - err = watcher.fs.MkdirAll(watcher.certificateDirectory, 0755) - if err != nil { - return fmt.Errorf("could not create cert directory: %w", err) - } - } - - for _, filename := range []string{ServerCert, ServerKey} { - if err = watcher.ensureCertificateFile(secret, filename); err != nil { - return err - } - } - isValid, err := watcher.ValidateCertificateExpiration(secret.Data[ServerCert], certificateRenewalInterval, time.Now()) - if err != nil { - return err - } else if !isValid { - return fmt.Errorf("certificate is outdated") - } - return nil -} - -func (watcher *CertificateWatcher) ensureCertificateFile(secret corev1.Secret, filename string) error { - f := filepath.Join(watcher.certificateDirectory, filename) - data, err := afero.ReadFile(watcher.fs, f) - if os.IsNotExist(err) || !bytes.Equal(data, secret.Data[filename]) { - return afero.WriteFile(watcher.fs, f, secret.Data[filename], 0666) - } - return err - -} - -func (watcher *CertificateWatcher) WaitForCertificates() { - for threshold := time.Now().Add(watcher.certificateTreshold); time.Now().Before(threshold); { - - if err := watcher.updateCertificatesFromSecret(); err != nil { - if k8serrors.IsNotFound(err) { - watcher.Log.Info("waiting for certificate secret to be available.") - } else { - watcher.Log.Error(err, "failed to update certificates") - } - time.Sleep(10 * time.Second) - continue - } - break - } - go watcher.watchForCertificatesSecret() -} - -func (watcher *CertificateWatcher) ValidateCertificateExpiration(certData []byte, renewalThreshold time.Duration, now time.Time) (bool, error) { - if block, _ := watcher.Decode(certData); block == nil { - watcher.Log.Error(errors.New("can't decode PEM file"), "failed to parse certificate") - return false, nil - } else if cert, err := watcher.Parse(block.Bytes); err != nil { - watcher.Log.Error(err, "failed to parse certificate") - return false, err - } else if now.After(cert.NotAfter.Add(-renewalThreshold)) { - watcher.Log.Info("certificate is outdated, waiting for new ones", "Valid until", cert.NotAfter.UTC()) - return false, nil - } - return true, nil -} diff --git a/metrics-operator/cmd/certificates/watcher_test.go b/metrics-operator/cmd/certificates/watcher_test.go deleted file mode 100644 index 4f4301e313..0000000000 --- a/metrics-operator/cmd/certificates/watcher_test.go +++ /dev/null @@ -1,318 +0,0 @@ -package certificates - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "os" - "path/filepath" - "testing" - "time" - - "github.com/go-logr/logr/testr" - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/certificates/fake" - fakeclient "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/fake" - "github.com/pkg/errors" - "github.com/spf13/afero" - "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const CACERT = `-----BEGIN CERTIFICATE----- -MIICPTCCAeKgAwIBAgIRAMIV/0UqFGHgKSYOWBdx/KcwCgYIKoZIzj0EAwIwczEL -MAkGA1UEBhMCQVQxCzAJBgNVBAgTAktMMRMwEQYDVQQHEwpLbGFnZW5mdXJ0MQ4w -DAYDVQQKEwVLZXB0bjEZMBcGA1UECxMQTGlmZWN5Y2xlVG9vbGtpdDEXMBUGA1UE -AwwOKi5rZXB0bi1ucy5zdmMwHhcNMjMwNDE5MTEwNDUzWhcNMjQwNDE4MTEwNDUz -WjBzMQswCQYDVQQGEwJBVDELMAkGA1UECBMCS0wxEzARBgNVBAcTCktsYWdlbmZ1 -cnQxDjAMBgNVBAoTBUtlcHRuMRkwFwYDVQQLExBMaWZlY3ljbGVUb29sa2l0MRcw -FQYDVQQDDA4qLmtlcHRuLW5zLnN2YzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA -BPxAP4JTJfwKz/P32dXuyfVi7kinQPebSYwF/gRAUcN0dCAi6GnxbI2OXlcU0guD -zHXv3VRh3EX2fiNszcfKaCajVzBVMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAK -BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQUGe/8XYV1HsZs -nWsyrOCCGr/sQDAKBggqhkjOPQQDAgNJADBGAiEAkcPaCANDXW5Uillrof0VrnPw -ow49D22Gsrh7YM+vmTQCIQDU1L5IT0Zz+bdIyFSsDnEUXZDeydNv56DoSLh+358Y -aw== ------END CERTIFICATE-----` - -const CAKEY = `-----BEGIN PRIVATE KEY----- -MHcCAQEEII5SAqBxINKatksyu2mTvLZZhfEOpNinYJDwlQjkfreboAoGCCqGSM49 -AwEHoUQDQgAE/EA/glMl/ArP8/fZ1e7J9WLuSKdA95tJjAX+BEBRw3R0ICLoafFs -jY5eVxTSC4PMde/dVGHcRfZ+I2zNx8poJg== ------END PRIVATE KEY-----` - -const uniqueIDPEM = `-----BEGIN CERTIFICATE----- -MIIFsDCCBJigAwIBAgIIrOyC1ydafZMwDQYJKoZIhvcNAQEFBQAwgY4xgYswgYgG -A1UEAx6BgABNAGkAYwByAG8AcwBvAGYAdAAgAEYAbwByAGUAZgByAG8AbgB0ACAA -VABNAEcAIABIAFQAVABQAFMAIABJAG4AcwBwAGUAYwB0AGkAbwBuACAAQwBlAHIA -dABpAGYAaQBjAGEAdABpAG8AbgAgAEEAdQB0AGgAbwByAGkAdAB5MB4XDTE0MDEx -ODAwNDEwMFoXDTE1MTExNTA5Mzc1NlowgZYxCzAJBgNVBAYTAklEMRAwDgYDVQQI -EwdqYWthcnRhMRIwEAYDVQQHEwlJbmRvbmVzaWExHDAaBgNVBAoTE3N0aG9ub3Jl -aG90ZWxyZXNvcnQxHDAaBgNVBAsTE3N0aG9ub3JlaG90ZWxyZXNvcnQxJTAjBgNV -BAMTHG1haWwuc3Rob25vcmVob3RlbHJlc29ydC5jb20wggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCvuu0qpI+Ko2X84Twkf84cRD/rgp6vpgc5Ebejx/D4 -PEVON5edZkazrMGocK/oQqIlRxx/lefponN/chlGcllcVVPWTuFjs8k+Aat6T1qp -4iXxZekAqX+U4XZMIGJD3PckPL6G2RQSlF7/LhGCsRNRdKpMWSTbou2Ma39g52Kf -gsl3SK/GwLiWpxpcSkNQD1hugguEIsQYLxbeNwpcheXZtxbBGguPzQ7rH8c5vuKU -BkMOzaiNKLzHbBdFSrua8KWwCJg76Vdq/q36O9GlW6YgG3i+A4pCJjXWerI1lWwX -Ktk5V+SvUHGey1bkDuZKJ6myMk2pGrrPWCT7jP7WskChAgMBAAGBCQBCr1dgEleo -cKOCAfswggH3MIHDBgNVHREEgbswgbiCHG1haWwuc3Rob25vcmVob3RlbHJlc29y -dC5jb22CIGFzaGNoc3ZyLnN0aG9ub3JlaG90ZWxyZXNvcnQuY29tgiRBdXRvRGlz -Y292ZXIuc3Rob25vcmVob3RlbHJlc29ydC5jb22CHEF1dG9EaXNjb3Zlci5ob3Rl -bHJlc29ydC5jb22CCEFTSENIU1ZSghdzdGhvbm9yZWhvdGVscmVzb3J0LmNvbYIP -aG90ZWxyZXNvcnQuY29tMCEGCSsGAQQBgjcUAgQUHhIAVwBlAGIAUwBlAHIAdgBl -AHIwHQYDVR0OBBYEFMAC3UR4FwAdGekbhMgnd6lMejtbMAsGA1UdDwQEAwIFoDAT -BgNVHSUEDDAKBggrBgEFBQcDATAJBgNVHRMEAjAAMIG/BgNVHQEEgbcwgbSAFGfF -6xihk+gJJ5TfwvtWe1UFnHLQoYGRMIGOMYGLMIGIBgNVBAMegYAATQBpAGMAcgBv -AHMAbwBmAHQAIABGAG8AcgBlAGYAcgBvAG4AdAAgAFQATQBHACAASABUAFQAUABT -ACAASQBuAHMAcABlAGMAdABpAG8AbgAgAEMAZQByAHQAaQBmAGkAYwBhAHQAaQBv -AG4AIABBAHUAdABoAG8AcgBpAHQAeYIIcKhXEmBXr0IwDQYJKoZIhvcNAQEFBQAD -ggEBABlSxyCMr3+ANr+WmPSjyN5YCJBgnS0IFCwJAzIYP87bcTye/U8eQ2+E6PqG -Q7Huj7nfHEw9qnGo+HNyPp1ad3KORzXDb54c6xEoi+DeuPzYHPbn4c3hlH49I0aQ -eWW2w4RslSWpLvO6Y7Lboyz2/Thk/s2kd4RHxkkWpH2ltPqJuYYg3X6oM5+gIFHJ -WGnh+ojZ5clKvS5yXh3Wkj78M6sb32KfcBk0Hx6NkCYPt60ODYmWtvqwtw6r73u5 -TnTYWRNvo2svX69TriL+CkHY9O1Hkwf2It5zHl3gNiKTJVaak8AuEz/CKWZneovt -yYLwhUhg3PX5Co1VKYE+9TxloiE= ------END CERTIFICATE-----` - -var ERR_BAD_CERT = errors.New("bad cert") - -var emptySecret = v1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "my-cert", - }, -} - -var goodSecret = v1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "my-cert", - }, - Data: map[string][]byte{ - ServerCert: []byte(CACERT), - ServerKey: []byte(CAKEY), - }, -} - -func TestCertificateWatcher_ValidateCertificateExpiration(t *testing.T) { - - tests := []struct { - name string - certHandler ICertificateHandler - certData []byte - renewalThreshold time.Duration - now time.Time - want bool - wantErr error - }{ - { - name: "certificate cannot be decoded", - certHandler: &fake.ICertificateHandlerMock{ - DecodeFunc: func(data []byte) (p *pem.Block, rest []byte) { - return nil, nil //fake a failure in the decoding - }, - ParseFunc: nil, - }, - want: false, - }, - { - name: "certificate cannot be parsed", - certHandler: &fake.ICertificateHandlerMock{ - DecodeFunc: func(data []byte) (p *pem.Block, rest []byte) { - return &pem.Block{Type: "test", Bytes: []byte("testdata")}, nil - }, - ParseFunc: func(der []byte) (*x509.Certificate, error) { - return nil, ERR_BAD_CERT - }, - }, - want: false, - wantErr: ERR_BAD_CERT, - }, - { - name: "good certificate - unexpired", - certData: []byte(uniqueIDPEM), - certHandler: defaultCertificateHandler{}, - want: true, - }, - { - name: "good certificate - expired", - certData: []byte(uniqueIDPEM), - now: time.Now(), //setting up now makes sure that the threshold is passed - certHandler: defaultCertificateHandler{}, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - ICertificateHandler: tt.certHandler, - Log: testr.New(t), - } - got, err := watcher.ValidateCertificateExpiration(tt.certData, tt.renewalThreshold, tt.now) - if tt.wantErr != nil { - require.Error(t, err) - t.Log("want:", tt.wantErr, "got:", err) - require.True(t, errors.Is(tt.wantErr, err)) - } - require.Equal(t, got, tt.want) - }) - } -} - -func TestCertificateWatcher_ensureCertificateFile(t *testing.T) { - - certdir := t.TempDir() - f := filepath.Join(certdir, ServerCert) - err := os.WriteFile(f, goodSecret.Data[ServerCert], 0666) - require.Nil(t, err) - baddir := t.TempDir() - f = filepath.Join(baddir, ServerCert) - err = os.WriteFile(f, goodSecret.Data[ServerKey], 0666) - require.Nil(t, err) - tests := []struct { - name string - fs afero.Fs - secret v1.Secret - filename string - certDir string - wantErr bool - err string - }{ - { - name: "if good cert exist in fs no error", - secret: goodSecret, - certDir: certdir, - filename: ServerCert, - wantErr: false, - }, - - { - name: "if unexisting file name, we expect a file system error", - secret: emptySecret, - filename: "$%&/())=$§%/=", - certDir: baddir, - wantErr: true, - err: "no such file or directory", - }, - - { - name: "wrong file content is replaced with updated cert", - certDir: baddir, - secret: goodSecret, - filename: ServerCert, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - fs: afero.NewOsFs(), - certificateDirectory: tt.certDir, - } - err := watcher.ensureCertificateFile(tt.secret, tt.filename) - if !tt.wantErr { - require.Nil(t, err) - f = filepath.Join(tt.certDir, ServerCert) - data, err := os.ReadFile(f) - if err != nil { - panic(err) - } - if !bytes.Equal(data, tt.secret.Data[tt.filename]) { - t.Errorf("ensureCertificateFile()data %v was not replaced with %v", data, tt.secret.Data[tt.filename]) - } - } else { - require.Contains(t, err.Error(), tt.err) - } - }) - } -} - -func TestCertificateWatcher_updateCertificatesFromSecret(t *testing.T) { - - oldDir := t.TempDir() - os.Remove(oldDir) - - tests := []struct { - name string - apiReader client.Reader - certificateDirectory string - namespace string - certificateSecretName string - wantErr error - }{ - { - name: "certificate not found", - apiReader: fakeclient.NewClient(), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("secrets \"my-cert\" not found"), - }, - { - name: "outdated certificate found, nothing in dir", - apiReader: fakeclient.NewClient(&emptySecret), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("certificate is outdated"), - }, - - { - name: "outdated certificate found, not existing in dir", - apiReader: fakeclient.NewClient(&emptySecret), - certificateDirectory: oldDir, - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("certificate is outdated"), - }, - { - name: "good certificate - not stored", - apiReader: fakeclient.NewClient(&goodSecret), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - apiReader: tt.apiReader, - fs: afero.NewOsFs(), - certificateDirectory: tt.certificateDirectory, - namespace: tt.namespace, - certificateSecretName: tt.certificateSecretName, - ICertificateHandler: defaultCertificateHandler{}, - Log: testr.New(t), - } - err := watcher.updateCertificatesFromSecret() - if tt.wantErr == nil { - require.Nil(t, err) - } else { - require.NotNil(t, err) - require.Contains(t, err.Error(), tt.wantErr.Error()) - } - }) - } -} - -func TestNewCertificateWatcher(t *testing.T) { - logger := testr.New(t) - client := fakeclient.NewClient() - want := &CertificateWatcher{ - apiReader: client, - fs: afero.NewOsFs(), - namespace: "default", - certificateSecretName: "my-secret", - certificateDirectory: "test", - certificateTreshold: CertThreshold, - ICertificateHandler: defaultCertificateHandler{}, - Log: testr.New(t), - } - got := NewCertificateWatcher(client, "test", "default", "my-secret", logger) - require.EqualValues(t, got, want) - -} diff --git a/metrics-operator/cmd/webhook/builder.go b/metrics-operator/cmd/webhook/builder.go deleted file mode 100644 index 311acff4ac..0000000000 --- a/metrics-operator/cmd/webhook/builder.go +++ /dev/null @@ -1,85 +0,0 @@ -package webhook - -import ( - "flag" - - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/certificates" - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/config" - cmdManager "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/manager" - "github.com/pkg/errors" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -const ( - flagCertificateDirectory = "certs-dir" - flagCertificateFileName = "cert" - flagCertificateKeyFileName = "cert-key" - secretCertsName = "klt-certs" -) - -var ( - certificateDirectory string - certificateFileName string - certificateKeyFileName string -) - -type Builder struct { - configProvider config.Provider - managerProvider cmdManager.Provider - namespace string - podName string -} - -func NewWebhookBuilder() Builder { - return Builder{} -} - -func (builder Builder) SetConfigProvider(provider config.Provider) Builder { - builder.configProvider = provider - return builder -} - -func (builder Builder) SetManagerProvider(provider cmdManager.Provider) Builder { - builder.managerProvider = provider - return builder -} - -func (builder Builder) SetNamespace(namespace string) Builder { - builder.namespace = namespace - return builder -} - -func (builder Builder) SetPodName(podName string) Builder { - builder.podName = podName - return builder -} - -func (builder Builder) GetManagerProvider() cmdManager.Provider { - if builder.managerProvider == nil { - builder.managerProvider = NewWebhookManagerProvider(certificateDirectory, certificateKeyFileName, certificateFileName) - } - - return builder.managerProvider -} - -func (builder Builder) Run(webhookManager manager.Manager) error { - - addFlags() - builder.GetManagerProvider().SetupWebhookServer(webhookManager) - - certificates. - NewCertificateWatcher(webhookManager.GetAPIReader(), webhookManager.GetWebhookServer().CertDir, builder.namespace, secretCertsName, ctrl.Log.WithName("Webhook Cert Manager")). - WaitForCertificates() - - signalHandler := ctrl.SetupSignalHandler() - err := webhookManager.Start(signalHandler) - return errors.WithStack(err) -} - -func addFlags() { - flag.StringVar(&certificateDirectory, flagCertificateDirectory, "/tmp/webhook/certs", "Directory to look certificates for.") - flag.StringVar(&certificateFileName, flagCertificateFileName, "tls.crt", "File name for the public certificate.") - flag.StringVar(&certificateKeyFileName, flagCertificateKeyFileName, "tls.key", "File name for the private key.") - flag.Parse() -} diff --git a/metrics-operator/cmd/webhook/builder_test.go b/metrics-operator/cmd/webhook/builder_test.go deleted file mode 100644 index fad6091c7d..0000000000 --- a/metrics-operator/cmd/webhook/builder_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package webhook - -import ( - "testing" - - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/fake" - "github.com/stretchr/testify/assert" -) - -func TestWebhookCommandBuilder(t *testing.T) { - - t.Run("set config provider", func(t *testing.T) { - builder := NewWebhookBuilder() - - assert.NotNil(t, builder) - - expectedProvider := &fake.MockProvider{} - builder = builder.SetConfigProvider(expectedProvider) - - assert.Equal(t, expectedProvider, builder.configProvider) - }) - t.Run("set manager provider", func(t *testing.T) { - expectedProvider := &fake.MockWebhookManager{} - builder := NewWebhookBuilder().SetManagerProvider(expectedProvider) - - assert.Equal(t, expectedProvider, builder.managerProvider) - }) - t.Run("set namespace", func(t *testing.T) { - builder := NewWebhookBuilder().SetNamespace("namespace") - - assert.Equal(t, "namespace", builder.namespace) - }) -} diff --git a/metrics-operator/cmd/webhook/manager.go b/metrics-operator/cmd/webhook/manager.go deleted file mode 100644 index 0e0fedccc6..0000000000 --- a/metrics-operator/cmd/webhook/manager.go +++ /dev/null @@ -1,43 +0,0 @@ -package webhook - -import ( - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -const ( - metricsBindAddress = ":8383" - port = 8443 -) - -type WebhookProvider struct { - certificateDirectory string - certificateFileName string - keyFileName string -} - -func NewWebhookManagerProvider(certificateDirectory string, keyFileName string, certificateFileName string) WebhookProvider { - return WebhookProvider{ - certificateDirectory: certificateDirectory, - certificateFileName: certificateFileName, - keyFileName: keyFileName, - } -} - -func (provider WebhookProvider) createOptions(scheme *runtime.Scheme, namespace string) ctrl.Options { - return ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsBindAddress, - Port: port, - Namespace: namespace, - } -} - -func (provider WebhookProvider) SetupWebhookServer(mgr manager.Manager) { - webhookServer := mgr.GetWebhookServer() - webhookServer.CertDir = provider.certificateDirectory - webhookServer.KeyName = provider.keyFileName - webhookServer.CertName = provider.certificateFileName - -} diff --git a/metrics-operator/cmd/webhook/manager_test.go b/metrics-operator/cmd/webhook/manager_test.go deleted file mode 100644 index 413fbf357f..0000000000 --- a/metrics-operator/cmd/webhook/manager_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package webhook - -import ( - "testing" - - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/fake" - cmdManager "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/manager" - "github.com/stretchr/testify/assert" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/webhook" -) - -func TestCreateOptions(t *testing.T) { - - t.Run("implements interface", func(t *testing.T) { - var provider cmdManager.Provider = NewWebhookManagerProvider("certs-dir", "key-file", "cert-file") - - providerImpl := provider.(WebhookProvider) - assert.Equal(t, "certs-dir", providerImpl.certificateDirectory) - assert.Equal(t, "key-file", providerImpl.keyFileName) - assert.Equal(t, "cert-file", providerImpl.certificateFileName) - }) - t.Run("creates options", func(t *testing.T) { - provider := WebhookProvider{} - options := provider.createOptions(scheme.Scheme, "test-namespace") - - assert.NotNil(t, options) - assert.Equal(t, "test-namespace", options.Namespace) - assert.Equal(t, scheme.Scheme, options.Scheme) - assert.Equal(t, metricsBindAddress, options.MetricsBindAddress) - assert.Equal(t, port, options.Port) - }) - t.Run("configures webhooks server", func(t *testing.T) { - provider := NewWebhookManagerProvider("certs-dir", "key-file", "cert-file") - expectedWebhookServer := &webhook.Server{} - - mgr := &fake.MockManager{ - GetWebhookServerFunc: func() *webhook.Server { - return expectedWebhookServer - }, - } - - provider.SetupWebhookServer(mgr) - - assert.Equal(t, "certs-dir", expectedWebhookServer.CertDir) - assert.Equal(t, "key-file", expectedWebhookServer.KeyName) - assert.Equal(t, "cert-file", expectedWebhookServer.CertName) - - mgrWebhookServer := mgr.GetWebhookServer() - assert.Equal(t, "certs-dir", mgrWebhookServer.CertDir) - assert.Equal(t, "key-file", mgrWebhookServer.KeyName) - assert.Equal(t, "cert-file", mgrWebhookServer.CertName) - }) -} diff --git a/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetrics.yaml b/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetrics.yaml index 6ef7eb50d2..f3eb4d8471 100644 --- a/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetrics.yaml +++ b/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetrics.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnmetrics.metrics.keptn.sh spec: group: metrics.keptn.sh diff --git a/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetricsproviders.yaml b/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetricsproviders.yaml index 7a807c4c90..96f75f4c96 100644 --- a/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetricsproviders.yaml +++ b/metrics-operator/config/crd/bases/metrics.keptn.sh_keptnmetricsproviders.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnmetricsproviders.metrics.keptn.sh spec: group: metrics.keptn.sh diff --git a/metrics-operator/config/manager/manager.yaml b/metrics-operator/config/manager/manager.yaml index 433317c78d..cb44f8a113 100644 --- a/metrics-operator/config/manager/manager.yaml +++ b/metrics-operator/config/manager/manager.yaml @@ -45,8 +45,6 @@ spec: - /manager args: - webhook-server - # OLM mounts the certificates here, so we reuse it for simplicity - - --certs-dir=/tmp/k8s-webhook-server/serving-certs/ - --leader-elect # Secure port for the metrics adapter - --adapter-port=6443 diff --git a/metrics-operator/controllers/common/providers/datadog/datadog.go b/metrics-operator/controllers/common/providers/datadog/datadog.go index 54c8bd2081..19a873e45c 100644 --- a/metrics-operator/controllers/common/providers/datadog/datadog.go +++ b/metrics-operator/controllers/common/providers/datadog/datadog.go @@ -65,7 +65,13 @@ func (d *KeptnDataDogProvider) EvaluateQuery(ctx context.Context, metric metrics err = json.Unmarshal(b, &result) if err != nil { d.Log.Error(err, "Error while parsing response") - return "", nil, err + return "", b, err + } + + if result.Error != nil { + err = fmt.Errorf("%s", *result.Error) + d.Log.Error(err, "Error from DataDog provider") + return "", b, err } if len(result.Series) == 0 { @@ -76,7 +82,7 @@ func (d *KeptnDataDogProvider) EvaluateQuery(ctx context.Context, metric metrics points := (result.Series)[0].Pointlist if len(points) == 0 { d.Log.Info("No metric points in query result") - return "", nil, fmt.Errorf("no metric points in query result") + return "", b, fmt.Errorf("no metric points in query result") } r := d.getSingleValue(points) diff --git a/metrics-operator/controllers/common/providers/datadog/datadog_test.go b/metrics-operator/controllers/common/providers/datadog/datadog_test.go index 2c426bf5b4..7f4d9d9f85 100644 --- a/metrics-operator/controllers/common/providers/datadog/datadog_test.go +++ b/metrics-operator/controllers/common/providers/datadog/datadog_test.go @@ -18,14 +18,16 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" ) +const ddErrorPayload = "{\"error\":\"Token is missing required scope\"}" const ddPayload = "{\"from_date\":1677736306000,\"group_by\":[],\"message\":\"\",\"query\":\"system.cpu.idle{*}\",\"res_type\":\"time_series\",\"series\":[{\"aggr\":null,\"display_name\":\"system.cpu.idle\",\"end\":1677821999000,\"expression\":\"system.cpu.idle{*}\",\"interval\":300,\"length\":7,\"metric\":\"system.cpu.idle\",\"pointlist\":[[1677781200000,92.37997436523438],[1677781500000,91.46615447998047],[1677781800000,92.05865631103515],[1677782100000,97.49858474731445],[1677782400000,95.95263163248698],[1677821400000,69.67094268798829],[1677821700000,84.78184509277344]],\"query_index\":0,\"scope\":\"*\",\"start\":1677781200000,\"tag_set\":[],\"unit\":[{\"family\":\"percentage\",\"name\":\"percent\",\"plural\":\"percent\",\"scale_factor\":1,\"short_name\":\"%\"},{}]}],\"status\":\"ok\",\"to_date\":1677822706000}" const ddEmptyPayload = "{\"from_date\":1677736306000,\"group_by\":[],\"message\":\"\",\"query\":\"system.cpu.idle{*}\",\"res_type\":\"time_series\",\"series\":[],\"status\":\"ok\",\"to_date\":1677822706000}" -func TestEvaluateQuery_HappyPath(t *testing.T) { +func TestEvaluateQuery_APIError(t *testing.T) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(ddPayload)) + _, err := w.Write([]byte(ddErrorPayload)) require.Nil(t, err) })) defer svr.Close() @@ -43,13 +45,53 @@ func TestEvaluateQuery_HappyPath(t *testing.T) { appKey: []byte(appKeyValue), }, } - fakeClient := fake.NewClient(apiToken) + kdd := setupTest(apiToken) + metric := metricsapi.KeptnMetric{ + Spec: metricsapi.KeptnMetricSpec{ + Query: "system.cpu.idle{*}", + }, + } + b := true + p := metricsapi.KeptnMetricsProvider{ + Spec: metricsapi.KeptnMetricsProviderSpec{ + SecretKeyRef: v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: secretName, + }, + Optional: &b, + }, + TargetServer: svr.URL, + }, + } - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, + r, raw, e := kdd.EvaluateQuery(context.TODO(), metric, p) + require.Error(t, e) + require.Contains(t, e.Error(), "Token is missing required scope") + require.Equal(t, []byte(ddErrorPayload), raw) + require.Empty(t, r) +} + +func TestEvaluateQuery_HappyPath(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(ddPayload)) + require.Nil(t, err) + })) + defer svr.Close() + + secretName := "datadogSecret" + apiKey, apiKeyValue := "DD_CLIENT_API_KEY", "fake-api-key" + appKey, appKeyValue := "DD_CLIENT_APP_KEY", "fake-app-key" + apiToken := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: "", + }, + Data: map[string][]byte{ + apiKey: []byte(apiKeyValue), + appKey: []byte(appKeyValue), + }, } + kdd := setupTest(apiToken) metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -93,13 +135,7 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { appKey: []byte(appKeyValue), }, } - fakeClient := fake.NewClient(apiToken) - - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest(apiToken) metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -120,7 +156,7 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { r, raw, e := kdd.EvaluateQuery(context.TODO(), metric, p) require.Equal(t, "", r) - require.Equal(t, []byte(nil), raw) + require.Equal(t, []byte("garbage"), raw) require.NotNil(t, e) } func TestEvaluateQuery_MissingSecret(t *testing.T) { @@ -130,13 +166,7 @@ func TestEvaluateQuery_MissingSecret(t *testing.T) { })) defer svr.Close() - fakeClient := fake.NewClient() - - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest() metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -159,14 +189,9 @@ func TestEvaluateQuery_SecretNotFound(t *testing.T) { })) defer svr.Close() - fakeClient := fake.NewClient() secretName := "datadogSecret" - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest() metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -207,13 +232,7 @@ func TestEvaluateQuery_RefNonExistingKey(t *testing.T) { apiKey: []byte(apiKeyValue), }, } - fakeClient := fake.NewClient(apiToken) - - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest(apiToken) metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -256,13 +275,7 @@ func TestEvaluateQuery_EmptyPayload(t *testing.T) { appKey: []byte(appKeyValue), }, } - fakeClient := fake.NewClient(apiToken) - - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest(apiToken) metric := metricsapi.KeptnMetric{ Spec: metricsapi.KeptnMetricSpec{ Query: "system.cpu.idle{*}", @@ -282,30 +295,22 @@ func TestEvaluateQuery_EmptyPayload(t *testing.T) { } r, raw, e := kdd.EvaluateQuery(context.TODO(), metric, p) + t.Log(string(raw)) require.Nil(t, raw) require.Equal(t, "", r) require.True(t, strings.Contains(e.Error(), "no values in query result")) } func TestGetSingleValue_EmptyPoints(t *testing.T) { - fakeClient := fake.NewClient() - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + kdd := setupTest() var points [][]*float64 value := kdd.getSingleValue(points) require.Zero(t, value) } func TestGetSingleValue_HappyPath(t *testing.T) { - fakeClient := fake.NewClient() - kdd := KeptnDataDogProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } + + kdd := setupTest() result := datadogV1.MetricsQueryResponse{} _ = json.Unmarshal([]byte(ddPayload), &result) points := (result.Series)[0].Pointlist @@ -314,3 +319,15 @@ func TestGetSingleValue_HappyPath(t *testing.T) { require.NotZero(t, value) require.Equal(t, 89.11554133097331, value) } + +func setupTest(objs ...client.Object) KeptnDataDogProvider { + + fakeClient := fake.NewClient(objs...) + + kdd := KeptnDataDogProvider{ + HttpClient: http.Client{}, + Log: ctrl.Log.WithName("testytest"), + K8sClient: fakeClient, + } + return kdd +} diff --git a/metrics-operator/controllers/common/providers/dynatrace/common.go b/metrics-operator/controllers/common/providers/dynatrace/common.go index 23ba4dab36..bded16027c 100644 --- a/metrics-operator/controllers/common/providers/dynatrace/common.go +++ b/metrics-operator/controllers/common/providers/dynatrace/common.go @@ -15,6 +15,13 @@ var ErrSecretKeyRefNotDefined = errors.New("the SecretKeyRef property with the D var ErrInvalidResult = errors.New("the answer does not contain any data") var ErrDQLQueryTimeout = errors.New("timed out waiting for result of DQL query") +const ErrAPIMsg = "provider api response: %s" + +type Error struct { + Code int `json:"-"` // optional + Message string `json:"message"` +} + func getDTSecret(ctx context.Context, provider metricsapi.KeptnMetricsProvider, k8sClient client.Client) (string, error) { if !provider.HasSecretDefined() { return "", ErrSecretKeyRefNotDefined diff --git a/metrics-operator/controllers/common/providers/dynatrace/dynatrace.go b/metrics-operator/controllers/common/providers/dynatrace/dynatrace.go index 5b95c8a817..5b819577d5 100644 --- a/metrics-operator/controllers/common/providers/dynatrace/dynatrace.go +++ b/metrics-operator/controllers/common/providers/dynatrace/dynatrace.go @@ -6,6 +6,8 @@ import ( "fmt" "io" "net/http" + "net/url" + "reflect" "strings" "time" @@ -24,6 +26,7 @@ type DynatraceResponse struct { TotalCount int `json:"totalCount"` Resolution string `json:"resolution"` Result []DynatraceResult `json:"result"` + Error `json:"error"` } type DynatraceResult struct { @@ -39,7 +42,8 @@ type DynatraceData struct { // EvaluateQuery fetches the SLI values from dynatrace provider func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) { baseURL := d.normalizeAPIURL(provider.Spec.TargetServer) - qURL := baseURL + "v2/metrics/query?metricSelector=" + metric.Spec.Query + query := url.QueryEscape(metric.Spec.Query) + qURL := baseURL + "v2/metrics/query?metricSelector=" + query d.Log.Info("Running query: " + qURL) ctx, cancel := context.WithTimeout(ctx, 20*time.Second) @@ -57,6 +61,7 @@ func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metri req.Header.Set("Authorization", "Api-Token "+token) res, err := d.HttpClient.Do(req) + if err != nil { d.Log.Error(err, "Error while creating request") return "", nil, err @@ -74,9 +79,13 @@ func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metri err = json.Unmarshal(b, &result) if err != nil { d.Log.Error(err, "Error while parsing response") - return "", nil, err + return "", b, err + } + if !reflect.DeepEqual(result.Error, Error{}) { + err = fmt.Errorf(ErrAPIMsg, result.Error.Message) + d.Log.Error(err, "Error from Dynatrace provider") + return "", b, err } - r := fmt.Sprintf("%f", d.getSingleValue(result)) return r, b, nil } diff --git a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql.go b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql.go index b01b1c51f8..f134a7d2ba 100644 --- a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql.go +++ b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "net/url" + "reflect" "time" "github.com/benbjohnson/clock" @@ -36,6 +37,7 @@ type DynatraceDQLHandler struct { type DynatraceDQLResult struct { State string `json:"state"` Result DQLResult `json:"result,omitempty"` + Error `json:"error"` } type DQLResult struct { @@ -100,6 +102,7 @@ func (d *keptnDynatraceDQLProvider) EvaluateQuery(ctx context.Context, metric me d.log.Error(err, "Error while waiting for DQL query", "query", dqlHandler) return "", nil, err } + // parse result if len(results.Records) > 1 { d.log.Info("More than a single result, the first one will be used") @@ -195,5 +198,11 @@ func (d *keptnDynatraceDQLProvider) retrieveDQLResults(ctx context.Context, hand d.log.Error(err, "Error while parsing response") return result, err } + + if !reflect.DeepEqual(result.Error, Error{}) { + err = fmt.Errorf(ErrAPIMsg, result.Error.Message) + d.log.Error(err, "Error from Dynatrace DQL provider") + return nil, err + } return result, nil } diff --git a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql_test.go b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql_test.go index 864d4c17ac..7ace591c61 100644 --- a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql_test.go +++ b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_dql_test.go @@ -24,9 +24,12 @@ const dqlRequestHandler = `{"requestToken": "my-token"}` const dqlPayload = "{\"state\":\"SUCCEEDED\",\"result\":{\"records\":[{\"value\":{\"count\":1,\"sum\":36.50,\"min\":36.50,\"avg\":36.50,\"max\":36.50},\"metric.key\":\"dt.containers.cpu.usage_user_milli_cores\",\"timeframe\":{\"start\":\"2023-01-31T09:11:00.000Z\",\"end\":\"2023-01-31T09:12:00.`00Z\"},\"Container\":\"frontend\",\"host.name\":\"default-pool-349eb8c6-gccf\",\"k8s.namespace.name\":\"hipstershop\",\"k8s.pod.uid\":\"632df64d-474c-4410-968d-666f639ad358\"}],\"types\":[{\"mappings\":{\"value\":{\"type\":\"summary_stats\"},\"metric.key\":{\"type\":\"string\"},\"timeframe\":{\"type\":\"timeframe\"},\"Container\":{\"type\":\"string\"},\"host.name\":{\"type\":\"string\"},\"k8s.namespace.name\":{\"type\":\"string\"},\"k8s.pod.uid\":{\"type\":\"string\"}},\"indexRange\":[0,1]}]}}" const dqlPayloadNotFinished = "{\"state\":\"\",\"result\":{\"records\":[{\"value\":{\"count\":1,\"sum\":36.50,\"min\":36.78336878333334,\"avg\":36.50,\"max\":36.50},\"metric.key\":\"dt.containers.cpu.usage_user_milli_cores\",\"timeframe\":{\"start\":\"2023-01-31T09:11:00.000Z\",\"end\":\"2023-01-31T09:12:00.`00Z\"},\"Container\":\"frontend\",\"host.name\":\"default-pool-349eb8c6-gccf\",\"k8s.namespace.name\":\"hipstershop\",\"k8s.pod.uid\":\"632df64d-474c-4410-968d-666f639ad358\"}],\"types\":[{\"mappings\":{\"value\":{\"type\":\"summary_stats\"},\"metric.key\":{\"type\":\"string\"},\"timeframe\":{\"type\":\"timeframe\"},\"Container\":{\"type\":\"string\"},\"host.name\":{\"type\":\"string\"},\"k8s.namespace.name\":{\"type\":\"string\"},\"k8s.pod.uid\":{\"type\":\"string\"}},\"indexRange\":[0,1]}]}}" +const dqlPayloadError = "{\"error\":{\"code\":403,\"message\":\"Token is missing required scope\"}}" const dqlPayloadTooManyItems = "{\"state\":\"SUCCEEDED\",\"result\":{\"records\":[{\"value\":{\"count\":1,\"sum\":6.293549483333334,\"min\":6.293549483333334,\"avg\":6.293549483333334,\"max\":6.293549483333334},\"metric.key\":\"dt.containers.cpu.usage_user_milli_cores\",\"timeframe\":{\"start\":\"2023-01-31T09:07:00.000Z\",\"end\":\"2023-01-31T09:08:00.000Z\"},\"Container\":\"loginservice\",\"host.name\":\"default-pool-349eb8c6-gccf\",\"k8s.namespace.name\":\"easytrade\",\"k8s.pod.uid\":\"fc084e57-11a0-4a95-b8a0-76191c31d839\"},{\"value\":{\"count\":1,\"sum\":1.0421756,\"min\":1.0421756,\"avg\":1.0421756,\"max\":1.0421756},\"metric.key\":\"dt.containers.cpu.usage_user_milli_cores\",\"timeframe\":{\"start\":\"2023-01-31T09:07:00.000Z\",\"end\":\"2023-01-31T09:08:00.000Z\"},\"Container\":\"frontendreverseproxy\",\"host.name\":\"default-pool-349eb8c6-gccf\",\"k8s.namespace.name\":\"easytrade\",\"k8s.pod.uid\":\"41b5d6e0-98fc-4dce-a1b4-bb269a03d72b\"},{\"value\":{\"count\":1,\"sum\":6.3881383000000005,\"min\":6.3881383000000005,\"avg\":6.3881383000000005,\"max\":6.3881383000000005},\"metric.key\":\"dt.containers.cpu.usage_user_milli_cores\",\"timeframe\":{\"start\":\"2023-01-31T09:07:00.000Z\",\"end\":\"2023-01-31T09:08:00.000Z\"},\"Container\":\"shippingservice\",\"host.name\":\"default-pool-349eb8c6-gccf\",\"k8s.namespace.name\":\"hipstershop\",\"k8s.pod.uid\":\"96fcf9d7-748a-47f7-b1b3-ca6427e20edd\"}],\"types\":[{\"mappings\":{\"value\":{\"type\":\"summary_stats\"},\"metric.key\":{\"type\":\"string\"},\"timeframe\":{\"type\":\"timeframe\"},\"Container\":{\"type\":\"string\"},\"host.name\":{\"type\":\"string\"},\"k8s.namespace.name\":{\"type\":\"string\"},\"k8s.pod.uid\":{\"type\":\"string\"}},\"indexRange\":[0,3]}]}}" +var ErrUnexpected = errors.New("unexpected path") + //nolint:dupl func TestGetDQL(t *testing.T) { @@ -40,8 +43,7 @@ func TestGetDQL(t *testing.T) { if strings.Contains(path, "query:poll") { return []byte(dqlPayload), nil } - - return nil, errors.New("unexpected path") + return nil, ErrUnexpected } dqlProvider := NewKeptnDynatraceDQLProvider( @@ -82,7 +84,7 @@ func TestGetDQLMultipleRecords(t *testing.T) { return []byte(dqlPayloadTooManyItems), nil } - return nil, errors.New("unexpected path") + return nil, ErrUnexpected } dqlProvider := NewKeptnDynatraceDQLProvider( @@ -108,6 +110,46 @@ func TestGetDQLMultipleRecords(t *testing.T) { require.Contains(t, mockClient.DoCalls()[1].Path, "query:poll") } +func TestGetDQLAPIError(t *testing.T) { + + mockClient := &fake.DTAPIClientMock{} + + mockClient.DoFunc = func(ctx context.Context, path string, method string, payload []byte) ([]byte, error) { + if strings.Contains(path, "query:execute") { + return []byte(dqlRequestHandler), nil + } + + if strings.Contains(path, "query:poll") { + return []byte(dqlPayloadError), nil + } + + return nil, ErrUnexpected + } + + dqlProvider := NewKeptnDynatraceDQLProvider( + nil, + WithDTAPIClient(mockClient), + WithLogger(logr.New(klog.NewKlogr().GetSink())), + ) + + result, raw, err := dqlProvider.EvaluateQuery(context.TODO(), + metricsapi.KeptnMetric{ + Spec: metricsapi.KeptnMetricSpec{Query: ""}, + }, metricsapi.KeptnMetricsProvider{ + Spec: metricsapi.KeptnMetricsProviderSpec{}, + }, + ) + + require.NotNil(t, err) + require.Contains(t, err.Error(), "Token is missing required scope") + require.Empty(t, raw) + require.Empty(t, result) + + require.Len(t, mockClient.DoCalls(), 2) + require.Contains(t, mockClient.DoCalls()[0].Path, "query:execute") + require.Contains(t, mockClient.DoCalls()[1].Path, "query:poll") +} + func TestGetDQLTimeout(t *testing.T) { mockClient := &fake.DTAPIClientMock{} @@ -121,7 +163,7 @@ func TestGetDQLTimeout(t *testing.T) { return []byte(dqlPayloadNotFinished), nil } - return nil, errors.New("unexpected path") + return nil, ErrUnexpected } dqlProvider := NewKeptnDynatraceDQLProvider( @@ -172,7 +214,7 @@ func TestGetDQLCannotPostQuery(t *testing.T) { return nil, errors.New("oops") } - return nil, errors.New("unexpected path") + return nil, ErrUnexpected } dqlProvider := NewKeptnDynatraceDQLProvider( diff --git a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_test.go b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_test.go index e238498a3c..e32bc875de 100644 --- a/metrics-operator/controllers/common/providers/dynatrace/dynatrace_test.go +++ b/metrics-operator/controllers/common/providers/dynatrace/dynatrace_test.go @@ -16,6 +16,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" ) const dtpayload = "{\"totalCount\":1,\"nextPageKey\":null,\"resolution\":\"1m\",\"result\":[{\"metricId\":\"dsfm:billing.hostunit.assigned:splitBy():sort(value(auto,descending)):avg\",\"dataPointCountRatio\":6.0E-6,\"dimensionCountRatio\":1.0E-5,\"data\":[{\"dimensions\":[],\"dimensionMap\":{},\"timestamps\":[1666090140000,1666090200000,1666090260000,1666090320000,1666090380000,1666090440000,1666090500000,1666090560000,1666090620000,1666090680000,1666090740000,1666090800000,1666090860000,1666090920000,1666090980000,1666091040000,1666091100000,1666091160000,1666091220000,1666091280000,1666091340000,1666091400000,1666091460000,1666091520000,1666091580000,1666091640000,1666091700000,1666091760000,1666091820000,1666091880000,1666091940000,1666092000000,1666092060000,1666092120000,1666092180000,1666092240000,1666092300000,1666092360000,1666092420000,1666092480000,1666092540000,1666092600000,1666092660000,1666092720000,1666092780000,1666092840000,1666092900000,1666092960000,1666093020000,1666093080000,1666093140000,1666093200000,1666093260000,1666093320000,1666093380000,1666093440000,1666093500000,1666093560000,1666093620000,1666093680000,1666093740000,1666093800000,1666093860000,1666093920000,1666093980000,1666094040000,1666094100000,1666094160000,1666094220000,1666094280000,1666094340000,1666094400000,1666094460000,1666094520000,1666094580000,1666094640000,1666094700000,1666094760000,1666094820000,1666094880000,1666094940000,1666095000000,1666095060000,1666095120000,1666095180000,1666095240000,1666095300000,1666095360000,1666095420000,1666095480000,1666095540000,1666095600000,1666095660000,1666095720000,1666095780000,1666095840000,1666095900000,1666095960000,1666096020000,1666096080000,1666096140000,1666096200000,1666096260000,1666096320000,1666096380000,1666096440000,1666096500000,1666096560000,1666096620000,1666096680000,1666096740000,1666096800000,1666096860000,1666096920000,1666096980000,1666097040000,1666097100000,1666097160000,1666097220000,1666097280000,1666097340000],\"values\":[null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null,null,null,null,null,null,50,null,null,null,null,null,null,null,null,null]}]}]}" @@ -151,18 +152,7 @@ func TestEvaluateQuery_CorrectHTTP(t *testing.T) { require.Equal(t, 1, len(r.Header["Authorization"])) })) defer svr.Close() - fakeClient := fake.NewClient() - - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: query, - }, - } + kdp, obj := setupTest() p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ SecretKeyRef: v1.SecretKeySelector{ @@ -181,9 +171,10 @@ func TestEvaluateQuery_CorrectHTTP(t *testing.T) { } -func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { +func TestEvaluateQuery_APIError(t *testing.T) { + errorResponse := []byte("{\"error\":{\"code\":403,\"message\":\"Token is missing required scope. Use one of: metrics.read (Read metrics)\"}}") svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte("garbage")) + _, err := w.Write(errorResponse) require.Nil(t, err) })) defer svr.Close() @@ -197,18 +188,44 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { secretKey: []byte(secretValue), }, } - fakeClient := fake.NewClient(apiToken) - - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, + kdp, obj := setupTest(apiToken) + p := metricsapi.KeptnMetricsProvider{ + Spec: metricsapi.KeptnMetricsProviderSpec{ + SecretKeyRef: v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: secretName, + }, + Key: secretKey, + }, + TargetServer: svr.URL, + }, } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: "my-query", + r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p) + require.Equal(t, "", r) + t.Log(string(raw)) + require.Equal(t, errorResponse, raw) //we still return the raw answer to help user debug + require.NotNil(t, e) + require.Contains(t, e.Error(), "Token is missing required scope.") +} + +func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte("garbage")) + require.Nil(t, err) + })) + defer svr.Close() + secretName, secretKey, secretValue := "secretName", "secretKey", "secretValue" + apiToken := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: "", + }, + Data: map[string][]byte{ + secretKey: []byte(secretValue), }, } + + kdp, obj := setupTest(apiToken) p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ SecretKeyRef: v1.SecretKeySelector{ @@ -222,7 +239,8 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) { } r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p) require.Equal(t, "", r) - require.Equal(t, []byte(nil), raw) + t.Log(string(raw), e) + require.Equal(t, []byte("garbage"), raw) //we still return the raw answer to help user debug require.NotNil(t, e) } @@ -232,18 +250,8 @@ func TestEvaluateQuery_MissingSecret(t *testing.T) { require.Nil(t, err) })) defer svr.Close() - fakeClient := fake.NewClient() + kdp, obj := setupTest() - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: "my-query", - }, - } p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ TargetServer: svr.URL, @@ -260,18 +268,8 @@ func TestEvaluateQuery_SecretNotFound(t *testing.T) { require.Nil(t, err) })) defer svr.Close() - fakeClient := fake.NewClient() + kdp, obj := setupTest() - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: "my-query", - }, - } p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ SecretKeyRef: v1.SecretKeySelector{ @@ -304,18 +302,8 @@ func TestEvaluateQuery_RefNotExistingKey(t *testing.T) { secretKey: []byte(secretValue), }, } - fakeClient := fake.NewClient(apiToken) + kdp, obj := setupTest(apiToken) - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: "my-query", - }, - } missingKey := "key_not_found" p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ @@ -350,18 +338,8 @@ func TestEvaluateQuery_HappyPath(t *testing.T) { secretKey: []byte(secretValue), }, } - fakeClient := fake.NewClient(apiToken) + kdp, obj := setupTest(apiToken) - kdp := KeptnDynatraceProvider{ - HttpClient: http.Client{}, - Log: ctrl.Log.WithName("testytest"), - K8sClient: fakeClient, - } - obj := metricsapi.KeptnMetric{ - Spec: metricsapi.KeptnMetricSpec{ - Query: "my-query", - }, - } p := metricsapi.KeptnMetricsProvider{ Spec: metricsapi.KeptnMetricsProviderSpec{ SecretKeyRef: v1.SecretKeySelector{ @@ -378,3 +356,20 @@ func TestEvaluateQuery_HappyPath(t *testing.T) { require.Equal(t, []byte(dtpayload), raw) require.Equal(t, fmt.Sprintf("%f", 50.0), r) } + +func setupTest(objs ...client.Object) (KeptnDynatraceProvider, metricsapi.KeptnMetric) { + + fakeClient := fake.NewClient(objs...) + + kdp := KeptnDynatraceProvider{ + HttpClient: http.Client{}, + Log: ctrl.Log.WithName("testytest"), + K8sClient: fakeClient, + } + obj := metricsapi.KeptnMetric{ + Spec: metricsapi.KeptnMetricSpec{ + Query: "my-query", + }, + } + return kdp, obj +} diff --git a/metrics-operator/controllers/common/providers/provider_test.go b/metrics-operator/controllers/common/providers/provider_test.go index ad150fa88a..f3869d424a 100644 --- a/metrics-operator/controllers/common/providers/provider_test.go +++ b/metrics-operator/controllers/common/providers/provider_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/go-logr/logr" + "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/fake" "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/providers/datadog" "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/providers/dynatrace" "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/providers/prometheus" @@ -26,6 +27,11 @@ func TestFactory(t *testing.T) { provider: &dynatrace.KeptnDynatraceProvider{}, err: false, }, + { + providerType: DynatraceDQLProviderType, + provider: dynatrace.NewKeptnDynatraceDQLProvider(fake.NewClient()), + err: false, + }, { providerType: DataDogProviderType, provider: &datadog.KeptnDataDogProvider{}, diff --git a/metrics-operator/controllers/metrics/controller.go b/metrics-operator/controllers/metrics/controller.go index 83982d1662..2d38a4c580 100644 --- a/metrics-operator/controllers/metrics/controller.go +++ b/metrics-operator/controllers/metrics/controller.go @@ -55,10 +55,6 @@ type KeptnMetricReconciler struct { // 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 KeptnMetric 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 @@ -102,10 +98,10 @@ func (r *KeptnMetricReconciler) Reconcile(ctx context.Context, req ctrl.Request) reconcile := ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second} value, rawValue, err := provider.EvaluateQuery(ctx, *metric, *metricProvider) if err != nil { - r.Log.Error(err, "Failed to evaluate the query") + r.Log.Error(err, "Failed to evaluate the query", "Response from provider was:", (string)(rawValue)) metric.Status.ErrMsg = err.Error() metric.Status.Value = "" - metric.Status.RawValue = []byte{} + metric.Status.RawValue = cupSize(rawValue) metric.Status.LastUpdated = metav1.Time{Time: time.Now()} reconcile = ctrl.Result{Requeue: false} } else { @@ -123,6 +119,9 @@ func (r *KeptnMetricReconciler) Reconcile(ctx context.Context, req ctrl.Request) } func cupSize(value []byte) []byte { + if len(value) == 0 { + return []byte{} + } if len(value) > MB { return value[:MB] } diff --git a/metrics-operator/go.mod b/metrics-operator/go.mod index a73ccd7c7b..9744935c4c 100644 --- a/metrics-operator/go.mod +++ b/metrics-operator/go.mod @@ -4,24 +4,24 @@ go 1.20 require ( github.com/DataDog/datadog-api-client-go/v2 v2.12.0 - github.com/benbjohnson/clock v1.3.3 + github.com/benbjohnson/clock v1.3.5 github.com/go-logr/logr v1.2.4 github.com/gorilla/mux v1.8.0 github.com/kelseyhightower/envconfig v1.4.0 + github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0 github.com/open-feature/go-sdk v1.3.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.15.0 - github.com/prometheus/common v0.42.0 - github.com/spf13/afero v1.9.5 - github.com/stretchr/testify v1.8.2 - k8s.io/api v0.26.4 - k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery v0.26.4 - k8s.io/apiserver v0.26.4 - k8s.io/client-go v0.26.4 - k8s.io/component-base v0.26.4 - k8s.io/klog/v2 v2.90.1 - k8s.io/metrics v0.26.4 + github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/common v0.44.0 + github.com/stretchr/testify v1.8.3 + k8s.io/api v0.26.5 + k8s.io/apiextensions-apiserver v0.26.5 + k8s.io/apimachinery v0.26.5 + k8s.io/apiserver v0.26.5 + k8s.io/client-go v0.26.5 + k8s.io/component-base v0.26.5 + k8s.io/klog/v2 v2.100.1 + k8s.io/metrics v0.26.5 sigs.k8s.io/controller-runtime v0.14.6 sigs.k8s.io/custom-metrics-apiserver v1.25.1-0.20230116101851-63817c8ac8f2 ) @@ -70,9 +70,9 @@ require ( github.com/onsi/ginkgo/v2 v2.8.1 // indirect github.com/onsi/gomega v1.27.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cobra v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect @@ -93,10 +93,10 @@ require ( go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect @@ -109,10 +109,10 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/kms v0.26.4 // indirect + k8s.io/kms v0.26.5 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/metrics-operator/go.sum b/metrics-operator/go.sum index 4040d76223..0be7552008 100644 --- a/metrics-operator/go.sum +++ b/metrics-operator/go.sum @@ -59,8 +59,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= -github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -263,6 +263,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0 h1:utqqOMPptCXMBUaU9oRBsXAQE1US5Bz9fbXxzOAk1YI= +github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0/go.mod h1:sZUFv7ZVPq4cMxcMQc77vjhEtem48BzV6fHbmXdVjQg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -311,19 +313,19 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -333,7 +335,6 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -351,17 +352,13 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= @@ -481,8 +478,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -543,8 +540,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= @@ -763,33 +761,33 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= -k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= -k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= -k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= -k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= -k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/apiserver v0.26.4 h1:3Oq4mnJv0mzVX7BR/Nod+8KjlELf/3Ljvu9ZWDyLUoA= -k8s.io/apiserver v0.26.4/go.mod h1:yAY3O1vBM4/0OIGAGeWcdfzQvgdwJ188VirLcuSAVnw= -k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= -k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= -k8s.io/component-base v0.26.4 h1:Bg2xzyXNKL3eAuiTEu3XE198d6z22ENgFgGQv2GGOUk= -k8s.io/component-base v0.26.4/go.mod h1:lTuWL1Xz/a4e80gmIC3YZG2JCO4xNwtKWHJWeJmsq20= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kms v0.26.4 h1:mQ+DeOvgAHC6+heZcozPkEd3rWtP4DVVjo1hLSih9w4= -k8s.io/kms v0.26.4/go.mod h1:69qGnf1NsFOQP07fBYqNLZklqEHSJF024JqYCaeVxHg= +k8s.io/api v0.26.5 h1:Npao/+sMSng6nkEcNydgH3BNo4s5YoBg7iw35HM7Hcw= +k8s.io/api v0.26.5/go.mod h1:O7ICW7lj6+ZQQQ3cxekgCoW+fnGo5kWT0nTHkLZ5grc= +k8s.io/apiextensions-apiserver v0.26.5 h1:VJ946z9RjyCPn3qiz4Kus/UYjCRrdn1xUvEsJFvN5Yo= +k8s.io/apiextensions-apiserver v0.26.5/go.mod h1:Olsde7ZNWnyz9rsL13iXYXmL1h7kWujtKeC3yWVCDPo= +k8s.io/apimachinery v0.26.5 h1:hTQVhJao2piX7vSgCn4Lwd6E0o/+TJIH4NqRf+q4EmE= +k8s.io/apimachinery v0.26.5/go.mod h1:HUvk6wrOP4v22AIYqeCGSQ6xWCHo41J9d6psb3temAg= +k8s.io/apiserver v0.26.5 h1:SBzyDpIXXPR4v+mpSU44p9fQerBMkpOH6lmSPCD1wmo= +k8s.io/apiserver v0.26.5/go.mod h1:OSbw98Y1bDSbA2izYIKqhi10vb4KWP9b4siiCRFkBVE= +k8s.io/client-go v0.26.5 h1:e8Z44pafL/c6ayF/6qYEypbJoDSakaFxhJ9lqULEJEo= +k8s.io/client-go v0.26.5/go.mod h1:/CYyNt+ZLMvWqMF8h1SvkUXz2ujFWQLwdDrdiQlZ5X0= +k8s.io/component-base v0.26.5 h1:nHAzDvXQ4whYpOqrQGWrDIYI/GIeXkuxzqC/iVICfZo= +k8s.io/component-base v0.26.5/go.mod h1:wvfNAS05EtKdPeUxFceo8WNh8bGPcFY8QfPhv5MYjA4= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kms v0.26.5 h1:Yjgvlxc3KBTAUWuxOIkMUB6YEmqR+rKBkRquBioP8YY= +k8s.io/kms v0.26.5/go.mod h1:AYuV9ZebRhr6cb1eT9L6kZVxvgIUxmE1Fe6kPhqYvuc= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/metrics v0.26.4 h1:ijyerycmjVp9EVPfDqha8eb+s9jw5c+A9MkTvuRBdms= -k8s.io/metrics v0.26.4/go.mod h1:0InNj7+/aS5POa0dDHuSleIDr5MHXaQQSpMc0mm17wE= +k8s.io/metrics v0.26.5 h1:J2vPw1u49iA1rAByeAObffn60WvcxZwTCmMTB3+LWAM= +k8s.io/metrics v0.26.5/go.mod h1:g3YZfYetr4JJ+uA2q2Vdkr/D9bswPgQDOvost7ZTLHQ= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36 h1:PUuX1qIFv309AT8hF/CdPKDmsG/hn/L8zRX7VvISM3A= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 h1:fAPTNEpzQMOLMGwOHNbUkR2xXTQwMJOZYNx+/mLlOh0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37/go.mod h1:vfnxT4FXNT8eGvO+xi/DsyC/qHmdujqwrUa1WSspCsk= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/custom-metrics-apiserver v1.25.1-0.20230116101851-63817c8ac8f2 h1:D49r2VoxIdm3s1yVoCMbHrLH0qxDj5TiaWB0XYdqgcI= diff --git a/metrics-operator/main.go b/metrics-operator/main.go index 930e47103f..bc99c602b7 100644 --- a/metrics-operator/main.go +++ b/metrics-operator/main.go @@ -26,12 +26,13 @@ import ( "time" "github.com/kelseyhightower/envconfig" + "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/certificates" + certCommon "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/common" + "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/webhook" metricsv1alpha1 "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha1" metricsv1alpha2 "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" metricsv1alpha3 "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" - cmdConfig "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/config" "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/metrics/adapter" - "github.com/keptn/lifecycle-toolkit/metrics-operator/cmd/webhook" metricscontroller "github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/metrics" keptnserver "github.com/keptn/lifecycle-toolkit/metrics-operator/pkg/metrics" "github.com/open-feature/go-sdk/pkg/openfeature" @@ -154,10 +155,22 @@ func main() { webhookBuilder := webhook.NewWebhookBuilder(). SetNamespace(env.PodNamespace). SetPodName(env.PodName). - SetConfigProvider(cmdConfig.NewKubeConfigProvider()) + SetManagerProvider( + webhook.NewWebhookManagerProvider( + mgr.GetWebhookServer().CertDir, "tls.key", "tls.crt"), + ). + SetCertificateWatcher( + certificates.NewCertificateWatcher( + mgr.GetAPIReader(), + mgr.GetWebhookServer().CertDir, + env.PodNamespace, + certCommon.SecretName, + setupLog, + ), + ) setupLog.Info("starting webhook and manager") - if err1 := webhookBuilder.Run(mgr); err1 != nil { + if err := webhookBuilder.Run(mgr, nil); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) } diff --git a/operator/Dockerfile b/operator/Dockerfile index ec0e3c4256..c597ac7c67 100644 --- a/operator/Dockerfile +++ b/operator/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.20.3-alpine3.16 AS builder +FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine3.16 AS builder ENV CGO_ENABLED=0 @@ -8,7 +8,7 @@ COPY go.mod go.sum ./ RUN go mod download # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -ARG CONTROLLER_TOOLS_VERSION=v0.11.4 +ARG CONTROLLER_TOOLS_VERSION=v0.12.0 RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@$CONTROLLER_TOOLS_VERSION # Copy the go source diff --git a/operator/Makefile b/operator/Makefile index a80f58ffde..19df8a9110 100644 --- a/operator/Makefile +++ b/operator/Makefile @@ -25,9 +25,9 @@ ENVTEST_K8S_VERSION=1.24.2 ## Tool Versions # renovate: datasource=github-tags depName=kubernetes-sigs/kustomize -KUSTOMIZE_VERSION?=v5.0.1 +KUSTOMIZE_VERSION?=v5.0.3 # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools -CONTROLLER_TOOLS_VERSION?=v0.11.4 +CONTROLLER_TOOLS_VERSION?=v0.12.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) diff --git a/operator/apis/lifecycle/v1alpha1/common/common.go b/operator/apis/lifecycle/v1alpha1/common/common.go index 73a93f495c..b714e7b71a 100644 --- a/operator/apis/lifecycle/v1alpha1/common/common.go +++ b/operator/apis/lifecycle/v1alpha1/common/common.go @@ -6,8 +6,7 @@ import ( "math/rand" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" + "go.opentelemetry.io/otel/metric" ) const WorkloadAnnotation = "keptn.sh/workload" @@ -132,14 +131,14 @@ const PreDeploymentEvaluationCheckType CheckType = "pre-eval" const PostDeploymentEvaluationCheckType CheckType = "post-eval" type KeptnMeters struct { - TaskCount syncint64.Counter - TaskDuration syncfloat64.Histogram - DeploymentCount syncint64.Counter - DeploymentDuration syncfloat64.Histogram - AppCount syncint64.Counter - AppDuration syncfloat64.Histogram - EvaluationCount syncint64.Counter - EvaluationDuration syncfloat64.Histogram + TaskCount metric.Int64Counter + TaskDuration metric.Float64Histogram + DeploymentCount metric.Int64Counter + DeploymentDuration metric.Float64Histogram + AppCount metric.Int64Counter + AppDuration metric.Float64Histogram + EvaluationCount metric.Int64Counter + EvaluationDuration metric.Float64Histogram } const ( diff --git a/operator/apis/lifecycle/v1alpha2/common/common.go b/operator/apis/lifecycle/v1alpha2/common/common.go index 92a8605524..4f758bbb6a 100644 --- a/operator/apis/lifecycle/v1alpha2/common/common.go +++ b/operator/apis/lifecycle/v1alpha2/common/common.go @@ -6,8 +6,7 @@ import ( "math/rand" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" + "go.opentelemetry.io/otel/metric" ) const WorkloadAnnotation = "keptn.sh/workload" @@ -134,14 +133,14 @@ const PreDeploymentEvaluationCheckType CheckType = "pre-eval" const PostDeploymentEvaluationCheckType CheckType = "post-eval" type KeptnMeters struct { - TaskCount syncint64.Counter - TaskDuration syncfloat64.Histogram - DeploymentCount syncint64.Counter - DeploymentDuration syncfloat64.Histogram - AppCount syncint64.Counter - AppDuration syncfloat64.Histogram - EvaluationCount syncint64.Counter - EvaluationDuration syncfloat64.Histogram + TaskCount metric.Int64Counter + TaskDuration metric.Float64Histogram + DeploymentCount metric.Int64Counter + DeploymentDuration metric.Float64Histogram + AppCount metric.Int64Counter + AppDuration metric.Float64Histogram + EvaluationCount metric.Int64Counter + EvaluationDuration metric.Float64Histogram } const ( diff --git a/operator/apis/lifecycle/v1alpha3/common/common.go b/operator/apis/lifecycle/v1alpha3/common/common.go index fe40028763..1fee458789 100644 --- a/operator/apis/lifecycle/v1alpha3/common/common.go +++ b/operator/apis/lifecycle/v1alpha3/common/common.go @@ -3,13 +3,12 @@ package common import ( "crypto/sha256" "encoding/hex" - "fmt" "math/rand" "strconv" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" + "go.opentelemetry.io/otel/metric" ) const WorkloadAnnotation = "keptn.sh/workload" @@ -31,10 +30,8 @@ const CreateAppEvalSpanName = "create_%s_app_evaluation" const CreateWorkloadEvalSpanName = "create_%s_deployment_evaluation" const AppTypeAnnotation = "keptn.sh/app-type" -const MaxAppNameLength = 25 -const MaxWorkloadNameLength = 25 -const MaxTaskNameLength = 25 -const MaxVersionLength = 12 +const MinKLTNameLen = 80 +const MaxK8sObjectLength = 253 type AppType string @@ -147,14 +144,14 @@ const PreDeploymentEvaluationCheckType CheckType = "pre-eval" const PostDeploymentEvaluationCheckType CheckType = "post-eval" type KeptnMeters struct { - TaskCount syncint64.Counter - TaskDuration syncfloat64.Histogram - DeploymentCount syncint64.Counter - DeploymentDuration syncfloat64.Histogram - AppCount syncint64.Counter - AppDuration syncfloat64.Histogram - EvaluationCount syncint64.Counter - EvaluationDuration syncfloat64.Histogram + TaskCount metric.Int64Counter + TaskDuration metric.Float64Histogram + DeploymentCount metric.Int64Counter + DeploymentDuration metric.Float64Histogram + AppCount metric.Int64Counter + AppDuration metric.Float64Histogram + EvaluationCount metric.Int64Counter + EvaluationDuration metric.Float64Histogram } const ( @@ -178,12 +175,17 @@ const ( func GenerateTaskName(checkType CheckType, taskName string) string { randomId := rand.Intn(99_999-10_000) + 10000 - return fmt.Sprintf("%s-%s-%d", checkType, TruncateString(taskName, 32), randomId) + return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, string(checkType), taskName, strconv.Itoa(randomId)) +} + +func GenerateJobName(taskName string) string { + randomId := rand.Intn(99_999-10_000) + 10000 + return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, taskName, strconv.Itoa(randomId)) } func GenerateEvaluationName(checkType CheckType, evalName string) string { randomId := rand.Intn(99_999-10_000) + 10000 - return fmt.Sprintf("%s-%s-%d", checkType, TruncateString(evalName, 27), randomId) + return operatorcommon.CreateResourceName(MaxK8sObjectLength, MinKLTNameLen, string(checkType), evalName, strconv.Itoa(randomId)) } // MergeMaps merges two maps into a new map. If a key exists in both maps, the diff --git a/operator/apis/lifecycle/v1alpha3/common/common_test.go b/operator/apis/lifecycle/v1alpha3/common/common_test.go index 9f40c417e4..293f3f9251 100644 --- a/operator/apis/lifecycle/v1alpha3/common/common_test.go +++ b/operator/apis/lifecycle/v1alpha3/common/common_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/require" ) +const ExtraLongName = "loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891234567890123456789" + func TestKeptnState_IsCompleted(t *testing.T) { tests := []struct { State KeptnState @@ -292,8 +294,8 @@ func Test_GenerateTaskName(t *testing.T) { }, { Check: PreDeploymentCheckType, - Name: "loooooooooooooooooooooooooooooooooooooong_name", - Want: "pre-looooooooooooooooooooooooooooooo-", + Name: ExtraLongName, + Want: "pre-loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo0123456789123456-", }, } for _, tt := range tests { @@ -321,8 +323,8 @@ func Test_GenerateEvaluationName(t *testing.T) { }, { Check: PreDeploymentEvaluationCheckType, - Name: "loooooooooooooooooooooooooooooooooooooong_name", - Want: "pre-eval-loooooooooooooooooooooooooo-", + Name: ExtraLongName, + Want: "pre-eval-loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891-", }, } for _, tt := range tests { @@ -332,6 +334,31 @@ func Test_GenerateEvaluationName(t *testing.T) { } } +func Test_GenerateJobName(t *testing.T) { + tests := []struct { + Name string + Want string + }{ + { + Name: "short-name", + Want: "short-name-", + }, + { + Name: "", + Want: "-", + }, + { + Name: ExtraLongName, + Want: "loooooooooooooooooooooo00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ooooooo01234567891234567890-", + }, + } + for _, tt := range tests { + t.Run("", func(t *testing.T) { + require.True(t, strings.HasPrefix(GenerateJobName(tt.Name), tt.Want)) + }) + } +} + func Test_MergeMaps(t *testing.T) { tests := []struct { In1 map[string]string diff --git a/operator/apis/lifecycle/v1alpha3/keptnapp_types.go b/operator/apis/lifecycle/v1alpha3/keptnapp_types.go index a5ec6b80b3..ede59936bc 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnapp_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnapp_types.go @@ -17,10 +17,8 @@ limitations under the License. package v1alpha3 import ( - "fmt" - "strings" - "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,23 +29,47 @@ import ( // KeptnAppSpec defines the desired state of KeptnApp type KeptnAppSpec struct { + // Version defines the version of the application. For automatically created KeptnApps, + // the version is a function of all KeptnWorkloads that are part of the KeptnApp. Version string `json:"version"` + // Revision can be modified to trigger another deployment of a KeptnApp of the same version. + // This can be used for restarting a KeptnApp which failed to deploy, + // e.g. due to a failed preDeploymentEvaluation/preDeploymentTask. // +kubebuilder:default:=1 - Revision uint `json:"revision,omitempty"` - Workloads []KeptnWorkloadRef `json:"workloads,omitempty"` - PreDeploymentTasks []string `json:"preDeploymentTasks,omitempty"` - PostDeploymentTasks []string `json:"postDeploymentTasks,omitempty"` - PreDeploymentEvaluations []string `json:"preDeploymentEvaluations,omitempty"` - PostDeploymentEvaluations []string `json:"postDeploymentEvaluations,omitempty"` + Revision uint `json:"revision,omitempty"` + // Workloads is a list of all KeptnWorkloads that are part of the KeptnApp. + Workloads []KeptnWorkloadRef `json:"workloads,omitempty"` + // PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnApp. + // The items of this list refer to the names of KeptnTaskDefinitions + // located in the same namespace as the KeptnApp, or in the KLT namespace. + PreDeploymentTasks []string `json:"preDeploymentTasks,omitempty"` + // PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnApp. + // The items of this list refer to the names of KeptnTaskDefinitions + // located in the same namespace as the KeptnApp, or in the KLT namespace. + PostDeploymentTasks []string `json:"postDeploymentTasks,omitempty"` + // PreDeploymentEvaluations is a list of all evaluations to be performed + // during the pre-deployment phase of the KeptnApp. + // The items of this list refer to the names of KeptnEvaluationDefinitions + // located in the same namespace as the KeptnApp, or in the KLT namespace. + PreDeploymentEvaluations []string `json:"preDeploymentEvaluations,omitempty"` + // PostDeploymentEvaluations is a list of all evaluations to be performed + // during the post-deployment phase of the KeptnApp. + // The items of this list refer to the names of KeptnEvaluationDefinitions + // located in the same namespace as the KeptnApp, or in the KLT namespace. + PostDeploymentEvaluations []string `json:"postDeploymentEvaluations,omitempty"` } // KeptnAppStatus defines the observed state of KeptnApp type KeptnAppStatus struct { + // CurrentVersion indicates the version that is currently deployed or being reconciled. CurrentVersion string `json:"currentVersion,omitempty"` } +// KeptnWorkloadRef refers to a KeptnWorkload that is part of a KeptnApp type KeptnWorkloadRef struct { - Name string `json:"name"` + // Name is the name of the KeptnWorkload. + Name string `json:"name"` + // Version is the version of the KeptnWorkload. Version string `json:"version"` } @@ -60,7 +82,9 @@ type KeptnApp struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnAppSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnApp. + Spec KeptnAppSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnApp. Status KeptnAppStatus `json:"status,omitempty"` } @@ -78,7 +102,7 @@ func init() { } func (a KeptnApp) GetAppVersionName() string { - return strings.ToLower(fmt.Sprintf("%s-%s-%s", a.Name, a.Spec.Version, common.Hash(a.Generation))) + return operatorcommon.CreateResourceName(common.MaxK8sObjectLength, common.MinKLTNameLen, a.Name, a.Spec.Version, common.Hash(a.Generation)) } func (a KeptnApp) SetSpanAttributes(span trace.Span) { diff --git a/operator/apis/lifecycle/v1alpha3/keptnappcreationrequest_types.go b/operator/apis/lifecycle/v1alpha3/keptnappcreationrequest_types.go index 2795ede381..8258aafc33 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnappcreationrequest_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnappcreationrequest_types.go @@ -46,7 +46,9 @@ type KeptnAppCreationRequest struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnAppCreationRequestSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnAppCreationRequest. + Spec KeptnAppCreationRequestSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnAppCreationRequest. Status KeptnAppCreationRequestStatus `json:"status,omitempty"` } diff --git a/operator/apis/lifecycle/v1alpha3/keptnappversion_types.go b/operator/apis/lifecycle/v1alpha3/keptnappversion_types.go index bdbfeec829..0d2f353689 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnappversion_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnappversion_types.go @@ -33,41 +33,60 @@ import ( // KeptnAppVersionSpec defines the desired state of KeptnAppVersion type KeptnAppVersionSpec struct { - KeptnAppSpec `json:",inline"` - AppName string `json:"appName"` + KeptnAppSpec `json:",inline"` + // AppName is the name of the KeptnApp. + AppName string `json:"appName"` + // PreviousVersion is the version of the KeptnApp that has been deployed prior to this version. PreviousVersion string `json:"previousVersion,omitempty"` - + // TraceId contains the OpenTelemetry trace ID. TraceId map[string]string `json:"traceId,omitempty"` } // KeptnAppVersionStatus defines the observed state of KeptnAppVersion type KeptnAppVersionStatus struct { + // PreDeploymentStatus indicates the current status of the KeptnAppVersion's PreDeployment phase. // +kubebuilder:default:=Pending PreDeploymentStatus common.KeptnState `json:"preDeploymentStatus,omitempty"` + // PostDeploymentStatus indicates the current status of the KeptnAppVersion's PostDeployment phase. // +kubebuilder:default:=Pending PostDeploymentStatus common.KeptnState `json:"postDeploymentStatus,omitempty"` + // PreDeploymentEvaluationStatus indicates the current status of the KeptnAppVersion's PreDeploymentEvaluation phase. // +kubebuilder:default:=Pending PreDeploymentEvaluationStatus common.KeptnState `json:"preDeploymentEvaluationStatus,omitempty"` + // PostDeploymentEvaluationStatus indicates the current status of the KeptnAppVersion's PostDeploymentEvaluation phase. // +kubebuilder:default:=Pending PostDeploymentEvaluationStatus common.KeptnState `json:"postDeploymentEvaluationStatus,omitempty"` + // WorkloadOverallStatus indicates the current status of the KeptnAppVersion's Workload deployment phase. // +kubebuilder:default:=Pending - WorkloadOverallStatus common.KeptnState `json:"workloadOverallStatus,omitempty"` - WorkloadStatus []WorkloadStatus `json:"workloadStatus,omitempty"` - CurrentPhase string `json:"currentPhase,omitempty"` - PreDeploymentTaskStatus []ItemStatus `json:"preDeploymentTaskStatus,omitempty"` - PostDeploymentTaskStatus []ItemStatus `json:"postDeploymentTaskStatus,omitempty"` - PreDeploymentEvaluationTaskStatus []ItemStatus `json:"preDeploymentEvaluationTaskStatus,omitempty"` - PostDeploymentEvaluationTaskStatus []ItemStatus `json:"postDeploymentEvaluationTaskStatus,omitempty"` - PhaseTraceIDs common.PhaseTraceID `json:"phaseTraceIDs,omitempty"` + WorkloadOverallStatus common.KeptnState `json:"workloadOverallStatus,omitempty"` + // WorkloadStatus contains the current status of each KeptnWorkload that is part of the KeptnAppVersion. + WorkloadStatus []WorkloadStatus `json:"workloadStatus,omitempty"` + // CurrentPhase indicates the current phase of the KeptnAppVersion. + CurrentPhase string `json:"currentPhase,omitempty"` + // PreDeploymentTaskStatus indicates the current state of each preDeploymentTask of the KeptnAppVersion. + PreDeploymentTaskStatus []ItemStatus `json:"preDeploymentTaskStatus,omitempty"` + // PostDeploymentTaskStatus indicates the current state of each postDeploymentTask of the KeptnAppVersion. + PostDeploymentTaskStatus []ItemStatus `json:"postDeploymentTaskStatus,omitempty"` + // PreDeploymentEvaluationTaskStatus indicates the current state of each preDeploymentEvaluation of the KeptnAppVersion. + PreDeploymentEvaluationTaskStatus []ItemStatus `json:"preDeploymentEvaluationTaskStatus,omitempty"` + // PostDeploymentEvaluationTaskStatus indicates the current state of each postDeploymentEvaluation of the KeptnAppVersion. + PostDeploymentEvaluationTaskStatus []ItemStatus `json:"postDeploymentEvaluationTaskStatus,omitempty"` + // PhaseTraceIDs contains the trace IDs of the OpenTelemetry spans of each phase of the KeptnAppVersion. + PhaseTraceIDs common.PhaseTraceID `json:"phaseTraceIDs,omitempty"` + // Status represents the overall status of the KeptnAppVersion. // +kubebuilder:default:=Pending Status common.KeptnState `json:"status,omitempty"` + // StartTime represents the time at which the deployment of the KeptnAppVersion started. StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` + // EndTime represents the time at which the deployment of the KeptnAppVersion finished. + EndTime metav1.Time `json:"endTime,omitempty"` } type WorkloadStatus struct { + // Workload refers to a KeptnWorkload that is part of the KeptnAppVersion. Workload KeptnWorkloadRef `json:"workload,omitempty"` + // Status indicates the current status of the KeptnWorkload. // +kubebuilder:default:=Pending Status common.KeptnState `json:"status,omitempty"` } @@ -90,7 +109,9 @@ type KeptnAppVersion struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnAppVersionSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnAppVersion. + Spec KeptnAppVersionSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnAppVersion. Status KeptnAppVersionStatus `json:"status,omitempty"` } diff --git a/operator/apis/lifecycle/v1alpha3/keptnevaluation_types.go b/operator/apis/lifecycle/v1alpha3/keptnevaluation_types.go index bbf814505c..4f3c55bd62 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnevaluation_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnevaluation_types.go @@ -32,38 +32,64 @@ import ( // KeptnEvaluationSpec defines the desired state of KeptnEvaluation type KeptnEvaluationSpec struct { - Workload string `json:"workload,omitempty"` - WorkloadVersion string `json:"workloadVersion"` - AppName string `json:"appName,omitempty"` - AppVersion string `json:"appVersion,omitempty"` + // Workload defines the KeptnWorkload for which the KeptnEvaluation is done. + Workload string `json:"workload,omitempty"` + // WorkloadVersion defines the version of the KeptnWorkload for which the KeptnEvaluation is done. + WorkloadVersion string `json:"workloadVersion"` + // AppName defines the KeptnApp for which the KeptnEvaluation is done. + AppName string `json:"appName,omitempty"` + // AppVersion defines the version of the KeptnApp for which the KeptnEvaluation is done. + AppVersion string `json:"appVersion,omitempty"` + // EvaluationDefinition refers to the name of the KeptnEvaluationDefinition + // which includes the objectives for the KeptnEvaluation. + // The KeptnEvaluationDefinition can be + // located in the same namespace as the KeptnEvaluation, or in the KLT namespace. EvaluationDefinition string `json:"evaluationDefinition"` + // Retries indicates how many times the KeptnEvaluation can be attempted in the case of an error or + // missed evaluation objective, before considering the KeptnEvaluation to be failed. // +kubebuilder:default:=10 Retries int `json:"retries,omitempty"` + // RetryInterval specifies the interval at which the KeptnEvaluation is retried in the case of an error + // or a missed objective. // +optional // +kubebuilder:default:="5s" // +kubebuilder:validation:Pattern="^0|([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$" // +kubebuilder:validation:Type:=string // +optional - RetryInterval metav1.Duration `json:"retryInterval,omitempty"` - FailAction string `json:"failAction,omitempty"` - Type common.CheckType `json:"checkType,omitempty"` + RetryInterval metav1.Duration `json:"retryInterval,omitempty"` + FailAction string `json:"failAction,omitempty"` + // Type indicates whether the KeptnEvaluation is part of the pre- or postDeployment phase. + Type common.CheckType `json:"checkType,omitempty"` } // KeptnEvaluationStatus defines the observed state of KeptnEvaluation type KeptnEvaluationStatus struct { + // RetryCount indicates how many times the KeptnEvaluation has been attempted already. // +kubebuilder:default:=0 - RetryCount int `json:"retryCount"` + RetryCount int `json:"retryCount"` + // EvaluationStatus describes the status of each objective of the KeptnEvaluationDefinition + // referenced by the KeptnEvaluation. EvaluationStatus map[string]EvaluationStatusItem `json:"evaluationStatus"` + // OverallStatus describes the overall status of the KeptnEvaluation. The Overall status is derived + // from the status of the individual objectives of the KeptnEvaluationDefinition + // referenced by the KeptnEvaluation. // +kubebuilder:default:=Pending OverallStatus common.KeptnState `json:"overallStatus"` - StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` + // StartTime represents the time at which the KeptnEvaluation started. + StartTime metav1.Time `json:"startTime,omitempty"` + // EndTime represents the time at which the KeptnEvaluation finished. + EndTime metav1.Time `json:"endTime,omitempty"` } type EvaluationStatusItem struct { - Value string `json:"value"` - Status common.KeptnState `json:"status"` - Message string `json:"message,omitempty"` + // Value represents the value of the KeptnMetric being evaluated. + Value string `json:"value"` + // Status indicates the status of the objective being evaluated. + Status common.KeptnState `json:"status"` + // Message contains additional information about the evaluation of an objective. + // This can include explanations about why an evaluation has failed (e.g. due to a missed objective), + // or if there was any error during the evaluation of the objective. + Message string `json:"message,omitempty"` } //+kubebuilder:object:root=true @@ -83,7 +109,9 @@ type KeptnEvaluation struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnEvaluationSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnEvaluation. + Spec KeptnEvaluationSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnEvaluation. Status KeptnEvaluationStatus `json:"status,omitempty"` } diff --git a/operator/apis/lifecycle/v1alpha3/keptnevaluationdefinition_types.go b/operator/apis/lifecycle/v1alpha3/keptnevaluationdefinition_types.go index 1fe70566ef..9d3930f07d 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnevaluationdefinition_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnevaluationdefinition_types.go @@ -25,20 +25,27 @@ import ( // KeptnEvaluationDefinitionSpec defines the desired state of KeptnEvaluationDefinition type KeptnEvaluationDefinitionSpec struct { + // Objectives is a list of objectives that have to be met for a KeptnEvaluation referencing this + // KeptnEvaluationDefinition to be successful. Objectives []Objective `json:"objectives"` } type Objective struct { - KeptnMetricRef KeptnMetricReference `json:"keptnMetricRef"` - EvaluationTarget string `json:"evaluationTarget"` + // KeptnMetricRef references the KeptnMetric that should be evaluated. + KeptnMetricRef KeptnMetricReference `json:"keptnMetricRef"` + // EvaluationTarget specifies the target value for the references KeptnMetric. + // Needs to start with either '<' or '>', followed by the target value (e.g. '<10'). + EvaluationTarget string `json:"evaluationTarget"` } type KeptnMetricReference struct { - Name string `json:"name"` + // Name is the name of the referenced KeptnMetric. + Name string `json:"name"` + // Namespace is the namespace where the referenced KeptnMetric is located. Namespace string `json:"namespace,omitempty"` } -// KeptnEvaluationDefinitionStatus defines the observed state of KeptnEvaluationDefinition +// KeptnEvaluationDefinitionStatus defines the observed state of KeptnEvaluationDefinition. type KeptnEvaluationDefinitionStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file @@ -54,7 +61,9 @@ type KeptnEvaluationDefinition struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnEvaluationDefinitionSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnEvaluationDefinition. + Spec KeptnEvaluationDefinitionSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnEvaluationDefinition. Status KeptnEvaluationDefinitionStatus `json:"status,omitempty"` } diff --git a/operator/apis/lifecycle/v1alpha3/keptntask_test.go b/operator/apis/lifecycle/v1alpha3/keptntask_test.go index df541ca84f..ff3865c8a3 100644 --- a/operator/apis/lifecycle/v1alpha3/keptntask_test.go +++ b/operator/apis/lifecycle/v1alpha3/keptntask_test.go @@ -95,8 +95,8 @@ func TestKeptnTask(t *testing.T) { "keptn.sh/app": "app", "keptn.sh/task-name": "task", "keptn.sh/version": "appversion", - "label1": "label2", - }, task.CreateKeptnLabels()) + "annotation1": "annotation2", + }, task.CreateKeptnAnnotations()) task.Spec.Workload = "workload" task.Spec.WorkloadVersion = "workloadversion" @@ -106,8 +106,8 @@ func TestKeptnTask(t *testing.T) { "keptn.sh/workload": "workload", "keptn.sh/task-name": "task", "keptn.sh/version": "workloadversion", - "label1": "label2", - }, task.CreateKeptnLabels()) + "annotation1": "annotation2", + }, task.CreateKeptnAnnotations()) require.Equal(t, []attribute.KeyValue{ common.AppName.String("app"), diff --git a/operator/apis/lifecycle/v1alpha3/keptntask_types.go b/operator/apis/lifecycle/v1alpha3/keptntask_types.go index 98221eb0b5..182cd4512b 100644 --- a/operator/apis/lifecycle/v1alpha3/keptntask_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptntask_types.go @@ -32,17 +32,35 @@ import ( // KeptnTaskSpec defines the desired state of KeptnTask type KeptnTaskSpec struct { - Workload string `json:"workload"` - WorkloadVersion string `json:"workloadVersion"` - AppName string `json:"app"` - AppVersion string `json:"appVersion"` - TaskDefinition string `json:"taskDefinition"` - Context TaskContext `json:"context"` - Parameters TaskParameters `json:"parameters,omitempty"` + // Workload defines the KeptnWorkload for which the KeptnTask is executed. + Workload string `json:"workload"` + // WorkloadVersion defines the version of the KeptnWorkload for which the KeptnTask is executed. + WorkloadVersion string `json:"workloadVersion"` + // AppName defines the KeptnApp for which the KeptnTask is executed. + AppName string `json:"app"` + // AppVersion defines the version of the KeptnApp for which the KeptnTask is executed. + AppVersion string `json:"appVersion"` + // TaskDefinition refers to the name of the KeptnTaskDefinition + // which includes the specification for the task to be performed. + // The KeptnTaskDefinition can be + // located in the same namespace as the KeptnTask, or in the KLT namespace. + TaskDefinition string `json:"taskDefinition"` + // Context contains contextual information about the task execution. + Context TaskContext `json:"context"` + // Parameters contains parameters that will be passed to the job that executes the task. + Parameters TaskParameters `json:"parameters,omitempty"` + // SecureParameters contains secure parameters that will be passed to the job that executes the task. + // These will be stored and accessed as secrets in the cluster. SecureParameters SecureParameters `json:"secureParameters,omitempty"` - Type common.CheckType `json:"checkType,omitempty"` + // Type indicates whether the KeptnTask is part of the pre- or postDeployment phase. + Type common.CheckType `json:"checkType,omitempty"` + // Retries indicates how many times the KeptnTask can be attempted in the case of an error + // before considering the KeptnTask to be failed. // +kubebuilder:default:=10 Retries *int32 `json:"retries,omitempty"` + // Timeout specifies the maximum time to wait for the task to be completed successfully. + // If the task does not complete successfully within this time frame, it will be + // considered to be failed. // +optional // +kubebuilder:default:="5m" // +kubebuilder:validation:Pattern="^0|([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$" @@ -52,31 +70,51 @@ type KeptnTaskSpec struct { } type TaskContext struct { - WorkloadName string `json:"workloadName"` - AppName string `json:"appName"` - AppVersion string `json:"appVersion"` + // WorkloadName the name of the KeptnWorkload the KeptnTask is being executed for. + WorkloadName string `json:"workloadName"` + // AppName the name of the KeptnApp the KeptnTask is being executed for. + AppName string `json:"appName"` + // AppVersion the version of the KeptnApp the KeptnTask is being executed for. + AppVersion string `json:"appVersion"` + // WorkloadVersion the version of the KeptnWorkload the KeptnTask is being executed for. WorkloadVersion string `json:"workloadVersion"` - TaskType string `json:"taskType"` - ObjectType string `json:"objectType"` + // TaskType indicates whether the KeptnTask is part of the pre- or postDeployment phase. + TaskType string `json:"taskType"` + // ObjectType indicates whether the KeptnTask is being executed for a KeptnApp or KeptnWorkload. + ObjectType string `json:"objectType"` } type TaskParameters struct { + // Inline contains the parameters that will be made available to the job + // executing the KeptnTask via the 'DATA' environment variable. + // The 'DATA' environment variable's content will be a json + // encoded string containing all properties of the map provided. Inline map[string]string `json:"map,omitempty"` } type SecureParameters struct { + // Secret contains the parameters that will be made available to the job + // executing the KeptnTask via the 'SECRET_DATA' environment variable. + // The 'SECRET_DATA' environment variable's content will the same as value of the 'SECRET_DATA' + // key of the referenced secret. Secret string `json:"secret,omitempty"` } // KeptnTaskStatus defines the observed state of KeptnTask type KeptnTaskStatus struct { + // JobName is the name of the Job executing the Task. JobName string `json:"jobName,omitempty"` + // Status represents the overall state of the KeptnTask. // +kubebuilder:default:=Pending - Status common.KeptnState `json:"status,omitempty"` - Message string `json:"message,omitempty"` - StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` - Reason string `json:"reason,omitempty"` + Status common.KeptnState `json:"status,omitempty"` + // Message contains information about unexpected errors encountered during the execution of the KeptnTask. + Message string `json:"message,omitempty"` + // StartTime represents the time at which the KeptnTask started. + StartTime metav1.Time `json:"startTime,omitempty"` + // EndTime represents the time at which the KeptnTask finished. + EndTime metav1.Time `json:"endTime,omitempty"` + // Reason contains more information about the reason for the last transition of the Job executing the KeptnTask. + Reason string `json:"reason,omitempty"` } // +kubebuilder:object:root=true @@ -94,7 +132,9 @@ type KeptnTask struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnTaskSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnTask. + Spec KeptnTaskSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnTask. Status KeptnTaskStatus `json:"status,omitempty"` } @@ -166,16 +206,16 @@ func (t KeptnTask) SetSpanAttributes(span trace.Span) { span.SetAttributes(t.GetSpanAttributes()...) } -func (t KeptnTask) CreateKeptnLabels() map[string]string { +func (t KeptnTask) CreateKeptnAnnotations() map[string]string { if t.Spec.Workload != "" { - return common.MergeMaps(t.Labels, map[string]string{ + return common.MergeMaps(t.Annotations, map[string]string{ common.AppAnnotation: t.Spec.AppName, common.WorkloadAnnotation: t.Spec.Workload, common.VersionAnnotation: t.Spec.WorkloadVersion, common.TaskNameAnnotation: t.Name, }) } - return common.MergeMaps(t.Labels, map[string]string{ + return common.MergeMaps(t.Annotations, map[string]string{ common.AppAnnotation: t.Spec.AppName, common.VersionAnnotation: t.Spec.AppVersion, common.TaskNameAnnotation: t.Name, diff --git a/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types.go b/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types.go index 0823d58cce..93b45fe139 100644 --- a/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha3 import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -25,55 +26,81 @@ import ( // KeptnTaskDefinitionSpec defines the desired state of KeptnTaskDefinition type KeptnTaskDefinitionSpec struct { - Function FunctionSpec `json:"function,omitempty"` + // Function contains the definition for the function that is to be executed in KeptnTasks based on + // the KeptnTaskDefinitions. + // +optional + Function *FunctionSpec `json:"function,omitempty"` + // Container contains the definition for the container that is to be used in Job based on + // the KeptnTaskDefinitions. + // +optional + Container *ContainerSpec `json:"container,omitempty"` + // Retries specifies how many times a job executing the KeptnTaskDefinition should be restarted in the case + // of an unsuccessful attempt. // +kubebuilder:default:=10 Retries *int32 `json:"retries,omitempty"` + // Timeout specifies the maximum time to wait for the task to be completed successfully. + // If the task does not complete successfully within this time frame, it will be + // considered to be failed. // +optional // +kubebuilder:default:="5m" // +kubebuilder:validation:Pattern="^0|([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$" // +kubebuilder:validation:Type:=string - // +optional Timeout metav1.Duration `json:"timeout,omitempty"` } type FunctionSpec struct { - FunctionReference FunctionReference `json:"functionRef,omitempty"` - Inline Inline `json:"inline,omitempty"` - HttpReference HttpReference `json:"httpRef,omitempty"` + // FunctionReference allows to reference another KeptnTaskDefinition which contains the source code of the + // function to be executes for KeptnTasks based on this KeptnTaskDefinition. This can be useful when you have + // multiple KeptnTaskDefinitions that should execute the same logic, but each with different parameters. + FunctionReference FunctionReference `json:"functionRef,omitempty"` + // Inline allows to specify the code that should be executed directly in the KeptnTaskDefinition, as a multi-line + // string. + Inline Inline `json:"inline,omitempty"` + // HttpReference allows to point to an HTTP URL containing the code of the function. + HttpReference HttpReference `json:"httpRef,omitempty"` + // ConfigMapReference allows to reference a ConfigMap containing the code of the function. + // When referencing a ConfigMap, the code of the function must be available as a value of the 'code' key + // of the referenced ConfigMap. ConfigMapReference ConfigMapReference `json:"configMapRef,omitempty"` - Parameters TaskParameters `json:"parameters,omitempty"` - SecureParameters SecureParameters `json:"secureParameters,omitempty"` + // Parameters contains parameters that will be passed to the job that executes the task. + Parameters TaskParameters `json:"parameters,omitempty"` + // SecureParameters contains secure parameters that will be passed to the job that executes the task. + // These will be stored and accessed as secrets in the cluster. + SecureParameters SecureParameters `json:"secureParameters,omitempty"` } type ConfigMapReference struct { + // Name is the name of the referenced ConfigMap. Name string `json:"name,omitempty"` } type FunctionReference struct { + // Name is the name of the referenced KeptnTaksDefinition. Name string `json:"name,omitempty"` } type Inline struct { + // Code contains the code of the function. Code string `json:"code,omitempty"` } type HttpReference struct { + // Url is the URL containing the code of the function. Url string `json:"url,omitempty"` } type ContainerSpec struct { + *v1.Container `json:",inline"` } // 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 contains status information of the function definition for the task. 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 indicates the ConfigMap in which the function code is stored. ConfigMap string `json:"configMap,omitempty"` } @@ -86,7 +113,9 @@ type KeptnTaskDefinition struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnTaskDefinitionSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnTaskDefinition. + Spec KeptnTaskDefinitionSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnTaskDefinition. Status KeptnTaskDefinitionStatus `json:"status,omitempty"` } @@ -102,3 +131,19 @@ type KeptnTaskDefinitionList struct { func init() { SchemeBuilder.Register(&KeptnTaskDefinition{}, &KeptnTaskDefinitionList{}) } + +func (d KeptnTaskDefinition) SpecExists() bool { + return d.IsJSSpecDefined() || d.IsContainerSpecDefined() +} + +func (d KeptnTaskDefinition) IsJSSpecDefined() bool { + return d.Spec.Function != nil +} + +func (d KeptnTaskDefinition) IsContainerSpecDefined() bool { + return d.Spec.Container != nil +} + +func (d KeptnTaskDefinition) IsVolumeMountPresent() bool { + return d.IsContainerSpecDefined() && d.Spec.Container.VolumeMounts != nil && len(d.Spec.Container.VolumeMounts) > 0 +} diff --git a/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types_test.go b/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types_test.go new file mode 100644 index 0000000000..3f141d7c2f --- /dev/null +++ b/operator/apis/lifecycle/v1alpha3/keptntaskdefinition_types_test.go @@ -0,0 +1,167 @@ +package v1alpha3 + +import ( + "testing" + + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" +) + +var jsTaskDef = &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{ + Function: &FunctionSpec{ + Inline: Inline{ + Code: "some code", + }, + }, + }, +} + +var containerTaskDef = &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{ + Container: &ContainerSpec{ + Container: &v1.Container{ + Image: "image", + }, + }, + }, +} + +func Test_SpecExists(t *testing.T) { + tests := []struct { + name string + taskDef *KeptnTaskDefinition + want bool + }{ + { + name: "js builder", + taskDef: jsTaskDef, + want: true, + }, + { + name: "container builder", + taskDef: containerTaskDef, + want: true, + }, + { + name: "empty builder", + taskDef: &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, tt.taskDef.SpecExists()) + }) + } +} + +func Test_IsJSSpecDefined(t *testing.T) { + tests := []struct { + name string + taskDef *KeptnTaskDefinition + want bool + }{ + { + name: "defined", + taskDef: jsTaskDef, + want: true, + }, + { + name: "empty", + taskDef: &KeptnTaskDefinition{}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, tt.taskDef.IsJSSpecDefined()) + }) + } +} + +func Test_IsVolumeMountPresent(t *testing.T) { + tests := []struct { + name string + taskDef *KeptnTaskDefinition + want bool + }{ + { + name: "defined", + taskDef: &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{ + Container: &ContainerSpec{ + Container: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{ + { + Name: "name", + MountPath: "path", + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "empty", + taskDef: &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{ + Container: &ContainerSpec{ + Container: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{}, + }, + }, + }, + }, + want: false, + }, + { + name: "nil", + taskDef: &KeptnTaskDefinition{ + Spec: KeptnTaskDefinitionSpec{ + Container: &ContainerSpec{ + Container: &v1.Container{ + Image: "image", + }, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, tt.taskDef.IsVolumeMountPresent()) + }) + } +} + +func Test_IsContainerSpecDefined(t *testing.T) { + tests := []struct { + name string + taskDef *KeptnTaskDefinition + want bool + }{ + { + name: "defined", + taskDef: containerTaskDef, + want: true, + }, + { + name: "empty", + taskDef: &KeptnTaskDefinition{}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, tt.taskDef.IsContainerSpecDefined()) + }) + } +} diff --git a/operator/apis/lifecycle/v1alpha3/keptnworkload_types.go b/operator/apis/lifecycle/v1alpha3/keptnworkload_types.go index c4c6937941..f550f7179f 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnworkload_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnworkload_types.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,17 +33,36 @@ import ( // KeptnWorkloadSpec defines the desired state of KeptnWorkload type KeptnWorkloadSpec struct { - AppName string `json:"app"` - Version string `json:"version"` - PreDeploymentTasks []string `json:"preDeploymentTasks,omitempty"` - PostDeploymentTasks []string `json:"postDeploymentTasks,omitempty"` - PreDeploymentEvaluations []string `json:"preDeploymentEvaluations,omitempty"` - PostDeploymentEvaluations []string `json:"postDeploymentEvaluations,omitempty"` - ResourceReference ResourceReference `json:"resourceReference"` + // AppName is the name of the KeptnApp containing the KeptnWorkload. + AppName string `json:"app"` + // Version defines the version of the KeptnWorkload. + Version string `json:"version"` + // PreDeploymentTasks is a list of all tasks to be performed during the pre-deployment phase of the KeptnWorkload. + // The items of this list refer to the names of KeptnTaskDefinitions + // located in the same namespace as the KeptnApp, or in the KLT namespace. + PreDeploymentTasks []string `json:"preDeploymentTasks,omitempty"` + // PostDeploymentTasks is a list of all tasks to be performed during the post-deployment phase of the KeptnWorkload. + // The items of this list refer to the names of KeptnTaskDefinitions + // located in the same namespace as the KeptnWorkload, or in the KLT namespace. + PostDeploymentTasks []string `json:"postDeploymentTasks,omitempty"` + // PreDeploymentEvaluations is a list of all evaluations to be performed + // during the pre-deployment phase of the KeptnWorkload. + // The items of this list refer to the names of KeptnEvaluationDefinitions + // located in the same namespace as the KeptnWorkload, or in the KLT namespace. + PreDeploymentEvaluations []string `json:"preDeploymentEvaluations,omitempty"` + // PostDeploymentEvaluations is a list of all evaluations to be performed + // during the post-deployment phase of the KeptnWorkload. + // The items of this list refer to the names of KeptnEvaluationDefinitions + // located in the same namespace as the KeptnWorkload, or in the KLT namespace. + PostDeploymentEvaluations []string `json:"postDeploymentEvaluations,omitempty"` + // ResourceReference is a reference to the Kubernetes resource + // (Deployment, DaemonSet, StatefulSet or ReplicaSet) the KeptnWorkload is representing. + ResourceReference ResourceReference `json:"resourceReference"` } // KeptnWorkloadStatus defines the observed state of KeptnWorkload type KeptnWorkloadStatus struct { + // CurrentVersion indicates the version that is currently deployed or being reconciled. CurrentVersion string `json:"currentVersion,omitempty"` } @@ -57,7 +77,9 @@ type KeptnWorkload struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnWorkloadSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnWorkload. + Spec KeptnWorkloadSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnWorkload. Status KeptnWorkloadStatus `json:"status,omitempty"` } @@ -81,7 +103,7 @@ func init() { } func (w KeptnWorkload) GetWorkloadInstanceName() string { - return strings.ToLower(w.Name + "-" + w.Spec.Version) + return operatorcommon.CreateResourceName(common.MaxK8sObjectLength, common.MinKLTNameLen, w.Name, w.Spec.Version) } func (w KeptnWorkload) SetSpanAttributes(span trace.Span) { diff --git a/operator/apis/lifecycle/v1alpha3/keptnworkloadinstance_types.go b/operator/apis/lifecycle/v1alpha3/keptnworkloadinstance_types.go index 3b03b64d9f..0440489ac3 100644 --- a/operator/apis/lifecycle/v1alpha3/keptnworkloadinstance_types.go +++ b/operator/apis/lifecycle/v1alpha3/keptnworkloadinstance_types.go @@ -34,31 +34,53 @@ import ( // KeptnWorkloadInstanceSpec defines the desired state of KeptnWorkloadInstance type KeptnWorkloadInstanceSpec struct { KeptnWorkloadSpec `json:",inline"` - WorkloadName string `json:"workloadName"` - PreviousVersion string `json:"previousVersion,omitempty"` - TraceId map[string]string `json:"traceId,omitempty"` + // WorkloadName is the name of the KeptnWorkload. + WorkloadName string `json:"workloadName"` + // PreviousVersion is the version of the KeptnWorkload that has been deployed prior to this version. + PreviousVersion string `json:"previousVersion,omitempty"` + // TraceId contains the OpenTelemetry trace ID. + TraceId map[string]string `json:"traceId,omitempty"` } // KeptnWorkloadInstanceStatus defines the observed state of KeptnWorkloadInstance type KeptnWorkloadInstanceStatus struct { + // PreDeploymentStatus indicates the current status of the KeptnWorkloadInstance's PreDeployment phase. // +kubebuilder:default:=Pending PreDeploymentStatus common.KeptnState `json:"preDeploymentStatus,omitempty"` + // DeploymentStatus indicates the current status of the KeptnWorkloadInstance's Deployment phase. // +kubebuilder:default:=Pending DeploymentStatus common.KeptnState `json:"deploymentStatus,omitempty"` + // PreDeploymentEvaluationStatus indicates the current status of the KeptnWorkloadInstance's PreDeploymentEvaluation phase. // +kubebuilder:default:=Pending PreDeploymentEvaluationStatus common.KeptnState `json:"preDeploymentEvaluationStatus,omitempty"` + // PostDeploymentEvaluationStatus indicates the current status of the KeptnWorkloadInstance's PostDeploymentEvaluation phase. // +kubebuilder:default:=Pending PostDeploymentEvaluationStatus common.KeptnState `json:"postDeploymentEvaluationStatus,omitempty"` + // PostDeploymentStatus indicates the current status of the KeptnWorkloadInstance's PostDeployment phase. // +kubebuilder:default:=Pending - PostDeploymentStatus common.KeptnState `json:"postDeploymentStatus,omitempty"` - PreDeploymentTaskStatus []ItemStatus `json:"preDeploymentTaskStatus,omitempty"` - PostDeploymentTaskStatus []ItemStatus `json:"postDeploymentTaskStatus,omitempty"` - PreDeploymentEvaluationTaskStatus []ItemStatus `json:"preDeploymentEvaluationTaskStatus,omitempty"` - PostDeploymentEvaluationTaskStatus []ItemStatus `json:"postDeploymentEvaluationTaskStatus,omitempty"` - StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` - CurrentPhase string `json:"currentPhase,omitempty"` - PhaseTraceIDs common.PhaseTraceID `json:"phaseTraceIDs,omitempty"` + PostDeploymentStatus common.KeptnState `json:"postDeploymentStatus,omitempty"` + // PreDeploymentTaskStatus indicates the current state of each preDeploymentTask of the KeptnWorkloadInstance. + PreDeploymentTaskStatus []ItemStatus `json:"preDeploymentTaskStatus,omitempty"` + // PostDeploymentTaskStatus indicates the current state of each postDeploymentTask of the KeptnWorkloadInstance. + PostDeploymentTaskStatus []ItemStatus `json:"postDeploymentTaskStatus,omitempty"` + // PreDeploymentEvaluationTaskStatus indicates the current state of each preDeploymentEvaluation of the KeptnWorkloadInstance. + PreDeploymentEvaluationTaskStatus []ItemStatus `json:"preDeploymentEvaluationTaskStatus,omitempty"` + // PostDeploymentEvaluationTaskStatus indicates the current state of each postDeploymentEvaluation of the KeptnWorkloadInstance. + PostDeploymentEvaluationTaskStatus []ItemStatus `json:"postDeploymentEvaluationTaskStatus,omitempty"` + // StartTime represents the time at which the deployment of the KeptnWorkloadInstance started. + StartTime metav1.Time `json:"startTime,omitempty"` + // EndTime represents the time at which the deployment of the KeptnWorkloadInstance finished. + EndTime metav1.Time `json:"endTime,omitempty"` + // CurrentPhase indicates the current phase of the KeptnWorkloadInstance. This can be: + // - PreDeploymentTasks + // - PreDeploymentEvaluations + // - Deployment + // - PostDeploymentTasks + // - PostDeploymentEvaluations + CurrentPhase string `json:"currentPhase,omitempty"` + // PhaseTraceIDs contains the trace IDs of the OpenTelemetry spans of each phase of the KeptnWorkloadInstance + PhaseTraceIDs common.PhaseTraceID `json:"phaseTraceIDs,omitempty"` + // Status represents the overall status of the KeptnWorkloadInstance. // +kubebuilder:default:=Pending Status common.KeptnState `json:"status,omitempty"` } @@ -69,9 +91,11 @@ type ItemStatus struct { // +kubebuilder:default:=Pending Status common.KeptnState `json:"status,omitempty"` // Name is the name of the Evaluation/Task - Name string `json:"name,omitempty"` + Name string `json:"name,omitempty"` + // StartTime represents the time at which the Item (Evaluation/Task) started. StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` + // EndTime represents the time at which the Item (Evaluation/Task) started. + EndTime metav1.Time `json:"endTime,omitempty"` } //+kubebuilder:object:root=true @@ -93,7 +117,9 @@ type KeptnWorkloadInstance struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec KeptnWorkloadInstanceSpec `json:"spec,omitempty"` + // Spec describes the desired state of the KeptnWorkloadInstance. + Spec KeptnWorkloadInstanceSpec `json:"spec,omitempty"` + // Status describes the current state of the KeptnWorkloadInstance. Status KeptnWorkloadInstanceStatus `json:"status,omitempty"` } diff --git a/operator/apis/lifecycle/v1alpha3/zz_generated.deepcopy.go b/operator/apis/lifecycle/v1alpha3/zz_generated.deepcopy.go index 7ee7cd41e2..cc68f44e91 100644 --- a/operator/apis/lifecycle/v1alpha3/zz_generated.deepcopy.go +++ b/operator/apis/lifecycle/v1alpha3/zz_generated.deepcopy.go @@ -24,6 +24,7 @@ package v1alpha3 import ( "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" "go.opentelemetry.io/otel/propagation" + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -45,6 +46,11 @@ func (in *ConfigMapReference) DeepCopy() *ConfigMapReference { // 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 + if in.Container != nil { + in, out := &in.Container, &out.Container + *out = new(v1.Container) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSpec. @@ -909,7 +915,16 @@ func (in *KeptnTaskDefinitionList) DeepCopyObject() runtime.Object { // 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) + if in.Function != nil { + in, out := &in.Function, &out.Function + *out = new(FunctionSpec) + (*in).DeepCopyInto(*out) + } + if in.Container != nil { + in, out := &in.Container, &out.Container + *out = new(ContainerSpec) + (*in).DeepCopyInto(*out) + } if in.Retries != nil { in, out := &in.Retries, &out.Retries *out = new(int32) diff --git a/operator/cmd/certificates/certificatehandler.go b/operator/cmd/certificates/certificatehandler.go deleted file mode 100644 index ba9450fad9..0000000000 --- a/operator/cmd/certificates/certificatehandler.go +++ /dev/null @@ -1,22 +0,0 @@ -package certificates - -import ( - "crypto/x509" - "encoding/pem" -) - -//go:generate moq -pkg fake -skip-ensure -out ./fake/certificatehandler_mock.go . ICertificateHandler -type ICertificateHandler interface { - Decode(data []byte) (p *pem.Block, rest []byte) - Parse(der []byte) (*x509.Certificate, error) -} - -type defaultCertificateHandler struct { -} - -func (c defaultCertificateHandler) Decode(data []byte) (p *pem.Block, rest []byte) { - return pem.Decode(data) -} -func (c defaultCertificateHandler) Parse(der []byte) (*x509.Certificate, error) { - return x509.ParseCertificate(der) -} diff --git a/operator/cmd/certificates/fake/certificatehandler_mock.go b/operator/cmd/certificates/fake/certificatehandler_mock.go deleted file mode 100644 index 45a5eacbae..0000000000 --- a/operator/cmd/certificates/fake/certificatehandler_mock.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "crypto/x509" - "encoding/pem" - "sync" -) - -// ICertificateHandlerMock is a mock implementation of certificates.ICertificateHandler. -// -// func TestSomethingThatUsesICertificateHandler(t *testing.T) { -// -// // make and configure a mocked certificates.ICertificateHandler -// mockedICertificateHandler := &ICertificateHandlerMock{ -// DecodeFunc: func(data []byte) (*pem.Block, []byte) { -// panic("mock out the Decode method") -// }, -// ParseFunc: func(der []byte) (*x509.Certificate, error) { -// panic("mock out the Parse method") -// }, -// } -// -// // use mockedICertificateHandler in code that requires certificates.ICertificateHandler -// // and then make assertions. -// -// } -type ICertificateHandlerMock struct { - // DecodeFunc mocks the Decode method. - DecodeFunc func(data []byte) (*pem.Block, []byte) - - // ParseFunc mocks the Parse method. - ParseFunc func(der []byte) (*x509.Certificate, error) - - // calls tracks calls to the methods. - calls struct { - // Decode holds details about calls to the Decode method. - Decode []struct { - // Data is the data argument value. - Data []byte - } - // Parse holds details about calls to the Parse method. - Parse []struct { - // Der is the der argument value. - Der []byte - } - } - lockDecode sync.RWMutex - lockParse sync.RWMutex -} - -// Decode calls DecodeFunc. -func (mock *ICertificateHandlerMock) Decode(data []byte) (*pem.Block, []byte) { - if mock.DecodeFunc == nil { - panic("ICertificateHandlerMock.DecodeFunc: method is nil but ICertificateHandler.Decode was just called") - } - callInfo := struct { - Data []byte - }{ - Data: data, - } - mock.lockDecode.Lock() - mock.calls.Decode = append(mock.calls.Decode, callInfo) - mock.lockDecode.Unlock() - return mock.DecodeFunc(data) -} - -// DecodeCalls gets all the calls that were made to Decode. -// Check the length with: -// -// len(mockedICertificateHandler.DecodeCalls()) -func (mock *ICertificateHandlerMock) DecodeCalls() []struct { - Data []byte -} { - var calls []struct { - Data []byte - } - mock.lockDecode.RLock() - calls = mock.calls.Decode - mock.lockDecode.RUnlock() - return calls -} - -// Parse calls ParseFunc. -func (mock *ICertificateHandlerMock) Parse(der []byte) (*x509.Certificate, error) { - if mock.ParseFunc == nil { - panic("ICertificateHandlerMock.ParseFunc: method is nil but ICertificateHandler.Parse was just called") - } - callInfo := struct { - Der []byte - }{ - Der: der, - } - mock.lockParse.Lock() - mock.calls.Parse = append(mock.calls.Parse, callInfo) - mock.lockParse.Unlock() - return mock.ParseFunc(der) -} - -// ParseCalls gets all the calls that were made to Parse. -// Check the length with: -// -// len(mockedICertificateHandler.ParseCalls()) -func (mock *ICertificateHandlerMock) ParseCalls() []struct { - Der []byte -} { - var calls []struct { - Der []byte - } - mock.lockParse.RLock() - calls = mock.calls.Parse - mock.lockParse.RUnlock() - return calls -} diff --git a/operator/cmd/certificates/watcher.go b/operator/cmd/certificates/watcher.go deleted file mode 100644 index 3621679ccc..0000000000 --- a/operator/cmd/certificates/watcher.go +++ /dev/null @@ -1,132 +0,0 @@ -package certificates - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/go-logr/logr" - "github.com/spf13/afero" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - certificateRenewalInterval = 6 * time.Hour - ServerKey = "tls.key" - ServerCert = "tls.crt" - CertThreshold = 5 * time.Minute -) - -type CertificateWatcher struct { - apiReader client.Reader - fs afero.Fs - certificateDirectory string - namespace string - certificateSecretName string - certificateTreshold time.Duration - ICertificateHandler - Log logr.Logger -} - -func NewCertificateWatcher(reader client.Reader, certDir string, namespace string, secretName string, log logr.Logger) *CertificateWatcher { - return &CertificateWatcher{ - apiReader: reader, - fs: afero.NewOsFs(), - certificateDirectory: certDir, - namespace: namespace, - certificateSecretName: secretName, - ICertificateHandler: defaultCertificateHandler{}, - certificateTreshold: CertThreshold, - Log: log, - } -} - -func (watcher *CertificateWatcher) watchForCertificatesSecret() { - for { - <-time.After(certificateRenewalInterval) - watcher.Log.Info("checking for new certificates") - if err := watcher.updateCertificatesFromSecret(); err != nil { - watcher.Log.Error(err, "failed to update certificates") - } else { - watcher.Log.Info("updated certificate successfully") - } - } -} - -func (watcher *CertificateWatcher) updateCertificatesFromSecret() error { - var secret corev1.Secret - - err := watcher.apiReader.Get(context.TODO(), - client.ObjectKey{Name: watcher.certificateSecretName, Namespace: watcher.namespace}, &secret) - if err != nil { - return err - } - - watcher.Log.Info("checking dir", "watcher.certificateDirectory ", watcher.certificateDirectory) - if _, err = watcher.fs.Stat(watcher.certificateDirectory); os.IsNotExist(err) { - err = watcher.fs.MkdirAll(watcher.certificateDirectory, 0755) - if err != nil { - return fmt.Errorf("could not create cert directory: %w", err) - } - } - - for _, filename := range []string{ServerCert, ServerKey} { - if err = watcher.ensureCertificateFile(secret, filename); err != nil { - return err - } - } - isValid, err := watcher.ValidateCertificateExpiration(secret.Data[ServerCert], certificateRenewalInterval, time.Now()) - if err != nil { - return err - } else if !isValid { - return fmt.Errorf("certificate is outdated") - } - return nil -} - -func (watcher *CertificateWatcher) ensureCertificateFile(secret corev1.Secret, filename string) error { - f := filepath.Join(watcher.certificateDirectory, filename) - data, err := afero.ReadFile(watcher.fs, f) - if os.IsNotExist(err) || !bytes.Equal(data, secret.Data[filename]) { - return afero.WriteFile(watcher.fs, f, secret.Data[filename], 0666) - } - return err - -} - -func (watcher *CertificateWatcher) WaitForCertificates() { - for threshold := time.Now().Add(watcher.certificateTreshold); time.Now().Before(threshold); { - - if err := watcher.updateCertificatesFromSecret(); err != nil { - if k8serrors.IsNotFound(err) { - watcher.Log.Info("waiting for certificate secret to be available.") - } else { - watcher.Log.Error(err, "failed to update certificates") - } - time.Sleep(10 * time.Second) - continue - } - break - } - go watcher.watchForCertificatesSecret() -} - -func (watcher *CertificateWatcher) ValidateCertificateExpiration(certData []byte, renewalThreshold time.Duration, now time.Time) (bool, error) { - if block, _ := watcher.Decode(certData); block == nil { - watcher.Log.Error(errors.New("can't decode PEM file"), "failed to parse certificate") - return false, nil - } else if cert, err := watcher.Parse(block.Bytes); err != nil { - watcher.Log.Error(err, "failed to parse certificate") - return false, err - } else if now.After(cert.NotAfter.Add(-renewalThreshold)) { - watcher.Log.Info("certificate is outdated, waiting for new ones", "Valid until", cert.NotAfter.UTC()) - return false, nil - } - return true, nil -} diff --git a/operator/cmd/certificates/watcher_test.go b/operator/cmd/certificates/watcher_test.go deleted file mode 100644 index 0b393332d8..0000000000 --- a/operator/cmd/certificates/watcher_test.go +++ /dev/null @@ -1,318 +0,0 @@ -package certificates - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "os" - "path/filepath" - "testing" - "time" - - "github.com/go-logr/logr/testr" - "github.com/keptn/lifecycle-toolkit/operator/cmd/certificates/fake" - fakeclient "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" - "github.com/pkg/errors" - "github.com/spf13/afero" - "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const CACERT = `-----BEGIN CERTIFICATE----- -MIICPTCCAeKgAwIBAgIRAMIV/0UqFGHgKSYOWBdx/KcwCgYIKoZIzj0EAwIwczEL -MAkGA1UEBhMCQVQxCzAJBgNVBAgTAktMMRMwEQYDVQQHEwpLbGFnZW5mdXJ0MQ4w -DAYDVQQKEwVLZXB0bjEZMBcGA1UECxMQTGlmZWN5Y2xlVG9vbGtpdDEXMBUGA1UE -AwwOKi5rZXB0bi1ucy5zdmMwHhcNMjMwNDE5MTEwNDUzWhcNMjQwNDE4MTEwNDUz -WjBzMQswCQYDVQQGEwJBVDELMAkGA1UECBMCS0wxEzARBgNVBAcTCktsYWdlbmZ1 -cnQxDjAMBgNVBAoTBUtlcHRuMRkwFwYDVQQLExBMaWZlY3ljbGVUb29sa2l0MRcw -FQYDVQQDDA4qLmtlcHRuLW5zLnN2YzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA -BPxAP4JTJfwKz/P32dXuyfVi7kinQPebSYwF/gRAUcN0dCAi6GnxbI2OXlcU0guD -zHXv3VRh3EX2fiNszcfKaCajVzBVMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAK -BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQUGe/8XYV1HsZs -nWsyrOCCGr/sQDAKBggqhkjOPQQDAgNJADBGAiEAkcPaCANDXW5Uillrof0VrnPw -ow49D22Gsrh7YM+vmTQCIQDU1L5IT0Zz+bdIyFSsDnEUXZDeydNv56DoSLh+358Y -aw== ------END CERTIFICATE-----` - -const CAKEY = `-----BEGIN PRIVATE KEY----- -MHcCAQEEII5SAqBxINKatksyu2mTvLZZhfEOpNinYJDwlQjkfreboAoGCCqGSM49 -AwEHoUQDQgAE/EA/glMl/ArP8/fZ1e7J9WLuSKdA95tJjAX+BEBRw3R0ICLoafFs -jY5eVxTSC4PMde/dVGHcRfZ+I2zNx8poJg== ------END PRIVATE KEY-----` - -const uniqueIDPEM = `-----BEGIN CERTIFICATE----- -MIIFsDCCBJigAwIBAgIIrOyC1ydafZMwDQYJKoZIhvcNAQEFBQAwgY4xgYswgYgG -A1UEAx6BgABNAGkAYwByAG8AcwBvAGYAdAAgAEYAbwByAGUAZgByAG8AbgB0ACAA -VABNAEcAIABIAFQAVABQAFMAIABJAG4AcwBwAGUAYwB0AGkAbwBuACAAQwBlAHIA -dABpAGYAaQBjAGEAdABpAG8AbgAgAEEAdQB0AGgAbwByAGkAdAB5MB4XDTE0MDEx -ODAwNDEwMFoXDTE1MTExNTA5Mzc1NlowgZYxCzAJBgNVBAYTAklEMRAwDgYDVQQI -EwdqYWthcnRhMRIwEAYDVQQHEwlJbmRvbmVzaWExHDAaBgNVBAoTE3N0aG9ub3Jl -aG90ZWxyZXNvcnQxHDAaBgNVBAsTE3N0aG9ub3JlaG90ZWxyZXNvcnQxJTAjBgNV -BAMTHG1haWwuc3Rob25vcmVob3RlbHJlc29ydC5jb20wggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCvuu0qpI+Ko2X84Twkf84cRD/rgp6vpgc5Ebejx/D4 -PEVON5edZkazrMGocK/oQqIlRxx/lefponN/chlGcllcVVPWTuFjs8k+Aat6T1qp -4iXxZekAqX+U4XZMIGJD3PckPL6G2RQSlF7/LhGCsRNRdKpMWSTbou2Ma39g52Kf -gsl3SK/GwLiWpxpcSkNQD1hugguEIsQYLxbeNwpcheXZtxbBGguPzQ7rH8c5vuKU -BkMOzaiNKLzHbBdFSrua8KWwCJg76Vdq/q36O9GlW6YgG3i+A4pCJjXWerI1lWwX -Ktk5V+SvUHGey1bkDuZKJ6myMk2pGrrPWCT7jP7WskChAgMBAAGBCQBCr1dgEleo -cKOCAfswggH3MIHDBgNVHREEgbswgbiCHG1haWwuc3Rob25vcmVob3RlbHJlc29y -dC5jb22CIGFzaGNoc3ZyLnN0aG9ub3JlaG90ZWxyZXNvcnQuY29tgiRBdXRvRGlz -Y292ZXIuc3Rob25vcmVob3RlbHJlc29ydC5jb22CHEF1dG9EaXNjb3Zlci5ob3Rl -bHJlc29ydC5jb22CCEFTSENIU1ZSghdzdGhvbm9yZWhvdGVscmVzb3J0LmNvbYIP -aG90ZWxyZXNvcnQuY29tMCEGCSsGAQQBgjcUAgQUHhIAVwBlAGIAUwBlAHIAdgBl -AHIwHQYDVR0OBBYEFMAC3UR4FwAdGekbhMgnd6lMejtbMAsGA1UdDwQEAwIFoDAT -BgNVHSUEDDAKBggrBgEFBQcDATAJBgNVHRMEAjAAMIG/BgNVHQEEgbcwgbSAFGfF -6xihk+gJJ5TfwvtWe1UFnHLQoYGRMIGOMYGLMIGIBgNVBAMegYAATQBpAGMAcgBv -AHMAbwBmAHQAIABGAG8AcgBlAGYAcgBvAG4AdAAgAFQATQBHACAASABUAFQAUABT -ACAASQBuAHMAcABlAGMAdABpAG8AbgAgAEMAZQByAHQAaQBmAGkAYwBhAHQAaQBv -AG4AIABBAHUAdABoAG8AcgBpAHQAeYIIcKhXEmBXr0IwDQYJKoZIhvcNAQEFBQAD -ggEBABlSxyCMr3+ANr+WmPSjyN5YCJBgnS0IFCwJAzIYP87bcTye/U8eQ2+E6PqG -Q7Huj7nfHEw9qnGo+HNyPp1ad3KORzXDb54c6xEoi+DeuPzYHPbn4c3hlH49I0aQ -eWW2w4RslSWpLvO6Y7Lboyz2/Thk/s2kd4RHxkkWpH2ltPqJuYYg3X6oM5+gIFHJ -WGnh+ojZ5clKvS5yXh3Wkj78M6sb32KfcBk0Hx6NkCYPt60ODYmWtvqwtw6r73u5 -TnTYWRNvo2svX69TriL+CkHY9O1Hkwf2It5zHl3gNiKTJVaak8AuEz/CKWZneovt -yYLwhUhg3PX5Co1VKYE+9TxloiE= ------END CERTIFICATE-----` - -var ERR_BAD_CERT = errors.New("bad cert") - -var emptySecret = v1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "my-cert", - }, -} - -var goodSecret = v1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "my-cert", - }, - Data: map[string][]byte{ - ServerCert: []byte(CACERT), - ServerKey: []byte(CAKEY), - }, -} - -func TestCertificateWatcher_ValidateCertificateExpiration(t *testing.T) { - - tests := []struct { - name string - certHandler ICertificateHandler - certData []byte - renewalThreshold time.Duration - now time.Time - want bool - wantErr error - }{ - { - name: "certificate cannot be decoded", - certHandler: &fake.ICertificateHandlerMock{ - DecodeFunc: func(data []byte) (p *pem.Block, rest []byte) { - return nil, nil //fake a failure in the decoding - }, - ParseFunc: nil, - }, - want: false, - }, - { - name: "certificate cannot be parsed", - certHandler: &fake.ICertificateHandlerMock{ - DecodeFunc: func(data []byte) (p *pem.Block, rest []byte) { - return &pem.Block{Type: "test", Bytes: []byte("testdata")}, nil - }, - ParseFunc: func(der []byte) (*x509.Certificate, error) { - return nil, ERR_BAD_CERT - }, - }, - want: false, - wantErr: ERR_BAD_CERT, - }, - { - name: "good certificate - unexpired", - certData: []byte(uniqueIDPEM), - certHandler: defaultCertificateHandler{}, - want: true, - }, - { - name: "good certificate - expired", - certData: []byte(uniqueIDPEM), - now: time.Now(), //setting up now makes sure that the threshold is passed - certHandler: defaultCertificateHandler{}, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - ICertificateHandler: tt.certHandler, - Log: testr.New(t), - } - got, err := watcher.ValidateCertificateExpiration(tt.certData, tt.renewalThreshold, tt.now) - if tt.wantErr != nil { - require.Error(t, err) - t.Log("want:", tt.wantErr, "got:", err) - require.True(t, errors.Is(tt.wantErr, err)) - } - require.Equal(t, got, tt.want) - }) - } -} - -func TestCertificateWatcher_ensureCertificateFile(t *testing.T) { - - certdir := t.TempDir() - f := filepath.Join(certdir, ServerCert) - err := os.WriteFile(f, goodSecret.Data[ServerCert], 0666) - require.Nil(t, err) - baddir := t.TempDir() - f = filepath.Join(baddir, ServerCert) - err = os.WriteFile(f, goodSecret.Data[ServerKey], 0666) - require.Nil(t, err) - tests := []struct { - name string - fs afero.Fs - secret v1.Secret - filename string - certDir string - wantErr bool - err string - }{ - { - name: "if good cert exist in fs no error", - secret: goodSecret, - certDir: certdir, - filename: ServerCert, - wantErr: false, - }, - - { - name: "if unexisting file name, we expect a file system error", - secret: emptySecret, - filename: "$%&/())=$§%/=", - certDir: baddir, - wantErr: true, - err: "no such file or directory", - }, - - { - name: "wrong file content is replaced with updated cert", - certDir: baddir, - secret: goodSecret, - filename: ServerCert, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - fs: afero.NewOsFs(), - certificateDirectory: tt.certDir, - } - err := watcher.ensureCertificateFile(tt.secret, tt.filename) - if !tt.wantErr { - require.Nil(t, err) - f = filepath.Join(tt.certDir, ServerCert) - data, err := os.ReadFile(f) - if err != nil { - panic(err) - } - if !bytes.Equal(data, tt.secret.Data[tt.filename]) { - t.Errorf("ensureCertificateFile()data %v was not replaced with %v", data, tt.secret.Data[tt.filename]) - } - } else { - require.Contains(t, err.Error(), tt.err) - } - }) - } -} - -func TestCertificateWatcher_updateCertificatesFromSecret(t *testing.T) { - - oldDir := t.TempDir() - os.Remove(oldDir) - - tests := []struct { - name string - apiReader client.Reader - certificateDirectory string - namespace string - certificateSecretName string - wantErr error - }{ - { - name: "certificate not found", - apiReader: fakeclient.NewClient(), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("secrets \"my-cert\" not found"), - }, - { - name: "outdated certificate found, nothing in dir", - apiReader: fakeclient.NewClient(&emptySecret), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("certificate is outdated"), - }, - - { - name: "outdated certificate found, not existing in dir", - apiReader: fakeclient.NewClient(&emptySecret), - certificateDirectory: oldDir, - namespace: "default", - certificateSecretName: "my-cert", - wantErr: errors.New("certificate is outdated"), - }, - { - name: "good certificate - not stored", - apiReader: fakeclient.NewClient(&goodSecret), - certificateDirectory: t.TempDir(), - namespace: "default", - certificateSecretName: "my-cert", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - watcher := &CertificateWatcher{ - apiReader: tt.apiReader, - fs: afero.NewOsFs(), - certificateDirectory: tt.certificateDirectory, - namespace: tt.namespace, - certificateSecretName: tt.certificateSecretName, - ICertificateHandler: defaultCertificateHandler{}, - Log: testr.New(t), - } - err := watcher.updateCertificatesFromSecret() - if tt.wantErr == nil { - require.Nil(t, err) - } else { - require.NotNil(t, err) - require.Contains(t, err.Error(), tt.wantErr.Error()) - } - }) - } -} - -func TestNewCertificateWatcher(t *testing.T) { - logger := testr.New(t) - client := fakeclient.NewClient() - want := &CertificateWatcher{ - apiReader: client, - fs: afero.NewOsFs(), - namespace: "default", - certificateSecretName: "my-secret", - certificateDirectory: "test", - certificateTreshold: CertThreshold, - ICertificateHandler: defaultCertificateHandler{}, - Log: testr.New(t), - } - got := NewCertificateWatcher(client, "test", "default", "my-secret", logger) - require.EqualValues(t, got, want) - -} diff --git a/operator/cmd/webhook/builder.go b/operator/cmd/webhook/builder.go deleted file mode 100644 index d87b0ef591..0000000000 --- a/operator/cmd/webhook/builder.go +++ /dev/null @@ -1,96 +0,0 @@ -package webhook - -import ( - "flag" - - "github.com/keptn/lifecycle-toolkit/operator/cmd/certificates" - "github.com/keptn/lifecycle-toolkit/operator/cmd/config" - cmdManager "github.com/keptn/lifecycle-toolkit/operator/cmd/manager" - "github.com/keptn/lifecycle-toolkit/operator/webhooks" - "github.com/keptn/lifecycle-toolkit/operator/webhooks/pod_mutator" - "github.com/pkg/errors" - "go.opentelemetry.io/otel" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/webhook" -) - -const ( - FlagCertificateDirectory = "certs-dir" - FlagCertificateFileName = "cert" - FlagCertificateKeyFileName = "cert-key" -) - -var ( - certificateDirectory string - certificateFileName string - certificateKeyFileName string -) - -type Builder struct { - configProvider config.Provider - managerProvider cmdManager.Provider - namespace string - podName string -} - -func NewWebhookBuilder() Builder { - return Builder{} -} - -func (builder Builder) SetConfigProvider(provider config.Provider) Builder { - builder.configProvider = provider - return builder -} - -func (builder Builder) SetManagerProvider(provider cmdManager.Provider) Builder { - builder.managerProvider = provider - return builder -} - -func (builder Builder) SetNamespace(namespace string) Builder { - builder.namespace = namespace - return builder -} - -func (builder Builder) SetPodName(podName string) Builder { - builder.podName = podName - return builder -} - -func (builder Builder) GetManagerProvider() cmdManager.Provider { - if builder.managerProvider == nil { - builder.managerProvider = NewWebhookManagerProvider(certificateDirectory, certificateKeyFileName, certificateFileName) - } - - return builder.managerProvider -} - -func (builder Builder) Run(webhookManager manager.Manager) error { - - addFlags() - builder.GetManagerProvider().SetupWebhookServer(webhookManager) - - certificates. - NewCertificateWatcher(webhookManager.GetAPIReader(), webhookManager.GetWebhookServer().CertDir, builder.namespace, webhooks.SecretCertsName, ctrl.Log.WithName("Webhook Cert Manager")). - WaitForCertificates() - - webhookManager.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{ - Handler: &pod_mutator.PodMutatingWebhook{ - Client: webhookManager.GetClient(), - Tracer: otel.Tracer("keptn/webhook"), - Recorder: webhookManager.GetEventRecorderFor("keptn/webhook"), - Log: ctrl.Log.WithName("Mutating Webhook"), - }}) - - signalHandler := ctrl.SetupSignalHandler() - err := webhookManager.Start(signalHandler) - return errors.WithStack(err) -} - -func addFlags() { - flag.StringVar(&certificateDirectory, FlagCertificateDirectory, "/tmp/webhook/certs", "Directory to look certificates for.") - flag.StringVar(&certificateFileName, FlagCertificateFileName, "tls.crt", "File name for the public certificate.") - flag.StringVar(&certificateKeyFileName, FlagCertificateKeyFileName, "tls.key", "File name for the private key.") - flag.Parse() -} diff --git a/operator/cmd/webhook/builder_test.go b/operator/cmd/webhook/builder_test.go deleted file mode 100644 index 1e5118573a..0000000000 --- a/operator/cmd/webhook/builder_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package webhook - -import ( - "testing" - - "github.com/keptn/lifecycle-toolkit/operator/cmd/fake" - "github.com/stretchr/testify/assert" -) - -func TestWebhookCommandBuilder(t *testing.T) { - - t.Run("set config provider", func(t *testing.T) { - builder := NewWebhookBuilder() - - assert.NotNil(t, builder) - - expectedProvider := &fake.MockProvider{} - builder = builder.SetConfigProvider(expectedProvider) - - assert.Equal(t, expectedProvider, builder.configProvider) - }) - t.Run("set manager provider", func(t *testing.T) { - expectedProvider := &fake.MockWebhookManager{} - builder := NewWebhookBuilder().SetManagerProvider(expectedProvider) - - assert.Equal(t, expectedProvider, builder.managerProvider) - }) - t.Run("set namespace", func(t *testing.T) { - builder := NewWebhookBuilder().SetNamespace("namespace") - - assert.Equal(t, "namespace", builder.namespace) - }) -} diff --git a/operator/cmd/webhook/manager.go b/operator/cmd/webhook/manager.go deleted file mode 100644 index 0e0fedccc6..0000000000 --- a/operator/cmd/webhook/manager.go +++ /dev/null @@ -1,43 +0,0 @@ -package webhook - -import ( - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -const ( - metricsBindAddress = ":8383" - port = 8443 -) - -type WebhookProvider struct { - certificateDirectory string - certificateFileName string - keyFileName string -} - -func NewWebhookManagerProvider(certificateDirectory string, keyFileName string, certificateFileName string) WebhookProvider { - return WebhookProvider{ - certificateDirectory: certificateDirectory, - certificateFileName: certificateFileName, - keyFileName: keyFileName, - } -} - -func (provider WebhookProvider) createOptions(scheme *runtime.Scheme, namespace string) ctrl.Options { - return ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsBindAddress, - Port: port, - Namespace: namespace, - } -} - -func (provider WebhookProvider) SetupWebhookServer(mgr manager.Manager) { - webhookServer := mgr.GetWebhookServer() - webhookServer.CertDir = provider.certificateDirectory - webhookServer.KeyName = provider.keyFileName - webhookServer.CertName = provider.certificateFileName - -} diff --git a/operator/cmd/webhook/manager_test.go b/operator/cmd/webhook/manager_test.go deleted file mode 100644 index 3e0913648e..0000000000 --- a/operator/cmd/webhook/manager_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package webhook - -import ( - "testing" - - "github.com/keptn/lifecycle-toolkit/operator/cmd/fake" - cmdManager "github.com/keptn/lifecycle-toolkit/operator/cmd/manager" - "github.com/stretchr/testify/assert" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/webhook" -) - -func TestCreateOptions(t *testing.T) { - - t.Run("implements interface", func(t *testing.T) { - var provider cmdManager.Provider = NewWebhookManagerProvider("certs-dir", "key-file", "cert-file") - - providerImpl := provider.(WebhookProvider) - assert.Equal(t, "certs-dir", providerImpl.certificateDirectory) - assert.Equal(t, "key-file", providerImpl.keyFileName) - assert.Equal(t, "cert-file", providerImpl.certificateFileName) - }) - t.Run("creates options", func(t *testing.T) { - provider := WebhookProvider{} - options := provider.createOptions(scheme.Scheme, "test-namespace") - - assert.NotNil(t, options) - assert.Equal(t, "test-namespace", options.Namespace) - assert.Equal(t, scheme.Scheme, options.Scheme) - assert.Equal(t, metricsBindAddress, options.MetricsBindAddress) - assert.Equal(t, port, options.Port) - }) - t.Run("configures webhooks server", func(t *testing.T) { - provider := NewWebhookManagerProvider("certs-dir", "key-file", "cert-file") - expectedWebhookServer := &webhook.Server{} - - mgr := &fake.MockManager{ - GetWebhookServerFunc: func() *webhook.Server { - return expectedWebhookServer - }, - } - - provider.SetupWebhookServer(mgr) - - assert.Equal(t, "certs-dir", expectedWebhookServer.CertDir) - assert.Equal(t, "key-file", expectedWebhookServer.KeyName) - assert.Equal(t, "cert-file", expectedWebhookServer.CertName) - - mgrWebhookServer := mgr.GetWebhookServer() - assert.Equal(t, "certs-dir", mgrWebhookServer.CertDir) - assert.Equal(t, "key-file", mgrWebhookServer.KeyName) - assert.Equal(t, "cert-file", mgrWebhookServer.CertName) - }) -} diff --git a/operator/common/common.go b/operator/common/common.go new file mode 100644 index 0000000000..1dde0f56e0 --- /dev/null +++ b/operator/common/common.go @@ -0,0 +1,48 @@ +package operatorcommon + +import "strings" + +// CreateResourceName is a function that concatenates the parts from the +// input and checks, if the resulting string matches the maxLen condition. +// If it does not match, it reduces the subparts, starting with the first +// one (but leaving its length at least in minSubstrLen so it's not deleted +// completely) adn continuing with the rest if needed. +// Let's take WorkloadInstance as an example (3 parts: app, workload, version). +// First the app name is reduced if needed (only to minSubstrLen), +// afterwards workload and the version is not reduced at all. This pattern is +// chosen to not reduce only one part of the name (that can be completely gone +// afterwards), but to include all of the parts in the resulting string. +func CreateResourceName(maxLen int, minSubstrLen int, str ...string) string { + // if the minSubstrLen is too long for the number of parts, + // needs to be reduced + for len(str)*minSubstrLen > maxLen { + minSubstrLen = minSubstrLen / 2 + } + // looping through the subparts and checking if the resulting string + // matches the maxLen condition + for i := 0; i < len(str)-1; i++ { + newStr := strings.Join(str, "-") + if len(newStr) > maxLen { + // if the part is already smaller than the minSubstrLen, + // this part cannot be reduced anymore, so we continue + if len(str[i]) <= minSubstrLen { + continue + } + // part needs to be reduced + cut := len(newStr) - maxLen + // if the needed reduction is bigger than the allowed + // reduction on the part, it's reduced to the minimum + if cut > len(str[i])-minSubstrLen { + str[i] = str[i][:minSubstrLen] + } else { + // the needed reduction can be completed fully on this + // part, so it's reduced accordingly + str[i] = str[i][:len(str[i])-cut] + } + } else { + return strings.ToLower(newStr) + } + } + + return strings.ToLower(strings.Join(str, "-")) +} diff --git a/operator/common/common_test.go b/operator/common/common_test.go new file mode 100644 index 0000000000..7b92bbd935 --- /dev/null +++ b/operator/common/common_test.go @@ -0,0 +1,100 @@ +package operatorcommon + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_CreateResourceName(t *testing.T) { + tests := []struct { + Name string + Input []string + Max int + Min int + Want string + }{ + { + Name: "parts not exceeding max, not min", + Input: []string{ + "str1", + "str2", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1-str2-str3", + }, + { + Name: "1 part exceeding max", + Input: []string{ + "str1111111111111111111111", + "str2", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1111111-str2-str3", + }, + { + Name: "2 part exceeding max", + Input: []string{ + "str1", + "str222222222222222222222222", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1-str2222222-str3", + }, + { + Name: "1 and 2 part exceeding max", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 5, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 10, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 20, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 100, + Want: "str111-str22222-str3", + }, + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + require.Equal(t, tt.Want, CreateResourceName(tt.Max, tt.Min, tt.Input...)) + }) + } +} diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml index 3ef83179fc..224bff0075 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappcreationrequests.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnappcreationrequests.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -33,8 +33,7 @@ spec: metadata: type: object spec: - description: KeptnAppCreationRequestSpec defines the desired state of - KeptnAppCreationRequest + description: Spec describes the desired state of the KeptnAppCreationRequest. properties: appName: description: AppName is the name of the KeptnApp the KeptnAppCreationRequest @@ -44,8 +43,7 @@ spec: - appName type: object status: - description: KeptnAppCreationRequestStatus defines the observed state - of KeptnAppCreationRequest + description: Status describes the current state of the KeptnAppCreationRequest. type: object type: object served: true diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnapps.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnapps.yaml index 7385e9ac35..51868e6817 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnapps.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnapps.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnapps.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -163,35 +163,63 @@ spec: metadata: type: object spec: - description: KeptnAppSpec defines the desired state of KeptnApp + description: Spec describes the desired state of the KeptnApp. properties: postDeploymentEvaluations: + description: PostDeploymentEvaluations is a list of all evaluations + to be performed during the post-deployment phase of the KeptnApp. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array postDeploymentTasks: + description: PostDeploymentTasks is a list of all tasks to be performed + during the post-deployment phase of the KeptnApp. The items of this + list refer to the names of KeptnTaskDefinitions located in the same + namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array preDeploymentEvaluations: + description: PreDeploymentEvaluations is a list of all evaluations + to be performed during the pre-deployment phase of the KeptnApp. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array preDeploymentTasks: + description: PreDeploymentTasks is a list of all tasks to be performed + during the pre-deployment phase of the KeptnApp. The items of this + list refer to the names of KeptnTaskDefinitions located in the same + namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array revision: default: 1 + description: Revision can be modified to trigger another deployment + of a KeptnApp of the same version. This can be used for restarting + a KeptnApp which failed to deploy, e.g. due to a failed preDeploymentEvaluation/preDeploymentTask. type: integer version: + description: Version defines the version of the application. For automatically + created KeptnApps, the version is a function of all KeptnWorkloads + that are part of the KeptnApp. type: string workloads: + description: Workloads is a list of all KeptnWorkloads that are part + of the KeptnApp. items: + description: KeptnWorkloadRef refers to a KeptnWorkload that is + part of a KeptnApp properties: name: + description: Name is the name of the KeptnWorkload. type: string version: + description: Version is the version of the KeptnWorkload. type: string required: - name @@ -202,9 +230,11 @@ spec: - version type: object status: - description: KeptnAppStatus defines the observed state of KeptnApp + description: Status describes the current state of the KeptnApp. properties: currentVersion: + description: CurrentVersion indicates the version that is currently + deployed or being reconciled. type: string type: object type: object diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnappversions.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappversions.yaml index 7aade16857..f032b831c6 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnappversions.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnappversions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnappversions.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -525,43 +525,75 @@ spec: metadata: type: object spec: - description: KeptnAppVersionSpec defines the desired state of KeptnAppVersion + description: Spec describes the desired state of the KeptnAppVersion. properties: appName: + description: AppName is the name of the KeptnApp. type: string postDeploymentEvaluations: + description: PostDeploymentEvaluations is a list of all evaluations + to be performed during the post-deployment phase of the KeptnApp. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array postDeploymentTasks: + description: PostDeploymentTasks is a list of all tasks to be performed + during the post-deployment phase of the KeptnApp. The items of this + list refer to the names of KeptnTaskDefinitions located in the same + namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array preDeploymentEvaluations: + description: PreDeploymentEvaluations is a list of all evaluations + to be performed during the pre-deployment phase of the KeptnApp. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array preDeploymentTasks: + description: PreDeploymentTasks is a list of all tasks to be performed + during the pre-deployment phase of the KeptnApp. The items of this + list refer to the names of KeptnTaskDefinitions located in the same + namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array previousVersion: + description: PreviousVersion is the version of the KeptnApp that has + been deployed prior to this version. type: string revision: default: 1 + description: Revision can be modified to trigger another deployment + of a KeptnApp of the same version. This can be used for restarting + a KeptnApp which failed to deploy, e.g. due to a failed preDeploymentEvaluation/preDeploymentTask. type: integer traceId: additionalProperties: type: string + description: TraceId contains the OpenTelemetry trace ID. type: object version: + description: Version defines the version of the application. For automatically + created KeptnApps, the version is a function of all KeptnWorkloads + that are part of the KeptnApp. type: string workloads: + description: Workloads is a list of all KeptnWorkloads that are part + of the KeptnApp. items: + description: KeptnWorkloadRef refers to a KeptnWorkload that is + part of a KeptnApp properties: name: + description: Name is the name of the KeptnWorkload. type: string version: + description: Version is the version of the KeptnWorkload. type: string required: - name @@ -573,11 +605,14 @@ spec: - version type: object status: - description: KeptnAppVersionStatus defines the observed state of KeptnAppVersion + description: Status describes the current state of the KeptnAppVersion. properties: currentPhase: + description: CurrentPhase indicates the current phase of the KeptnAppVersion. type: string endTime: + description: EndTime represents the time at which the deployment of + the KeptnAppVersion finished. format: date-time type: string phaseTraceIDs: @@ -587,23 +622,33 @@ spec: description: MapCarrier is a TextMapCarrier that uses a map held in memory as a storage medium for propagated key-value pairs. type: object + description: PhaseTraceIDs contains the trace IDs of the OpenTelemetry + spans of each phase of the KeptnAppVersion. type: object postDeploymentEvaluationStatus: default: Pending + description: PostDeploymentEvaluationStatus indicates the current + status of the KeptnAppVersion's PostDeploymentEvaluation phase. type: string postDeploymentEvaluationTaskStatus: + description: PostDeploymentEvaluationTaskStatus indicates the current + state of each postDeploymentEvaluation of the KeptnAppVersion. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -613,20 +658,28 @@ spec: type: array postDeploymentStatus: default: Pending + description: PostDeploymentStatus indicates the current status of + the KeptnAppVersion's PostDeployment phase. type: string postDeploymentTaskStatus: + description: PostDeploymentTaskStatus indicates the current state + of each postDeploymentTask of the KeptnAppVersion. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -636,20 +689,28 @@ spec: type: array preDeploymentEvaluationStatus: default: Pending + description: PreDeploymentEvaluationStatus indicates the current status + of the KeptnAppVersion's PreDeploymentEvaluation phase. type: string preDeploymentEvaluationTaskStatus: + description: PreDeploymentEvaluationTaskStatus indicates the current + state of each preDeploymentEvaluation of the KeptnAppVersion. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -659,20 +720,28 @@ spec: type: array preDeploymentStatus: default: Pending + description: PreDeploymentStatus indicates the current status of the + KeptnAppVersion's PreDeployment phase. type: string preDeploymentTaskStatus: + description: PreDeploymentTaskStatus indicates the current state of + each preDeploymentTask of the KeptnAppVersion. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -681,25 +750,37 @@ spec: type: object type: array startTime: + description: StartTime represents the time at which the deployment + of the KeptnAppVersion started. format: date-time type: string status: default: Pending + description: Status represents the overall status of the KeptnAppVersion. type: string workloadOverallStatus: default: Pending + description: WorkloadOverallStatus indicates the current status of + the KeptnAppVersion's Workload deployment phase. type: string workloadStatus: + description: WorkloadStatus contains the current status of each KeptnWorkload + that is part of the KeptnAppVersion. items: properties: status: default: Pending + description: Status indicates the current status of the KeptnWorkload. type: string workload: + description: Workload refers to a KeptnWorkload that is part + of the KeptnAppVersion. properties: name: + description: Name is the name of the KeptnWorkload. type: string version: + description: Version is the version of the KeptnWorkload. type: string required: - name diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationdefinitions.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationdefinitions.yaml index 42a5201fc2..d66fcc87de 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationdefinitions.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationdefinitions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnevaluationdefinitions.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -139,19 +139,29 @@ spec: metadata: type: object spec: - description: KeptnEvaluationDefinitionSpec defines the desired state of - KeptnEvaluationDefinition + description: Spec describes the desired state of the KeptnEvaluationDefinition. properties: objectives: + description: Objectives is a list of objectives that have to be met + for a KeptnEvaluation referencing this KeptnEvaluationDefinition + to be successful. items: properties: evaluationTarget: + description: EvaluationTarget specifies the target value for + the references KeptnMetric. Needs to start with either '<' + or '>', followed by the target value (e.g. '<10'). type: string keptnMetricRef: + description: KeptnMetricRef references the KeptnMetric that + should be evaluated. properties: name: + description: Name is the name of the referenced KeptnMetric. type: string namespace: + description: Namespace is the namespace where the referenced + KeptnMetric is located. type: string required: - name @@ -165,8 +175,7 @@ spec: - objectives type: object status: - description: KeptnEvaluationDefinitionStatus defines the observed state - of KeptnEvaluationDefinition + description: Status describes the current state of the KeptnEvaluationDefinition. type: object type: object served: true diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationproviders.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationproviders.yaml index bde9971adc..973214e834 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationproviders.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluationproviders.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnevaluationproviders.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluations.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluations.yaml index 9a4c08af68..c72dcd6c2e 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluations.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnevaluations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnevaluations.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -268,60 +268,98 @@ spec: metadata: type: object spec: - description: KeptnEvaluationSpec defines the desired state of KeptnEvaluation + description: Spec describes the desired state of the KeptnEvaluation. properties: appName: + description: AppName defines the KeptnApp for which the KeptnEvaluation + is done. type: string appVersion: + description: AppVersion defines the version of the KeptnApp for which + the KeptnEvaluation is done. type: string checkType: + description: Type indicates whether the KeptnEvaluation is part of + the pre- or postDeployment phase. type: string evaluationDefinition: + description: EvaluationDefinition refers to the name of the KeptnEvaluationDefinition + which includes the objectives for the KeptnEvaluation. The KeptnEvaluationDefinition + can be located in the same namespace as the KeptnEvaluation, or + in the KLT namespace. type: string failAction: type: string retries: default: 10 + description: Retries indicates how many times the KeptnEvaluation + can be attempted in the case of an error or missed evaluation objective, + before considering the KeptnEvaluation to be failed. type: integer retryInterval: default: 5s + description: RetryInterval specifies the interval at which the KeptnEvaluation + is retried in the case of an error or a missed objective. pattern: ^0|([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ type: string workload: + description: Workload defines the KeptnWorkload for which the KeptnEvaluation + is done. type: string workloadVersion: + description: WorkloadVersion defines the version of the KeptnWorkload + for which the KeptnEvaluation is done. type: string required: - evaluationDefinition - workloadVersion type: object status: - description: KeptnEvaluationStatus defines the observed state of KeptnEvaluation + description: Status describes the current state of the KeptnEvaluation. properties: endTime: + description: EndTime represents the time at which the KeptnEvaluation + finished. format: date-time type: string evaluationStatus: additionalProperties: properties: message: + description: Message contains additional information about the + evaluation of an objective. This can include explanations + about why an evaluation has failed (e.g. due to a missed objective), + or if there was any error during the evaluation of the objective. type: string status: + description: Status indicates the status of the objective being + evaluated. type: string value: + description: Value represents the value of the KeptnMetric being + evaluated. type: string required: - status - value type: object + description: EvaluationStatus describes the status of each objective + of the KeptnEvaluationDefinition referenced by the KeptnEvaluation. type: object overallStatus: default: Pending + description: OverallStatus describes the overall status of the KeptnEvaluation. + The Overall status is derived from the status of the individual + objectives of the KeptnEvaluationDefinition referenced by the KeptnEvaluation. type: string retryCount: default: 0 + description: RetryCount indicates how many times the KeptnEvaluation + has been attempted already. type: integer startTime: + description: StartTime represents the time at which the KeptnEvaluation + started. format: date-time type: string required: diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml index b9284fe6a6..d88447aeef 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptntaskdefinitions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptntaskdefinitions.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -187,64 +187,1334 @@ spec: metadata: type: object spec: - description: KeptnTaskDefinitionSpec defines the desired state of KeptnTaskDefinition + description: Spec describes the desired state of the KeptnTaskDefinition. properties: + container: + description: Container contains the definition for the container that + is to be used in Job based on the KeptnTaskDefinitions. + properties: + args: + description: 'Arguments to the entrypoint. The container image''s + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. If a variable + cannot be resolved, the reference in the input string will be + unchanged. Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references + will never be expanded, regardless of whether the variable exists + or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. The + container image''s ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the reference + in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: + i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. If + a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single + $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be a + C_IDENTIFIER. All invalid keys will be reported as an event + when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take + precedence. Values defined by an Env with a duplicate key will + take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a set of + ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key + in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take in + response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. Other management + of the container blocks until the hook completes. More info: + https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', + etc) won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range 1 + to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as + a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range 1 + to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. The handler is not called if the container + crashes or exits. The Pod''s termination grace period countdown + begins before the PreStop hook is executed. Regardless of + the outcome of the handler, the container will eventually + terminate within the Pod''s termination grace period (unless + delayed by finalizers). Other management of the container + blocks until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's filesystem. + The command is simply exec'd, it is not run inside + a shell, so traditional shell instructions ('|', + etc) won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is treated + as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range 1 + to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as + a LifecycleHandler and kept for the backward compatibility. + There are no validation of this field and lifecycle + hooks will fail in runtime when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range 1 + to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. Each + container in a pod must have a unique name (DNS_LABEL). Cannot + be updated. + type: string + ports: + description: List of ports to expose from the container. Not specifying + a port here DOES NOT prevent that port from being exposed. Any + port which is listening on the default "0.0.0.0" address inside + a container will be accessible from the network. Modifying this + array with strategic merge patch may corrupt the data. For more + information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a single + container. + properties: + containerPort: + description: Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If specified, + this must be a valid port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod must + have a unique name. Name for the port that can be referred + to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. Container + will be removed from service endpoints if the probe fails. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable. It can only be + set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. More info: + https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. If this probe fails, the Pod will + be restarted, just as if the livenessProbe failed. This can + be used to provide different probe parameters at the beginning + of a Pod''s lifecycle, when it might take a long time to load + data or warm a cache, than during steady-state operation. This + cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is a beta field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer for + stdin in the container runtime. If this is not set, reads from + stdin in the container will always result in EOF. Default is + false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the stdin + channel after it has been opened by a single attach. When stdin + is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container + start, is empty until the first client attaches to stdin, and + then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container + is restarted. If this flag is false, a container processes that + reads from stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the container''s + termination message will be written is mounted into the container''s + filesystem. Message written is intended to be brief final status, + such as an assertion failure message. Will be truncated by the + node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be populated. + File will use the contents of terminationMessagePath to populate + the container status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other way + around. When not set, MountPropagationNone is used. This + field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. Behaves similarly + to SubPath but environment variable references $(VAR_NAME) + are expanded using the container's environment. Defaults + to "" (volume's root). SubPathExpr and SubPath are mutually + exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might be + configured in the container image. Cannot be updated. + type: string + required: + - name + type: object function: + description: Function contains the definition for the function that + is to be executed in KeptnTasks based on the KeptnTaskDefinitions. properties: configMapRef: + description: ConfigMapReference allows to reference a ConfigMap + containing the code of the function. When referencing a ConfigMap, + the code of the function must be available as a value of the + 'code' key of the referenced ConfigMap. properties: name: + description: Name is the name of the referenced ConfigMap. type: string type: object functionRef: + description: FunctionReference allows to reference another KeptnTaskDefinition + which contains the source code of the function to be executes + for KeptnTasks based on this KeptnTaskDefinition. This can be + useful when you have multiple KeptnTaskDefinitions that should + execute the same logic, but each with different parameters. properties: name: + description: Name is the name of the referenced KeptnTaksDefinition. type: string type: object httpRef: + description: HttpReference allows to point to an HTTP URL containing + the code of the function. properties: url: + description: Url is the URL containing the code of the function. type: string type: object inline: + description: Inline allows to specify the code that should be + executed directly in the KeptnTaskDefinition, as a multi-line + string. properties: code: + description: Code contains the code of the function. type: string type: object parameters: + description: Parameters contains parameters that will be passed + to the job that executes the task. properties: map: additionalProperties: type: string + description: Inline contains the parameters that will be made + available to the job executing the KeptnTask via the 'DATA' + environment variable. The 'DATA' environment variable's + content will be a json encoded string containing all properties + of the map provided. type: object type: object secureParameters: + description: SecureParameters contains secure parameters that + will be passed to the job that executes the task. These will + be stored and accessed as secrets in the cluster. properties: secret: + description: Secret contains the parameters that will be made + available to the job executing the KeptnTask via the 'SECRET_DATA' + environment variable. The 'SECRET_DATA' environment variable's + content will the same as value of the 'SECRET_DATA' key + of the referenced secret. type: string type: object type: object retries: default: 10 + description: Retries specifies how many times a job executing the + KeptnTaskDefinition should be restarted in the case of an unsuccessful + attempt. format: int32 type: integer timeout: default: 5m + description: Timeout specifies the maximum time to wait for the task + to be completed successfully. If the task does not complete successfully + within this time frame, it will be considered to be failed. pattern: ^0|([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ type: string type: object status: - description: KeptnTaskDefinitionStatus defines the observed state of KeptnTaskDefinition + description: Status describes the current state of the KeptnTaskDefinition. properties: function: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' + description: Function contains status information of the function + definition for the task. properties: configMap: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed - state of cluster Important: Run "make" to regenerate code after - modifying this file' + description: ConfigMap indicates the ConfigMap in which the function + code is stored. type: string type: object type: object diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml index 423576ce7f..713ce884b1 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptntasks.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptntasks.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -281,27 +281,47 @@ spec: metadata: type: object spec: - description: KeptnTaskSpec defines the desired state of KeptnTask + description: Spec describes the desired state of the KeptnTask. properties: app: + description: AppName defines the KeptnApp for which the KeptnTask + is executed. type: string appVersion: + description: AppVersion defines the version of the KeptnApp for which + the KeptnTask is executed. type: string checkType: + description: Type indicates whether the KeptnTask is part of the pre- + or postDeployment phase. type: string context: + description: Context contains contextual information about the task + execution. properties: appName: + description: AppName the name of the KeptnApp the KeptnTask is + being executed for. type: string appVersion: + description: AppVersion the version of the KeptnApp the KeptnTask + is being executed for. type: string objectType: + description: ObjectType indicates whether the KeptnTask is being + executed for a KeptnApp or KeptnWorkload. type: string taskType: + description: TaskType indicates whether the KeptnTask is part + of the pre- or postDeployment phase. type: string workloadName: + description: WorkloadName the name of the KeptnWorkload the KeptnTask + is being executed for. type: string workloadVersion: + description: WorkloadVersion the version of the KeptnWorkload + the KeptnTask is being executed for. type: string required: - appName @@ -312,30 +332,59 @@ spec: - workloadVersion type: object parameters: + description: Parameters contains parameters that will be passed to + the job that executes the task. properties: map: additionalProperties: type: string + description: Inline contains the parameters that will be made + available to the job executing the KeptnTask via the 'DATA' + environment variable. The 'DATA' environment variable's content + will be a json encoded string containing all properties of the + map provided. type: object type: object retries: default: 10 + description: Retries indicates how many times the KeptnTask can be + attempted in the case of an error before considering the KeptnTask + to be failed. format: int32 type: integer secureParameters: + description: SecureParameters contains secure parameters that will + be passed to the job that executes the task. These will be stored + and accessed as secrets in the cluster. properties: secret: + description: Secret contains the parameters that will be made + available to the job executing the KeptnTask via the 'SECRET_DATA' + environment variable. The 'SECRET_DATA' environment variable's + content will the same as value of the 'SECRET_DATA' key of the + referenced secret. type: string type: object taskDefinition: + description: TaskDefinition refers to the name of the KeptnTaskDefinition + which includes the specification for the task to be performed. The + KeptnTaskDefinition can be located in the same namespace as the + KeptnTask, or in the KLT namespace. type: string timeout: default: 5m + description: Timeout specifies the maximum time to wait for the task + to be completed successfully. If the task does not complete successfully + within this time frame, it will be considered to be failed. pattern: ^0|([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ type: string workload: + description: Workload defines the KeptnWorkload for which the KeptnTask + is executed. type: string workloadVersion: + description: WorkloadVersion defines the version of the KeptnWorkload + for which the KeptnTask is executed. type: string required: - app @@ -346,22 +395,31 @@ spec: - workloadVersion type: object status: - description: KeptnTaskStatus defines the observed state of KeptnTask + description: Status describes the current state of the KeptnTask. properties: endTime: + description: EndTime represents the time at which the KeptnTask finished. format: date-time type: string jobName: + description: JobName is the name of the Job executing the Task. type: string message: + description: Message contains information about unexpected errors + encountered during the execution of the KeptnTask. type: string reason: + description: Reason contains more information about the reason for + the last transition of the Job executing the KeptnTask. type: string startTime: + description: StartTime represents the time at which the KeptnTask + started. format: date-time type: string status: default: Pending + description: Status represents the overall state of the KeptnTask. type: string type: object type: object diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloadinstances.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloadinstances.yaml index bf59f5d604..5bdc6b25d9 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloadinstances.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloadinstances.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnworkloadinstances.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -518,29 +518,53 @@ spec: metadata: type: object spec: - description: KeptnWorkloadInstanceSpec defines the desired state of KeptnWorkloadInstance + description: Spec describes the desired state of the KeptnWorkloadInstance. properties: app: + description: AppName is the name of the KeptnApp containing the KeptnWorkload. type: string postDeploymentEvaluations: + description: PostDeploymentEvaluations is a list of all evaluations + to be performed during the post-deployment phase of the KeptnWorkload. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnWorkload, or in the KLT + namespace. items: type: string type: array postDeploymentTasks: + description: PostDeploymentTasks is a list of all tasks to be performed + during the post-deployment phase of the KeptnWorkload. The items + of this list refer to the names of KeptnTaskDefinitions located + in the same namespace as the KeptnWorkload, or in the KLT namespace. items: type: string type: array preDeploymentEvaluations: + description: PreDeploymentEvaluations is a list of all evaluations + to be performed during the pre-deployment phase of the KeptnWorkload. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnWorkload, or in the KLT + namespace. items: type: string type: array preDeploymentTasks: + description: PreDeploymentTasks is a list of all tasks to be performed + during the pre-deployment phase of the KeptnWorkload. The items + of this list refer to the names of KeptnTaskDefinitions located + in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array previousVersion: + description: PreviousVersion is the version of the KeptnWorkload that + has been deployed prior to this version. type: string resourceReference: + description: ResourceReference is a reference to the Kubernetes resource + (Deployment, DaemonSet, StatefulSet or ReplicaSet) the KeptnWorkload + is representing. properties: kind: type: string @@ -560,10 +584,13 @@ spec: traceId: additionalProperties: type: string + description: TraceId contains the OpenTelemetry trace ID. type: object version: + description: Version defines the version of the KeptnWorkload. type: string workloadName: + description: WorkloadName is the name of the KeptnWorkload. type: string required: - app @@ -572,15 +599,21 @@ spec: - workloadName type: object status: - description: KeptnWorkloadInstanceStatus defines the observed state of - KeptnWorkloadInstance + description: Status describes the current state of the KeptnWorkloadInstance. properties: currentPhase: + description: 'CurrentPhase indicates the current phase of the KeptnWorkloadInstance. + This can be: - PreDeploymentTasks - PreDeploymentEvaluations - Deployment + - PostDeploymentTasks - PostDeploymentEvaluations' type: string deploymentStatus: default: Pending + description: DeploymentStatus indicates the current status of the + KeptnWorkloadInstance's Deployment phase. type: string endTime: + description: EndTime represents the time at which the deployment of + the KeptnWorkloadInstance finished. format: date-time type: string phaseTraceIDs: @@ -590,23 +623,33 @@ spec: description: MapCarrier is a TextMapCarrier that uses a map held in memory as a storage medium for propagated key-value pairs. type: object + description: PhaseTraceIDs contains the trace IDs of the OpenTelemetry + spans of each phase of the KeptnWorkloadInstance type: object postDeploymentEvaluationStatus: default: Pending + description: PostDeploymentEvaluationStatus indicates the current + status of the KeptnWorkloadInstance's PostDeploymentEvaluation phase. type: string postDeploymentEvaluationTaskStatus: + description: PostDeploymentEvaluationTaskStatus indicates the current + state of each postDeploymentEvaluation of the KeptnWorkloadInstance. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -616,20 +659,28 @@ spec: type: array postDeploymentStatus: default: Pending + description: PostDeploymentStatus indicates the current status of + the KeptnWorkloadInstance's PostDeployment phase. type: string postDeploymentTaskStatus: + description: PostDeploymentTaskStatus indicates the current state + of each postDeploymentTask of the KeptnWorkloadInstance. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -639,20 +690,28 @@ spec: type: array preDeploymentEvaluationStatus: default: Pending + description: PreDeploymentEvaluationStatus indicates the current status + of the KeptnWorkloadInstance's PreDeploymentEvaluation phase. type: string preDeploymentEvaluationTaskStatus: + description: PreDeploymentEvaluationTaskStatus indicates the current + state of each preDeploymentEvaluation of the KeptnWorkloadInstance. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -662,20 +721,28 @@ spec: type: array preDeploymentStatus: default: Pending + description: PreDeploymentStatus indicates the current status of the + KeptnWorkloadInstance's PreDeployment phase. type: string preDeploymentTaskStatus: + description: PreDeploymentTaskStatus indicates the current state of + each preDeploymentTask of the KeptnWorkloadInstance. items: properties: definitionName: description: DefinitionName is the name of the EvaluationDefinition/TaskDefiniton type: string endTime: + description: EndTime represents the time at which the Item (Evaluation/Task) + started. format: date-time type: string name: description: Name is the name of the Evaluation/Task type: string startTime: + description: StartTime represents the time at which the Item + (Evaluation/Task) started. format: date-time type: string status: @@ -684,10 +751,13 @@ spec: type: object type: array startTime: + description: StartTime represents the time at which the deployment + of the KeptnWorkloadInstance started. format: date-time type: string status: default: Pending + description: Status represents the overall status of the KeptnWorkloadInstance. type: string type: object type: object diff --git a/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloads.yaml b/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloads.yaml index 1292f9464d..d490b4da9b 100644 --- a/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloads.yaml +++ b/operator/config/crd/bases/lifecycle.keptn.sh_keptnworkloads.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnworkloads.lifecycle.keptn.sh spec: group: lifecycle.keptn.sh @@ -199,27 +199,49 @@ spec: metadata: type: object spec: - description: KeptnWorkloadSpec defines the desired state of KeptnWorkload + description: Spec describes the desired state of the KeptnWorkload. properties: app: + description: AppName is the name of the KeptnApp containing the KeptnWorkload. type: string postDeploymentEvaluations: + description: PostDeploymentEvaluations is a list of all evaluations + to be performed during the post-deployment phase of the KeptnWorkload. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnWorkload, or in the KLT + namespace. items: type: string type: array postDeploymentTasks: + description: PostDeploymentTasks is a list of all tasks to be performed + during the post-deployment phase of the KeptnWorkload. The items + of this list refer to the names of KeptnTaskDefinitions located + in the same namespace as the KeptnWorkload, or in the KLT namespace. items: type: string type: array preDeploymentEvaluations: + description: PreDeploymentEvaluations is a list of all evaluations + to be performed during the pre-deployment phase of the KeptnWorkload. + The items of this list refer to the names of KeptnEvaluationDefinitions + located in the same namespace as the KeptnWorkload, or in the KLT + namespace. items: type: string type: array preDeploymentTasks: + description: PreDeploymentTasks is a list of all tasks to be performed + during the pre-deployment phase of the KeptnWorkload. The items + of this list refer to the names of KeptnTaskDefinitions located + in the same namespace as the KeptnApp, or in the KLT namespace. items: type: string type: array resourceReference: + description: ResourceReference is a reference to the Kubernetes resource + (Deployment, DaemonSet, StatefulSet or ReplicaSet) the KeptnWorkload + is representing. properties: kind: type: string @@ -237,6 +259,7 @@ spec: - uid type: object version: + description: Version defines the version of the KeptnWorkload. type: string required: - app @@ -244,9 +267,11 @@ spec: - version type: object status: - description: KeptnWorkloadStatus defines the observed state of KeptnWorkload + description: Status describes the current state of the KeptnWorkload. properties: currentVersion: + description: CurrentVersion indicates the version that is currently + deployed or being reconciled. type: string type: object type: object diff --git a/operator/config/crd/bases/options.keptn.sh_keptnconfigs.yaml b/operator/config/crd/bases/options.keptn.sh_keptnconfigs.yaml index f6c4bfe925..e2f8357919 100644 --- a/operator/config/crd/bases/options.keptn.sh_keptnconfigs.yaml +++ b/operator/config/crd/bases/options.keptn.sh_keptnconfigs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.4 + controller-gen.kubebuilder.io/version: v0.12.0 name: keptnconfigs.options.keptn.sh spec: group: options.keptn.sh diff --git a/operator/config/manager/manager.yaml b/operator/config/manager/manager.yaml index 1558e8c7a0..1bd9d121e6 100644 --- a/operator/config/manager/manager.yaml +++ b/operator/config/manager/manager.yaml @@ -40,8 +40,6 @@ spec: - /manager args: - webhook-server - # OLM mounts the certificates here, so we reuse it for simplicity - - --certs-dir=/tmp/k8s-webhook-server/serving-certs/ - --leader-elect # Secure port for the metrics adapter - --adapter-port=6443 @@ -68,7 +66,7 @@ spec: fieldRef: fieldPath: metadata.name - name: FUNCTION_RUNNER_IMAGE - value: ghcr.keptn.sh/keptn/functions-runtime:v0.7.1 # x-release-please-version + value: ghcr.io/keptn/functions-runtime:v0.7.1 # x-release-please-version - name: OTEL_COLLECTOR_URL value: otel-collector:4317 - name: KEPTN_APP_CONTROLLER_LOG_LEVEL diff --git a/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition.yaml b/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition.yaml deleted file mode 100644 index 5d10c28fae..0000000000 --- a/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: lifecycle.keptn.sh/v1alpha3 -kind: KeptnTaskDefinition -metadata: - name: keptntaskdefinition-sample -spec: -# TODO(user): Add fields here diff --git a/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_container.yaml b/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_container.yaml new file mode 100644 index 0000000000..a0cbfcffc0 --- /dev/null +++ b/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_container.yaml @@ -0,0 +1,19 @@ +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: keptntaskdefinition-sample-container +spec: + container: + name: testy-test + image: busybox:1.36.0 + resources: + limits: + memory: "200Mi" + command: + - 'echo' + - 'Hello World!' + - '>' + - '/cache/log.txt' + volumeMounts: + - mountPath: /cache + name: logger diff --git a/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_js.yaml b/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_js.yaml new file mode 100644 index 0000000000..83a33dfc3c --- /dev/null +++ b/operator/config/samples/lifecycle_v1alpha3_keptntaskdefinition_js.yaml @@ -0,0 +1,10 @@ +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: keptntaskdefinition-sample-js +spec: + function: + inline: + code: | + console.log('hello'); + retries: 2 diff --git a/operator/controllers/common/fake/fakeclient.go b/operator/controllers/common/fake/fakeclient.go index c627e2ced3..0039bd514b 100644 --- a/operator/controllers/common/fake/fakeclient.go +++ b/operator/controllers/common/fake/fakeclient.go @@ -1,7 +1,7 @@ package fake import ( - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" lfcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" optionsv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/apis/options/v1alpha1" corev1 "k8s.io/api/core/v1" @@ -15,16 +15,14 @@ import ( // NewClient returns a new controller-runtime fake Client configured with the Operator's scheme, and initialized with objs. func NewClient(objs ...client.Object) client.Client { - setupSchemes() + SetupSchemes() return fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(objs...).Build() } -func setupSchemes() { +func SetupSchemes() { utilruntime.Must(clientgoscheme.AddToScheme(scheme.Scheme)) utilruntime.Must(corev1.AddToScheme(scheme.Scheme)) utilruntime.Must(apiv1.AddToScheme(scheme.Scheme)) - // utilruntime.Must(lfcv1alpha1.AddToScheme(scheme.Scheme)) - // utilruntime.Must(lfcv1alpha2.AddToScheme(scheme.Scheme)) utilruntime.Must(lfcv1alpha3.AddToScheme(scheme.Scheme)) utilruntime.Must(optionsv1alpha1.AddToScheme(scheme.Scheme)) utilruntime.Must(metricsapi.AddToScheme(scheme.Scheme)) diff --git a/operator/controllers/common/helperfunctions.go b/operator/controllers/common/helperfunctions.go index bdc6792d89..3419fa6274 100644 --- a/operator/controllers/common/helperfunctions.go +++ b/operator/controllers/common/helperfunctions.go @@ -30,10 +30,6 @@ func GetItemStatus(name string, instanceStatus []klcv1alpha3.ItemStatus) klcv1al } } -func GetAppVersionName(namespace string, appName string, version string) types.NamespacedName { - return types.NamespacedName{Namespace: namespace, Name: appName + "-" + version} -} - // GetOldStatus retrieves the state of the task/evaluation func GetOldStatus(name string, statuses []klcv1alpha3.ItemStatus) apicommon.KeptnState { var oldstatus apicommon.KeptnState diff --git a/operator/controllers/common/helperfunctions_test.go b/operator/controllers/common/helperfunctions_test.go index 469082527d..33126e8810 100644 --- a/operator/controllers/common/helperfunctions_test.go +++ b/operator/controllers/common/helperfunctions_test.go @@ -8,7 +8,6 @@ import ( apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" "github.com/stretchr/testify/require" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -69,58 +68,6 @@ func Test_GetItemStatus(t *testing.T) { } } -func Test_GetAppVersionName(t *testing.T) { - tests := []struct { - namespace string - appName string - version string - want types.NamespacedName - }{ - { - namespace: "namespace", - appName: "name", - version: "version", - want: types.NamespacedName{ - Namespace: "namespace", - Name: "name-version", - }, - }, - { - namespace: "", - appName: "name", - version: "version", - want: types.NamespacedName{ - Namespace: "", - Name: "name-version", - }, - }, - { - namespace: "namespace", - appName: "", - version: "version", - want: types.NamespacedName{ - Namespace: "namespace", - Name: "-version", - }, - }, - { - namespace: "namespace", - appName: "name", - version: "", - want: types.NamespacedName{ - Namespace: "namespace", - Name: "name-", - }, - }, - } - - for _, tt := range tests { - t.Run("", func(t *testing.T) { - require.Equal(t, GetAppVersionName(tt.namespace, tt.appName, tt.version), tt.want) - }) - } -} - func Test_GetOldStatus(t *testing.T) { tests := []struct { statuses []klcv1alpha3.ItemStatus diff --git a/operator/controllers/common/metrics.go b/operator/controllers/common/metrics.go index 4d002a1229..298a4b8f78 100644 --- a/operator/controllers/common/metrics.go +++ b/operator/controllers/common/metrics.go @@ -7,12 +7,11 @@ import ( controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" + "go.opentelemetry.io/otel/metric" "sigs.k8s.io/controller-runtime/pkg/client" ) -func ObserveDeploymentDuration(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge asyncfloat64.Gauge) error { +func ObserveDeploymentDuration(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge metric.Float64ObservableGauge, o metric.Observer) error { err := client.List(ctx, reconcileObjectList) if err != nil { return fmt.Errorf(controllererrors.ErrCannotRetrieveInstancesMsg, err) @@ -27,14 +26,14 @@ func ObserveDeploymentDuration(ctx context.Context, client client.Client, reconc reconcileObject, _ := interfaces.NewMetricsObjectWrapperFromClientObject(ro) if reconcileObject.IsEndTimeSet() { duration := reconcileObject.GetEndTime().Sub(reconcileObject.GetStartTime()) - gauge.Observe(ctx, duration.Seconds(), reconcileObject.GetDurationMetricsAttributes()...) + o.ObserveFloat64(gauge, duration.Seconds(), metric.WithAttributes(reconcileObject.GetDurationMetricsAttributes()...)) } } return nil } -func ObserveDeploymentInterval(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge asyncfloat64.Gauge) error { +func ObserveDeploymentInterval(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge metric.Float64ObservableGauge, o metric.Observer) error { err := client.List(ctx, reconcileObjectList) if err != nil { return fmt.Errorf(controllererrors.ErrCannotRetrieveInstancesMsg, err) @@ -59,7 +58,7 @@ func ObserveDeploymentInterval(ctx context.Context, client client.Client, reconc } previousInterval := reconcileObject.GetEndTime().Sub(predecessor.GetEndTime()) - gauge.Observe(ctx, previousInterval.Seconds(), reconcileObject.GetDurationMetricsAttributes()...) + o.ObserveFloat64(gauge, previousInterval.Seconds(), metric.WithAttributes(reconcileObject.GetDurationMetricsAttributes()...)) } } @@ -84,7 +83,7 @@ func getPredecessor(successor *interfaces.MetricsObjectWrapper, items []client.O return predecessor } -func ObserveActiveInstances(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge asyncint64.Gauge) error { +func ObserveActiveInstances(ctx context.Context, client client.Client, reconcileObjectList client.ObjectList, gauge metric.Int64ObservableGauge, o metric.Observer) error { err := client.List(ctx, reconcileObjectList) if err != nil { return fmt.Errorf(controllererrors.ErrCannotRetrieveInstancesMsg, err) @@ -102,7 +101,7 @@ func ObserveActiveInstances(ctx context.Context, client client.Client, reconcile gaugeValue = int64(1) } - gauge.Observe(ctx, gaugeValue, activeMetricsObject.GetActiveMetricsAttributes()...) + o.ObserveInt64(gauge, gaugeValue, metric.WithAttributes(activeMetricsObject.GetActiveMetricsAttributes()...)) } return nil diff --git a/operator/controllers/common/metrics_test.go b/operator/controllers/common/metrics_test.go index 780fd1174a..4bfcfb46f1 100644 --- a/operator/controllers/common/metrics_test.go +++ b/operator/controllers/common/metrics_test.go @@ -9,8 +9,8 @@ import ( controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" "github.com/stretchr/testify/require" - noop "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" @@ -18,16 +18,14 @@ import ( ) func TestMetrics_ObserveDeploymentDuration(t *testing.T) { - - gauge, err := noop.NewNoopMeter().AsyncFloat64().Gauge("mine") - require.Nil(t, err) + gauge := noop.Float64ObservableGauge{} tests := []struct { name string clientObjects client.ObjectList list client.ObjectList err error - gauge asyncfloat64.Gauge + gauge metric.Float64ObservableGauge }{ { name: "failed to create wrapper", @@ -79,7 +77,7 @@ func TestMetrics_ObserveDeploymentDuration(t *testing.T) { err := lifecyclev1alpha3.AddToScheme(scheme.Scheme) require.Nil(t, err) client := fake.NewClientBuilder().WithLists(tt.clientObjects).Build() - err = ObserveDeploymentDuration(context.TODO(), client, tt.list, gauge) + err = ObserveDeploymentDuration(context.TODO(), client, tt.list, gauge, noop.Observer{}) require.ErrorIs(t, err, tt.err) }) @@ -153,9 +151,9 @@ func TestMetrics_ObserveActiveInstances(t *testing.T) { err := lifecyclev1alpha3.AddToScheme(scheme.Scheme) require.Nil(t, err) client := fake.NewClientBuilder().WithLists(tt.clientObjects).Build() - gauge, err := noop.NewNoopMeter().AsyncInt64().Gauge("mine") + gauge := noop.Int64ObservableGauge{} require.Nil(t, err) - err = ObserveActiveInstances(context.TODO(), client, tt.list, gauge) + err = ObserveActiveInstances(context.TODO(), client, tt.list, gauge, noop.Observer{}) require.ErrorIs(t, err, tt.err) }) @@ -365,15 +363,14 @@ func TestMetrics_ObserveDeploymentInterval(t *testing.T) { }, } - gauge, err := noop.NewNoopMeter().AsyncFloat64().Gauge("mine") - require.Nil(t, err) + gauge := noop.Float64ObservableGauge{} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := lifecyclev1alpha3.AddToScheme(scheme.Scheme) require.Nil(t, err) fakeClient := fake.NewClientBuilder().WithLists(tt.clientObjects).Build() - err = ObserveDeploymentInterval(context.TODO(), fakeClient, tt.list, gauge) + err = ObserveDeploymentInterval(context.TODO(), fakeClient, tt.list, gauge, noop.Observer{}) require.ErrorIs(t, err, tt.err) }) diff --git a/operator/controllers/common/otel_utils.go b/operator/controllers/common/otel_utils.go index fcd5b483dc..0d33e31c6d 100644 --- a/operator/controllers/common/otel_utils.go +++ b/operator/controllers/common/otel_utils.go @@ -9,14 +9,11 @@ import ( lifecyclev1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" - "go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" @@ -158,141 +155,141 @@ func newResource() *resource.Resource { return r } -func SetUpKeptnMeters(meter metric.Meter, mgr client.Client) { - deploymentActiveGauge, err := meter.AsyncInt64().Gauge("keptn.deployment.active", instrument.WithDescription("a gauge keeping track of the currently active Keptn Deployments")) +func SetUpKeptnMeters(meter interfaces.IMeter, mgr client.Client) { + deploymentActiveGauge, err := meter.Int64ObservableGauge("keptn.deployment.active", metric.WithDescription("a gauge keeping track of the currently active Keptn Deployments")) if err != nil { logger.Error(err, "unable to initialize active deployments OTel gauge") } - taskActiveGauge, err := meter.AsyncInt64().Gauge("keptn.task.active", instrument.WithDescription("a simple counter of active Keptn Tasks")) + taskActiveGauge, err := meter.Int64ObservableGauge("keptn.task.active", metric.WithDescription("a simple counter of active Keptn Tasks")) if err != nil { logger.Error(err, "unable to initialize active tasks OTel gauge") } - appActiveGauge, err := meter.AsyncInt64().Gauge("keptn.app.active", instrument.WithDescription("a simple counter of active Keptn Apps")) + appActiveGauge, err := meter.Int64ObservableGauge("keptn.app.active", metric.WithDescription("a simple counter of active Keptn Apps")) if err != nil { logger.Error(err, "unable to initialize active apps OTel gauge") } - evaluationActiveGauge, err := meter.AsyncInt64().Gauge("keptn.evaluation.active", instrument.WithDescription("a simple counter of active Keptn Evaluations")) + evaluationActiveGauge, err := meter.Int64ObservableGauge("keptn.evaluation.active", metric.WithDescription("a simple counter of active Keptn Evaluations")) if err != nil { logger.Error(err, "unable to initialize active evaluations OTel gauge") } - appDeploymentIntervalGauge, err := meter.AsyncFloat64().Gauge("keptn.app.deploymentinterval", instrument.WithDescription("a gauge of the interval between app deployments")) + appDeploymentIntervalGauge, err := meter.Float64ObservableGauge("keptn.app.deploymentinterval", metric.WithDescription("a gauge of the interval between app deployments")) if err != nil { logger.Error(err, "unable to initialize app deployment interval OTel gauge") } - appDeploymentDurationGauge, err := meter.AsyncFloat64().Gauge("keptn.app.deploymentduration", instrument.WithDescription("a gauge of the duration of app deployments")) + appDeploymentDurationGauge, err := meter.Float64ObservableGauge("keptn.app.deploymentduration", metric.WithDescription("a gauge of the duration of app deployments")) if err != nil { logger.Error(err, "unable to initialize app deployment duration OTel gauge") } - workloadDeploymentIntervalGauge, err := meter.AsyncFloat64().Gauge("keptn.deployment.deploymentinterval", instrument.WithDescription("a gauge of the interval between workload deployments")) + workloadDeploymentIntervalGauge, err := meter.Float64ObservableGauge("keptn.deployment.deploymentinterval", metric.WithDescription("a gauge of the interval between workload deployments")) if err != nil { logger.Error(err, "unable to initialize workload deployment interval OTel gauge") } - workloadDeploymentDurationGauge, err := meter.AsyncFloat64().Gauge("keptn.deployment.deploymentduration", instrument.WithDescription("a gauge of the duration of workload deployments")) + workloadDeploymentDurationGauge, err := meter.Float64ObservableGauge("keptn.deployment.deploymentduration", metric.WithDescription("a gauge of the duration of workload deployments")) if err != nil { logger.Error(err, "unable to initialize workload deployment duration OTel gauge") } - err = meter.RegisterCallback( - []instrument.Asynchronous{ - deploymentActiveGauge, - taskActiveGauge, - appActiveGauge, - evaluationActiveGauge, - appDeploymentIntervalGauge, - appDeploymentDurationGauge, - workloadDeploymentIntervalGauge, - workloadDeploymentDurationGauge, + _, err = meter.RegisterCallback( + func(ctx context.Context, o metric.Observer) error { + observeActiveInstances(ctx, mgr, deploymentActiveGauge, appActiveGauge, taskActiveGauge, evaluationActiveGauge, o) + observeDeploymentInterval(ctx, mgr, appDeploymentIntervalGauge, workloadDeploymentIntervalGauge, o) + observeDuration(ctx, mgr, appDeploymentDurationGauge, workloadDeploymentDurationGauge, o) + return nil }, - func(ctx context.Context) { - observeActiveInstances(ctx, mgr, deploymentActiveGauge, appActiveGauge, taskActiveGauge, evaluationActiveGauge) - observeDeploymentInterval(ctx, mgr, appDeploymentIntervalGauge, workloadDeploymentIntervalGauge) - observeDuration(ctx, mgr, appDeploymentDurationGauge, workloadDeploymentDurationGauge) - }) + deploymentActiveGauge, + taskActiveGauge, + appActiveGauge, + evaluationActiveGauge, + appDeploymentIntervalGauge, + appDeploymentDurationGauge, + workloadDeploymentIntervalGauge, + workloadDeploymentDurationGauge, + ) if err != nil { fmt.Println("Failed to register callback") panic(err) } } -func observeDuration(ctx context.Context, mgr client.Client, appDeploymentDurationGauge asyncfloat64.Gauge, workloadDeploymentDurationGauge asyncfloat64.Gauge) { +func observeDuration(ctx context.Context, mgr client.Client, appDeploymentDurationGauge metric.Float64ObservableGauge, workloadDeploymentDurationGauge metric.Float64ObservableGauge, observer metric.Observer) { - err := ObserveDeploymentDuration(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appDeploymentDurationGauge) + err := ObserveDeploymentDuration(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appDeploymentDurationGauge, observer) if err != nil { logger.Error(err, "unable to gather app deployment durations") } - err = ObserveDeploymentDuration(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, workloadDeploymentDurationGauge) + err = ObserveDeploymentDuration(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, workloadDeploymentDurationGauge, observer) if err != nil { logger.Error(err, "unable to gather workload deployment durations") } } -func observeDeploymentInterval(ctx context.Context, mgr client.Client, appDeploymentIntervalGauge asyncfloat64.Gauge, workloadDeploymentIntervalGauge asyncfloat64.Gauge) { - err := ObserveDeploymentInterval(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appDeploymentIntervalGauge) +func observeDeploymentInterval(ctx context.Context, mgr client.Client, appDeploymentIntervalGauge metric.Float64ObservableGauge, workloadDeploymentIntervalGauge metric.Float64ObservableGauge, observer metric.Observer) { + err := ObserveDeploymentInterval(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appDeploymentIntervalGauge, observer) if err != nil { logger.Error(err, "unable to gather app deployment intervals") } - err = ObserveDeploymentInterval(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, workloadDeploymentIntervalGauge) + err = ObserveDeploymentInterval(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, workloadDeploymentIntervalGauge, observer) if err != nil { logger.Error(err, "unable to gather workload deployment intervals") } } -func observeActiveInstances(ctx context.Context, mgr client.Client, deploymentActiveGauge asyncint64.Gauge, appActiveGauge asyncint64.Gauge, taskActiveGauge asyncint64.Gauge, evaluationActiveGauge asyncint64.Gauge) { +func observeActiveInstances(ctx context.Context, mgr client.Client, deploymentActiveGauge metric.Int64ObservableGauge, appActiveGauge metric.Int64ObservableGauge, taskActiveGauge metric.Int64ObservableGauge, evaluationActiveGauge metric.Int64ObservableGauge, observer metric.Observer) { - err := ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, deploymentActiveGauge) + err := ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnWorkloadInstanceList{}, deploymentActiveGauge, observer) if err != nil { logger.Error(err, "unable to gather active deployments") } - err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appActiveGauge) + err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnAppVersionList{}, appActiveGauge, observer) if err != nil { logger.Error(err, "unable to gather active apps") } - err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnTaskList{}, taskActiveGauge) + err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnTaskList{}, taskActiveGauge, observer) if err != nil { logger.Error(err, "unable to gather active tasks") } - err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnEvaluationList{}, evaluationActiveGauge) + err = ObserveActiveInstances(ctx, mgr, &lifecyclev1alpha3.KeptnEvaluationList{}, evaluationActiveGauge, observer) if err != nil { logger.Error(err, "unable to gather active evaluations") } } -func SetUpKeptnTaskMeters(meter metric.Meter) common.KeptnMeters { - deploymentCount, err := meter.SyncInt64().Counter("keptn.deployment.count", instrument.WithDescription("a simple counter for Keptn Deployments")) +func SetUpKeptnTaskMeters(meter interfaces.IMeter) common.KeptnMeters { + deploymentCount, err := meter.Int64Counter("keptn.deployment.count", metric.WithDescription("a simple counter for Keptn Deployments")) if err != nil { logger.Error(err, "unable to initialize deployment count OTel counter") } - deploymentDuration, err := meter.SyncFloat64().Histogram("keptn.deployment.duration", instrument.WithDescription("a histogram of duration for Keptn Deployments"), instrument.WithUnit(unit.Unit("s"))) + deploymentDuration, err := meter.Float64Histogram("keptn.deployment.duration", metric.WithDescription("a histogram of duration for Keptn Deployments"), metric.WithUnit("s")) if err != nil { logger.Error(err, "unable to initialize deployment duration OTel histogram") } - taskCount, err := meter.SyncInt64().Counter("keptn.task.count", instrument.WithDescription("a simple counter for Keptn Tasks")) + taskCount, err := meter.Int64Counter("keptn.task.count", metric.WithDescription("a simple counter for Keptn Tasks")) if err != nil { logger.Error(err, "unable to initialize task OTel counter") } - taskDuration, err := meter.SyncFloat64().Histogram("keptn.task.duration", instrument.WithDescription("a histogram of duration for Keptn Tasks"), instrument.WithUnit(unit.Unit("s"))) + taskDuration, err := meter.Float64Histogram("keptn.task.duration", metric.WithDescription("a histogram of duration for Keptn Tasks"), metric.WithUnit("s")) if err != nil { logger.Error(err, "unable to initialize task duration OTel histogram") } - appCount, err := meter.SyncInt64().Counter("keptn.app.count", instrument.WithDescription("a simple counter for Keptn Apps")) + appCount, err := meter.Int64Counter("keptn.app.count", metric.WithDescription("a simple counter for Keptn Apps")) if err != nil { logger.Error(err, "unable to initialize app OTel counter") } - appDuration, err := meter.SyncFloat64().Histogram("keptn.app.duration", instrument.WithDescription("a histogram of duration for Keptn Apps"), instrument.WithUnit(unit.Unit("s"))) + appDuration, err := meter.Float64Histogram("keptn.app.duration", metric.WithDescription("a histogram of duration for Keptn Apps"), metric.WithUnit("s")) if err != nil { logger.Error(err, "unable to initialize app duration OTel histogram") } - evaluationCount, err := meter.SyncInt64().Counter("keptn.evaluation.count", instrument.WithDescription("a simple counter for Keptn Evaluations")) + evaluationCount, err := meter.Int64Counter("keptn.evaluation.count", metric.WithDescription("a simple counter for Keptn Evaluations")) if err != nil { logger.Error(err, "unable to initialize evaluation OTel counter") } - evaluationDuration, err := meter.SyncFloat64().Histogram("keptn.evaluation.duration", instrument.WithDescription("a histogram of duration for Keptn Evaluations"), instrument.WithUnit(unit.Unit("s"))) + evaluationDuration, err := meter.Float64Histogram("keptn.evaluation.duration", metric.WithDescription("a histogram of duration for Keptn Evaluations"), metric.WithUnit("s")) if err != nil { logger.Error(err, "unable to initialize evaluation duration OTel histogram") } diff --git a/operator/controllers/common/otel_utils_test.go b/operator/controllers/common/otel_utils_test.go index 49a68296bb..aee0b318ad 100644 --- a/operator/controllers/common/otel_utils_test.go +++ b/operator/controllers/common/otel_utils_test.go @@ -1,21 +1,15 @@ package common import ( - "context" "net" "testing" + "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces/fake" - fakeasync "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces/fake/async" - fakesync "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/interfaces/fake/sync" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" + "go.opentelemetry.io/otel/metric/noop" "google.golang.org/grpc" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -85,31 +79,20 @@ func TestGetOTelTracerProviderOptions(t *testing.T) { } func TestSetUpKeptnMeters(t *testing.T) { - fakeAsyncIntTracerProvider := &fakeasync.ITracerProviderAsyncInt64Mock{ - GaugeFunc: func(name string, opts ...instrument.Option) (asyncint64.Gauge, error) { + fakeMeter := &fake.IMeterMock{ + Int64ObservableGaugeFunc: func(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { return nil, errors.New("some error") }, - } - fakeAsyncFloatTracerProvider := &fakeasync.ITracerProviderAsyncFloat64Mock{ - GaugeFunc: func(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) { + Float64ObservableGaugeFunc: func(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { return nil, errors.New("some error") }, - } - - fakeMeter := &fake.IMeterMock{ - AsyncInt64Func: func() asyncint64.InstrumentProvider { - return fakeAsyncIntTracerProvider - }, - AsyncFloat64Func: func() asyncfloat64.InstrumentProvider { - return fakeAsyncFloatTracerProvider - }, - RegisterCallbackFunc: func(insts []instrument.Asynchronous, function func(context.Context)) error { - return nil + RegisterCallbackFunc: func(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { + return nil, nil }, } type args struct { - meter metric.Meter + meter interfaces.IMeter mgr client.Client } tests := []struct { @@ -120,7 +103,7 @@ func TestSetUpKeptnMeters(t *testing.T) { { name: "Basic case", args: args{ - meter: metric.NewNoopMeter(), + meter: noop.NewMeterProvider().Meter(("test")), mgr: nil, }, wantRegisterCalls: 0, @@ -143,17 +126,6 @@ func TestSetUpKeptnMeters(t *testing.T) { } func TestSetUpKeptnMetersError(t *testing.T) { - fakeAsyncIntTracerProvider := &fakeasync.ITracerProviderAsyncInt64Mock{ - GaugeFunc: func(name string, opts ...instrument.Option) (asyncint64.Gauge, error) { - return nil, errors.New("some error") - }, - } - fakeAsyncFloatTracerProvider := &fakeasync.ITracerProviderAsyncFloat64Mock{ - GaugeFunc: func(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) { - return nil, errors.New("some error") - }, - } - defer func() { if r := recover(); r == nil { t.Errorf("The code did not panic") @@ -161,14 +133,14 @@ func TestSetUpKeptnMetersError(t *testing.T) { }() errorFakeMeter := &fake.IMeterMock{ - AsyncInt64Func: func() asyncint64.InstrumentProvider { - return fakeAsyncIntTracerProvider + Int64ObservableGaugeFunc: func(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { + return nil, errors.New("some error") }, - AsyncFloat64Func: func() asyncfloat64.InstrumentProvider { - return fakeAsyncFloatTracerProvider + Float64ObservableGaugeFunc: func(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { + return nil, errors.New("some error") }, - RegisterCallbackFunc: func(insts []instrument.Asynchronous, function func(context.Context)) error { - return errors.New("some error") + RegisterCallbackFunc: func(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { + return nil, errors.New("some error") }, } @@ -176,9 +148,7 @@ func TestSetUpKeptnMetersError(t *testing.T) { } func TestSetUpKeptnTaskMeters(t *testing.T) { - noopMeter := metric.NewNoopMeter() - - got := SetUpKeptnTaskMeters(noopMeter) + got := SetUpKeptnTaskMeters(noop.NewMeterProvider().Meter(("test"))) require.NotNil(t, got.TaskCount) require.NotNil(t, got.TaskDuration) @@ -191,32 +161,21 @@ func TestSetUpKeptnTaskMeters(t *testing.T) { } func TestSetUpKeptnTaskMeters_ErrorCase(t *testing.T) { - fakeSyncIntTracerProvider := &fakesync.ITracerProviderSyncInt64Mock{ - CounterFunc: func(name string, opts ...instrument.Option) (syncint64.Counter, error) { + errorFakeMeter := &fake.IMeterMock{ + Int64CounterFunc: func(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) { return nil, errors.New("some error") }, - HistogramFunc: func(name string, opts ...instrument.Option) (syncint64.Histogram, error) { + Int64HistogramFunc: func(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) { return nil, errors.New("some error") }, - } - fakeSyncFloatTracerProvider := &fakesync.ITracerProviderSyncFloat64Mock{ - CounterFunc: func(name string, opts ...instrument.Option) (syncfloat64.Counter, error) { + Float64CounterFunc: func(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) { return nil, errors.New("some error") }, - HistogramFunc: func(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) { + Float64HistogramFunc: func(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) { return nil, errors.New("some error") }, - } - - errorFakeMeter := &fake.IMeterMock{ - SyncInt64Func: func() syncint64.InstrumentProvider { - return fakeSyncIntTracerProvider - }, - SyncFloat64Func: func() syncfloat64.InstrumentProvider { - return fakeSyncFloatTracerProvider - }, - RegisterCallbackFunc: func(insts []instrument.Asynchronous, function func(context.Context)) error { - return errors.New("some error") + RegisterCallbackFunc: func(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { + return nil, errors.New("some error") }, } diff --git a/operator/controllers/common/providers/keptnmetric/keptnmetric.go b/operator/controllers/common/providers/keptnmetric/keptnmetric.go index fd08879a7b..7cc6ba799d 100644 --- a/operator/controllers/common/providers/keptnmetric/keptnmetric.go +++ b/operator/controllers/common/providers/keptnmetric/keptnmetric.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/go-logr/logr" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" "github.com/keptn/lifecycle-toolkit/operator/controllers/common" "k8s.io/apimachinery/pkg/types" diff --git a/operator/controllers/common/providers/keptnmetric/keptnmetric_test.go b/operator/controllers/common/providers/keptnmetric/keptnmetric_test.go index e05cb5919a..beed84da0e 100644 --- a/operator/controllers/common/providers/keptnmetric/keptnmetric_test.go +++ b/operator/controllers/common/providers/keptnmetric/keptnmetric_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" "github.com/keptn/lifecycle-toolkit/operator/controllers/common" "github.com/stretchr/testify/require" diff --git a/operator/controllers/common/test_utils.go b/operator/controllers/common/test_utils.go index 1ef07717be..6ec5dfd62c 100644 --- a/operator/controllers/common/test_utils.go +++ b/operator/controllers/common/test_utils.go @@ -6,9 +6,8 @@ import ( lfcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/unit" - "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -66,12 +65,12 @@ func AddAppVersion(c client.Client, namespace string, appName string, version st } func InitAppMeters() apicommon.KeptnMeters { - provider := metric.NewMeterProvider() + provider := sdkmetric.NewMeterProvider() meter := provider.Meter("keptn/task") - appCount, _ := meter.SyncInt64().Counter("keptn.app.count", instrument.WithDescription("a simple counter for Keptn Apps")) - appDuration, _ := meter.SyncFloat64().Histogram("keptn.app.duration", instrument.WithDescription("a histogram of duration for Keptn Apps"), instrument.WithUnit("s")) - deploymentCount, _ := meter.SyncInt64().Counter("keptn.deployment.count", instrument.WithDescription("a simple counter for Keptn Deployments")) - deploymentDuration, _ := meter.SyncFloat64().Histogram("keptn.deployment.duration", instrument.WithDescription("a histogram of duration for Keptn Deployments"), instrument.WithUnit(unit.Unit("s"))) + appCount, _ := meter.Int64Counter("keptn.app.count", metric.WithDescription("a simple counter for Keptn Apps")) + appDuration, _ := meter.Float64Histogram("keptn.app.duration", metric.WithDescription("a histogram of duration for Keptn Apps"), metric.WithUnit("s")) + deploymentCount, _ := meter.Int64Counter("keptn.deployment.count", metric.WithDescription("a simple counter for Keptn Deployments")) + deploymentDuration, _ := meter.Float64Histogram("keptn.deployment.duration", metric.WithDescription("a histogram of duration for Keptn Deployments"), metric.WithUnit("s")) meters := apicommon.KeptnMeters{ AppCount: appCount, diff --git a/operator/controllers/errors/errors.go b/operator/controllers/errors/errors.go index f998f283b9..e7b742515d 100644 --- a/operator/controllers/errors/errors.go +++ b/operator/controllers/errors/errors.go @@ -12,6 +12,7 @@ var ErrRetryCountExceeded = fmt.Errorf("retryCount for evaluation exceeded") var ErrNoValues = fmt.Errorf("no values") var ErrInvalidOperator = fmt.Errorf("invalid operator") var ErrCannotMarshalParams = fmt.Errorf("could not marshal parameters") +var ErrNoTaskDefinitionSpec = fmt.Errorf("the TaskDefinition specs are empty") var ErrUnsupportedWorkloadInstanceResourceReference = fmt.Errorf("unsupported Resource Reference") var ErrCannotGetKeptnTaskDefinition = fmt.Errorf("cannot retrieve KeptnTaskDefinition") @@ -22,7 +23,7 @@ var ErrCannotFetchAppVersionMsg = "could not retrieve KeptnappVersion: %w" var ErrCannotRetrieveWorkloadInstancesMsg = "could not retrieve KeptnWorkloadInstance: %w" var ErrCannotRetrieveWorkloadMsg = "could not retrieve KeptnWorkload: %w" var ErrNoLabelsFoundTask = "no labels found for task: %s" -var ErrNoConfigMapMsg = "No ConfigMap specified or HTTP source specified in TaskDefinition) / Namespace: %s, Name: %s" +var ErrNoConfigMapMsg = "no ConfigMap specified or HTTP source specified in TaskDefinition) / Namespace: %s, Name: %s" var ErrCannotGetFunctionConfigMap = "could not get function configMap: %w" var ErrCannotFetchAppVersionForWorkloadInstanceMsg = "could not fetch AppVersion for KeptnWorkloadInstance: " var ErrCouldNotUnbindSpan = "could not unbind span for %s" diff --git a/operator/controllers/lifecycle/interfaces/fake/activemetricsobject_mock.go b/operator/controllers/lifecycle/interfaces/fake/activemetricsobject_mock.go index 13e60982a7..789e44d8c7 100644 --- a/operator/controllers/lifecycle/interfaces/fake/activemetricsobject_mock.go +++ b/operator/controllers/lifecycle/interfaces/fake/activemetricsobject_mock.go @@ -4,16 +4,15 @@ package fake import ( - "sync" - "go.opentelemetry.io/otel/attribute" + "sync" ) -// ActiveMetricsObjectMock is a mock implementation of common.ActiveMetricsObject. +// ActiveMetricsObjectMock is a mock implementation of interfaces.ActiveMetricsObject. // // func TestSomethingThatUsesActiveMetricsObject(t *testing.T) { // -// // make and configure a mocked common.ActiveMetricsObject +// // make and configure a mocked interfaces.ActiveMetricsObject // mockedActiveMetricsObject := &ActiveMetricsObjectMock{ // GetActiveMetricsAttributesFunc: func() []attribute.KeyValue { // panic("mock out the GetActiveMetricsAttributes method") @@ -23,7 +22,7 @@ import ( // }, // } // -// // use mockedActiveMetricsObject in code that requires common.ActiveMetricsObject +// // use mockedActiveMetricsObject in code that requires interfaces.ActiveMetricsObject // // and then make assertions. // // } diff --git a/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_float_mock.go b/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_float_mock.go deleted file mode 100644 index c9b589c760..0000000000 --- a/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_float_mock.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "sync" - - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" -) - -// ITracerProviderAsyncFloat64Mock is a mock implementation of interfaces.ITracerProviderAsyncFloat64. -// -// func TestSomethingThatUsesITracerProviderAsyncFloat64(t *testing.T) { -// -// // make and configure a mocked interfaces.ITracerProviderAsyncFloat64 -// mockedITracerProviderAsyncFloat64 := &ITracerProviderAsyncFloat64Mock{ -// CounterFunc: func(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) { -// panic("mock out the Counter method") -// }, -// GaugeFunc: func(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) { -// panic("mock out the Gauge method") -// }, -// UpDownCounterFunc: func(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) { -// panic("mock out the UpDownCounter method") -// }, -// } -// -// // use mockedITracerProviderAsyncFloat64 in code that requires interfaces.ITracerProviderAsyncFloat64 -// // and then make assertions. -// -// } -type ITracerProviderAsyncFloat64Mock struct { - // CounterFunc mocks the Counter method. - CounterFunc func(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) - - // GaugeFunc mocks the Gauge method. - GaugeFunc func(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) - - // UpDownCounterFunc mocks the UpDownCounter method. - UpDownCounterFunc func(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) - - // calls tracks calls to the methods. - calls struct { - // Counter holds details about calls to the Counter method. - Counter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // Gauge holds details about calls to the Gauge method. - Gauge []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // UpDownCounter holds details about calls to the UpDownCounter method. - UpDownCounter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - } - lockCounter sync.RWMutex - lockGauge sync.RWMutex - lockUpDownCounter sync.RWMutex -} - -// Counter calls CounterFunc. -func (mock *ITracerProviderAsyncFloat64Mock) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) { - if mock.CounterFunc == nil { - panic("ITracerProviderAsyncFloat64Mock.CounterFunc: method is nil but ITracerProviderAsyncFloat64.Counter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockCounter.Lock() - mock.calls.Counter = append(mock.calls.Counter, callInfo) - mock.lockCounter.Unlock() - return mock.CounterFunc(name, opts...) -} - -// CounterCalls gets all the calls that were made to Counter. -// Check the length with: -// -// len(mockedITracerProviderAsyncFloat64.CounterCalls()) -func (mock *ITracerProviderAsyncFloat64Mock) CounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockCounter.RLock() - calls = mock.calls.Counter - mock.lockCounter.RUnlock() - return calls -} - -// Gauge calls GaugeFunc. -func (mock *ITracerProviderAsyncFloat64Mock) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) { - if mock.GaugeFunc == nil { - panic("ITracerProviderAsyncFloat64Mock.GaugeFunc: method is nil but ITracerProviderAsyncFloat64.Gauge was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockGauge.Lock() - mock.calls.Gauge = append(mock.calls.Gauge, callInfo) - mock.lockGauge.Unlock() - return mock.GaugeFunc(name, opts...) -} - -// GaugeCalls gets all the calls that were made to Gauge. -// Check the length with: -// -// len(mockedITracerProviderAsyncFloat64.GaugeCalls()) -func (mock *ITracerProviderAsyncFloat64Mock) GaugeCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockGauge.RLock() - calls = mock.calls.Gauge - mock.lockGauge.RUnlock() - return calls -} - -// UpDownCounter calls UpDownCounterFunc. -func (mock *ITracerProviderAsyncFloat64Mock) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) { - if mock.UpDownCounterFunc == nil { - panic("ITracerProviderAsyncFloat64Mock.UpDownCounterFunc: method is nil but ITracerProviderAsyncFloat64.UpDownCounter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockUpDownCounter.Lock() - mock.calls.UpDownCounter = append(mock.calls.UpDownCounter, callInfo) - mock.lockUpDownCounter.Unlock() - return mock.UpDownCounterFunc(name, opts...) -} - -// UpDownCounterCalls gets all the calls that were made to UpDownCounter. -// Check the length with: -// -// len(mockedITracerProviderAsyncFloat64.UpDownCounterCalls()) -func (mock *ITracerProviderAsyncFloat64Mock) UpDownCounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockUpDownCounter.RLock() - calls = mock.calls.UpDownCounter - mock.lockUpDownCounter.RUnlock() - return calls -} diff --git a/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_int_mock.go b/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_int_mock.go deleted file mode 100644 index 18c03e7259..0000000000 --- a/operator/controllers/lifecycle/interfaces/fake/async/tracer_provider_int_mock.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "sync" - - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" -) - -// ITracerProviderAsyncInt64Mock is a mock implementation of interfaces.ITracerProviderAsyncInt64. -// -// func TestSomethingThatUsesITracerProviderAsyncInt64(t *testing.T) { -// -// // make and configure a mocked interfaces.ITracerProviderAsyncInt64 -// mockedITracerProviderAsyncInt64 := &ITracerProviderAsyncInt64Mock{ -// CounterFunc: func(name string, opts ...instrument.Option) (asyncint64.Counter, error) { -// panic("mock out the Counter method") -// }, -// GaugeFunc: func(name string, opts ...instrument.Option) (asyncint64.Gauge, error) { -// panic("mock out the Gauge method") -// }, -// UpDownCounterFunc: func(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) { -// panic("mock out the UpDownCounter method") -// }, -// } -// -// // use mockedITracerProviderAsyncInt64 in code that requires interfaces.ITracerProviderAsyncInt64 -// // and then make assertions. -// -// } -type ITracerProviderAsyncInt64Mock struct { - // CounterFunc mocks the Counter method. - CounterFunc func(name string, opts ...instrument.Option) (asyncint64.Counter, error) - - // GaugeFunc mocks the Gauge method. - GaugeFunc func(name string, opts ...instrument.Option) (asyncint64.Gauge, error) - - // UpDownCounterFunc mocks the UpDownCounter method. - UpDownCounterFunc func(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) - - // calls tracks calls to the methods. - calls struct { - // Counter holds details about calls to the Counter method. - Counter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // Gauge holds details about calls to the Gauge method. - Gauge []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // UpDownCounter holds details about calls to the UpDownCounter method. - UpDownCounter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - } - lockCounter sync.RWMutex - lockGauge sync.RWMutex - lockUpDownCounter sync.RWMutex -} - -// Counter calls CounterFunc. -func (mock *ITracerProviderAsyncInt64Mock) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) { - if mock.CounterFunc == nil { - panic("ITracerProviderAsyncInt64Mock.CounterFunc: method is nil but ITracerProviderAsyncInt64.Counter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockCounter.Lock() - mock.calls.Counter = append(mock.calls.Counter, callInfo) - mock.lockCounter.Unlock() - return mock.CounterFunc(name, opts...) -} - -// CounterCalls gets all the calls that were made to Counter. -// Check the length with: -// -// len(mockedITracerProviderAsyncInt64.CounterCalls()) -func (mock *ITracerProviderAsyncInt64Mock) CounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockCounter.RLock() - calls = mock.calls.Counter - mock.lockCounter.RUnlock() - return calls -} - -// Gauge calls GaugeFunc. -func (mock *ITracerProviderAsyncInt64Mock) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) { - if mock.GaugeFunc == nil { - panic("ITracerProviderAsyncInt64Mock.GaugeFunc: method is nil but ITracerProviderAsyncInt64.Gauge was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockGauge.Lock() - mock.calls.Gauge = append(mock.calls.Gauge, callInfo) - mock.lockGauge.Unlock() - return mock.GaugeFunc(name, opts...) -} - -// GaugeCalls gets all the calls that were made to Gauge. -// Check the length with: -// -// len(mockedITracerProviderAsyncInt64.GaugeCalls()) -func (mock *ITracerProviderAsyncInt64Mock) GaugeCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockGauge.RLock() - calls = mock.calls.Gauge - mock.lockGauge.RUnlock() - return calls -} - -// UpDownCounter calls UpDownCounterFunc. -func (mock *ITracerProviderAsyncInt64Mock) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) { - if mock.UpDownCounterFunc == nil { - panic("ITracerProviderAsyncInt64Mock.UpDownCounterFunc: method is nil but ITracerProviderAsyncInt64.UpDownCounter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockUpDownCounter.Lock() - mock.calls.UpDownCounter = append(mock.calls.UpDownCounter, callInfo) - mock.lockUpDownCounter.Unlock() - return mock.UpDownCounterFunc(name, opts...) -} - -// UpDownCounterCalls gets all the calls that were made to UpDownCounter. -// Check the length with: -// -// len(mockedITracerProviderAsyncInt64.UpDownCounterCalls()) -func (mock *ITracerProviderAsyncInt64Mock) UpDownCounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockUpDownCounter.RLock() - calls = mock.calls.UpDownCounter - mock.lockUpDownCounter.RUnlock() - return calls -} diff --git a/operator/controllers/lifecycle/interfaces/fake/meter_mock.go b/operator/controllers/lifecycle/interfaces/fake/meter_mock.go index 9569b04e66..939eb33a1d 100644 --- a/operator/controllers/lifecycle/interfaces/fake/meter_mock.go +++ b/operator/controllers/lifecycle/interfaces/fake/meter_mock.go @@ -4,14 +4,8 @@ package fake import ( - "context" + "go.opentelemetry.io/otel/metric" "sync" - - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" ) // IMeterMock is a mock implementation of interfaces.IMeter. @@ -20,20 +14,26 @@ import ( // // // make and configure a mocked interfaces.IMeter // mockedIMeter := &IMeterMock{ -// AsyncFloat64Func: func() asyncfloat64.InstrumentProvider { -// panic("mock out the AsyncFloat64 method") +// Float64CounterFunc: func(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) { +// panic("mock out the Float64Counter method") // }, -// AsyncInt64Func: func() asyncint64.InstrumentProvider { -// panic("mock out the AsyncInt64 method") +// Float64HistogramFunc: func(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) { +// panic("mock out the Float64Histogram method") // }, -// RegisterCallbackFunc: func(insts []instrument.Asynchronous, function func(context.Context)) error { -// panic("mock out the RegisterCallback method") +// Float64ObservableGaugeFunc: func(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { +// panic("mock out the Float64ObservableGauge method") +// }, +// Int64CounterFunc: func(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) { +// panic("mock out the Int64Counter method") // }, -// SyncFloat64Func: func() syncfloat64.InstrumentProvider { -// panic("mock out the SyncFloat64 method") +// Int64HistogramFunc: func(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) { +// panic("mock out the Int64Histogram method") // }, -// SyncInt64Func: func() syncint64.InstrumentProvider { -// panic("mock out the SyncInt64 method") +// Int64ObservableGaugeFunc: func(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { +// panic("mock out the Int64ObservableGauge method") +// }, +// RegisterCallbackFunc: func(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { +// panic("mock out the RegisterCallback method") // }, // } // @@ -42,190 +42,336 @@ import ( // // } type IMeterMock struct { - // AsyncFloat64Func mocks the AsyncFloat64 method. - AsyncFloat64Func func() asyncfloat64.InstrumentProvider + // Float64CounterFunc mocks the Float64Counter method. + Float64CounterFunc func(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) - // AsyncInt64Func mocks the AsyncInt64 method. - AsyncInt64Func func() asyncint64.InstrumentProvider + // Float64HistogramFunc mocks the Float64Histogram method. + Float64HistogramFunc func(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) - // RegisterCallbackFunc mocks the RegisterCallback method. - RegisterCallbackFunc func(insts []instrument.Asynchronous, function func(context.Context)) error + // Float64ObservableGaugeFunc mocks the Float64ObservableGauge method. + Float64ObservableGaugeFunc func(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) + + // Int64CounterFunc mocks the Int64Counter method. + Int64CounterFunc func(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) - // SyncFloat64Func mocks the SyncFloat64 method. - SyncFloat64Func func() syncfloat64.InstrumentProvider + // Int64HistogramFunc mocks the Int64Histogram method. + Int64HistogramFunc func(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) - // SyncInt64Func mocks the SyncInt64 method. - SyncInt64Func func() syncint64.InstrumentProvider + // Int64ObservableGaugeFunc mocks the Int64ObservableGauge method. + Int64ObservableGaugeFunc func(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) + + // RegisterCallbackFunc mocks the RegisterCallback method. + RegisterCallbackFunc func(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) // calls tracks calls to the methods. calls struct { - // AsyncFloat64 holds details about calls to the AsyncFloat64 method. - AsyncFloat64 []struct { + // Float64Counter holds details about calls to the Float64Counter method. + Float64Counter []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Float64CounterOption } - // AsyncInt64 holds details about calls to the AsyncInt64 method. - AsyncInt64 []struct { + // Float64Histogram holds details about calls to the Float64Histogram method. + Float64Histogram []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Float64HistogramOption } - // RegisterCallback holds details about calls to the RegisterCallback method. - RegisterCallback []struct { - // Insts is the insts argument value. - Insts []instrument.Asynchronous - // Function is the function argument value. - Function func(context.Context) + // Float64ObservableGauge holds details about calls to the Float64ObservableGauge method. + Float64ObservableGauge []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Float64ObservableGaugeOption + } + // Int64Counter holds details about calls to the Int64Counter method. + Int64Counter []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Int64CounterOption + } + // Int64Histogram holds details about calls to the Int64Histogram method. + Int64Histogram []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Int64HistogramOption } - // SyncFloat64 holds details about calls to the SyncFloat64 method. - SyncFloat64 []struct { + // Int64ObservableGauge holds details about calls to the Int64ObservableGauge method. + Int64ObservableGauge []struct { + // Name is the name argument value. + Name string + // Options is the options argument value. + Options []metric.Int64ObservableGaugeOption } - // SyncInt64 holds details about calls to the SyncInt64 method. - SyncInt64 []struct { + // RegisterCallback holds details about calls to the RegisterCallback method. + RegisterCallback []struct { + // F is the f argument value. + F metric.Callback + // Instruments is the instruments argument value. + Instruments []metric.Observable } } - lockAsyncFloat64 sync.RWMutex - lockAsyncInt64 sync.RWMutex - lockRegisterCallback sync.RWMutex - lockSyncFloat64 sync.RWMutex - lockSyncInt64 sync.RWMutex + lockFloat64Counter sync.RWMutex + lockFloat64Histogram sync.RWMutex + lockFloat64ObservableGauge sync.RWMutex + lockInt64Counter sync.RWMutex + lockInt64Histogram sync.RWMutex + lockInt64ObservableGauge sync.RWMutex + lockRegisterCallback sync.RWMutex } -// AsyncFloat64 calls AsyncFloat64Func. -func (mock *IMeterMock) AsyncFloat64() asyncfloat64.InstrumentProvider { - if mock.AsyncFloat64Func == nil { - panic("IMeterMock.AsyncFloat64Func: method is nil but IMeter.AsyncFloat64 was just called") +// Float64Counter calls Float64CounterFunc. +func (mock *IMeterMock) Float64Counter(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) { + if mock.Float64CounterFunc == nil { + panic("IMeterMock.Float64CounterFunc: method is nil but IMeter.Float64Counter was just called") } callInfo := struct { - }{} - mock.lockAsyncFloat64.Lock() - mock.calls.AsyncFloat64 = append(mock.calls.AsyncFloat64, callInfo) - mock.lockAsyncFloat64.Unlock() - return mock.AsyncFloat64Func() + Name string + Options []metric.Float64CounterOption + }{ + Name: name, + Options: options, + } + mock.lockFloat64Counter.Lock() + mock.calls.Float64Counter = append(mock.calls.Float64Counter, callInfo) + mock.lockFloat64Counter.Unlock() + return mock.Float64CounterFunc(name, options...) } -// AsyncFloat64Calls gets all the calls that were made to AsyncFloat64. +// Float64CounterCalls gets all the calls that were made to Float64Counter. // Check the length with: // -// len(mockedIMeter.AsyncFloat64Calls()) -func (mock *IMeterMock) AsyncFloat64Calls() []struct { +// len(mockedIMeter.Float64CounterCalls()) +func (mock *IMeterMock) Float64CounterCalls() []struct { + Name string + Options []metric.Float64CounterOption } { var calls []struct { + Name string + Options []metric.Float64CounterOption } - mock.lockAsyncFloat64.RLock() - calls = mock.calls.AsyncFloat64 - mock.lockAsyncFloat64.RUnlock() + mock.lockFloat64Counter.RLock() + calls = mock.calls.Float64Counter + mock.lockFloat64Counter.RUnlock() return calls } -// AsyncInt64 calls AsyncInt64Func. -func (mock *IMeterMock) AsyncInt64() asyncint64.InstrumentProvider { - if mock.AsyncInt64Func == nil { - panic("IMeterMock.AsyncInt64Func: method is nil but IMeter.AsyncInt64 was just called") +// Float64Histogram calls Float64HistogramFunc. +func (mock *IMeterMock) Float64Histogram(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) { + if mock.Float64HistogramFunc == nil { + panic("IMeterMock.Float64HistogramFunc: method is nil but IMeter.Float64Histogram was just called") } callInfo := struct { - }{} - mock.lockAsyncInt64.Lock() - mock.calls.AsyncInt64 = append(mock.calls.AsyncInt64, callInfo) - mock.lockAsyncInt64.Unlock() - return mock.AsyncInt64Func() + Name string + Options []metric.Float64HistogramOption + }{ + Name: name, + Options: options, + } + mock.lockFloat64Histogram.Lock() + mock.calls.Float64Histogram = append(mock.calls.Float64Histogram, callInfo) + mock.lockFloat64Histogram.Unlock() + return mock.Float64HistogramFunc(name, options...) } -// AsyncInt64Calls gets all the calls that were made to AsyncInt64. +// Float64HistogramCalls gets all the calls that were made to Float64Histogram. // Check the length with: // -// len(mockedIMeter.AsyncInt64Calls()) -func (mock *IMeterMock) AsyncInt64Calls() []struct { +// len(mockedIMeter.Float64HistogramCalls()) +func (mock *IMeterMock) Float64HistogramCalls() []struct { + Name string + Options []metric.Float64HistogramOption } { var calls []struct { + Name string + Options []metric.Float64HistogramOption } - mock.lockAsyncInt64.RLock() - calls = mock.calls.AsyncInt64 - mock.lockAsyncInt64.RUnlock() + mock.lockFloat64Histogram.RLock() + calls = mock.calls.Float64Histogram + mock.lockFloat64Histogram.RUnlock() return calls } -// RegisterCallback calls RegisterCallbackFunc. -func (mock *IMeterMock) RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error { - if mock.RegisterCallbackFunc == nil { - panic("IMeterMock.RegisterCallbackFunc: method is nil but IMeter.RegisterCallback was just called") +// Float64ObservableGauge calls Float64ObservableGaugeFunc. +func (mock *IMeterMock) Float64ObservableGauge(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { + if mock.Float64ObservableGaugeFunc == nil { + panic("IMeterMock.Float64ObservableGaugeFunc: method is nil but IMeter.Float64ObservableGauge was just called") } callInfo := struct { - Insts []instrument.Asynchronous - Function func(context.Context) + Name string + Options []metric.Float64ObservableGaugeOption }{ - Insts: insts, - Function: function, + Name: name, + Options: options, } - mock.lockRegisterCallback.Lock() - mock.calls.RegisterCallback = append(mock.calls.RegisterCallback, callInfo) - mock.lockRegisterCallback.Unlock() - return mock.RegisterCallbackFunc(insts, function) + mock.lockFloat64ObservableGauge.Lock() + mock.calls.Float64ObservableGauge = append(mock.calls.Float64ObservableGauge, callInfo) + mock.lockFloat64ObservableGauge.Unlock() + return mock.Float64ObservableGaugeFunc(name, options...) } -// RegisterCallbackCalls gets all the calls that were made to RegisterCallback. +// Float64ObservableGaugeCalls gets all the calls that were made to Float64ObservableGauge. // Check the length with: // -// len(mockedIMeter.RegisterCallbackCalls()) -func (mock *IMeterMock) RegisterCallbackCalls() []struct { - Insts []instrument.Asynchronous - Function func(context.Context) +// len(mockedIMeter.Float64ObservableGaugeCalls()) +func (mock *IMeterMock) Float64ObservableGaugeCalls() []struct { + Name string + Options []metric.Float64ObservableGaugeOption } { var calls []struct { - Insts []instrument.Asynchronous - Function func(context.Context) + Name string + Options []metric.Float64ObservableGaugeOption } - mock.lockRegisterCallback.RLock() - calls = mock.calls.RegisterCallback - mock.lockRegisterCallback.RUnlock() + mock.lockFloat64ObservableGauge.RLock() + calls = mock.calls.Float64ObservableGauge + mock.lockFloat64ObservableGauge.RUnlock() return calls } -// SyncFloat64 calls SyncFloat64Func. -func (mock *IMeterMock) SyncFloat64() syncfloat64.InstrumentProvider { - if mock.SyncFloat64Func == nil { - panic("IMeterMock.SyncFloat64Func: method is nil but IMeter.SyncFloat64 was just called") +// Int64Counter calls Int64CounterFunc. +func (mock *IMeterMock) Int64Counter(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) { + if mock.Int64CounterFunc == nil { + panic("IMeterMock.Int64CounterFunc: method is nil but IMeter.Int64Counter was just called") } callInfo := struct { - }{} - mock.lockSyncFloat64.Lock() - mock.calls.SyncFloat64 = append(mock.calls.SyncFloat64, callInfo) - mock.lockSyncFloat64.Unlock() - return mock.SyncFloat64Func() + Name string + Options []metric.Int64CounterOption + }{ + Name: name, + Options: options, + } + mock.lockInt64Counter.Lock() + mock.calls.Int64Counter = append(mock.calls.Int64Counter, callInfo) + mock.lockInt64Counter.Unlock() + return mock.Int64CounterFunc(name, options...) } -// SyncFloat64Calls gets all the calls that were made to SyncFloat64. +// Int64CounterCalls gets all the calls that were made to Int64Counter. // Check the length with: // -// len(mockedIMeter.SyncFloat64Calls()) -func (mock *IMeterMock) SyncFloat64Calls() []struct { +// len(mockedIMeter.Int64CounterCalls()) +func (mock *IMeterMock) Int64CounterCalls() []struct { + Name string + Options []metric.Int64CounterOption } { var calls []struct { + Name string + Options []metric.Int64CounterOption } - mock.lockSyncFloat64.RLock() - calls = mock.calls.SyncFloat64 - mock.lockSyncFloat64.RUnlock() + mock.lockInt64Counter.RLock() + calls = mock.calls.Int64Counter + mock.lockInt64Counter.RUnlock() return calls } -// SyncInt64 calls SyncInt64Func. -func (mock *IMeterMock) SyncInt64() syncint64.InstrumentProvider { - if mock.SyncInt64Func == nil { - panic("IMeterMock.SyncInt64Func: method is nil but IMeter.SyncInt64 was just called") +// Int64Histogram calls Int64HistogramFunc. +func (mock *IMeterMock) Int64Histogram(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) { + if mock.Int64HistogramFunc == nil { + panic("IMeterMock.Int64HistogramFunc: method is nil but IMeter.Int64Histogram was just called") } callInfo := struct { - }{} - mock.lockSyncInt64.Lock() - mock.calls.SyncInt64 = append(mock.calls.SyncInt64, callInfo) - mock.lockSyncInt64.Unlock() - return mock.SyncInt64Func() + Name string + Options []metric.Int64HistogramOption + }{ + Name: name, + Options: options, + } + mock.lockInt64Histogram.Lock() + mock.calls.Int64Histogram = append(mock.calls.Int64Histogram, callInfo) + mock.lockInt64Histogram.Unlock() + return mock.Int64HistogramFunc(name, options...) } -// SyncInt64Calls gets all the calls that were made to SyncInt64. +// Int64HistogramCalls gets all the calls that were made to Int64Histogram. // Check the length with: // -// len(mockedIMeter.SyncInt64Calls()) -func (mock *IMeterMock) SyncInt64Calls() []struct { +// len(mockedIMeter.Int64HistogramCalls()) +func (mock *IMeterMock) Int64HistogramCalls() []struct { + Name string + Options []metric.Int64HistogramOption } { var calls []struct { + Name string + Options []metric.Int64HistogramOption } - mock.lockSyncInt64.RLock() - calls = mock.calls.SyncInt64 - mock.lockSyncInt64.RUnlock() + mock.lockInt64Histogram.RLock() + calls = mock.calls.Int64Histogram + mock.lockInt64Histogram.RUnlock() + return calls +} + +// Int64ObservableGauge calls Int64ObservableGaugeFunc. +func (mock *IMeterMock) Int64ObservableGauge(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { + if mock.Int64ObservableGaugeFunc == nil { + panic("IMeterMock.Int64ObservableGaugeFunc: method is nil but IMeter.Int64ObservableGauge was just called") + } + callInfo := struct { + Name string + Options []metric.Int64ObservableGaugeOption + }{ + Name: name, + Options: options, + } + mock.lockInt64ObservableGauge.Lock() + mock.calls.Int64ObservableGauge = append(mock.calls.Int64ObservableGauge, callInfo) + mock.lockInt64ObservableGauge.Unlock() + return mock.Int64ObservableGaugeFunc(name, options...) +} + +// Int64ObservableGaugeCalls gets all the calls that were made to Int64ObservableGauge. +// Check the length with: +// +// len(mockedIMeter.Int64ObservableGaugeCalls()) +func (mock *IMeterMock) Int64ObservableGaugeCalls() []struct { + Name string + Options []metric.Int64ObservableGaugeOption +} { + var calls []struct { + Name string + Options []metric.Int64ObservableGaugeOption + } + mock.lockInt64ObservableGauge.RLock() + calls = mock.calls.Int64ObservableGauge + mock.lockInt64ObservableGauge.RUnlock() + return calls +} + +// RegisterCallback calls RegisterCallbackFunc. +func (mock *IMeterMock) RegisterCallback(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) { + if mock.RegisterCallbackFunc == nil { + panic("IMeterMock.RegisterCallbackFunc: method is nil but IMeter.RegisterCallback was just called") + } + callInfo := struct { + F metric.Callback + Instruments []metric.Observable + }{ + F: f, + Instruments: instruments, + } + mock.lockRegisterCallback.Lock() + mock.calls.RegisterCallback = append(mock.calls.RegisterCallback, callInfo) + mock.lockRegisterCallback.Unlock() + return mock.RegisterCallbackFunc(f, instruments...) +} + +// RegisterCallbackCalls gets all the calls that were made to RegisterCallback. +// Check the length with: +// +// len(mockedIMeter.RegisterCallbackCalls()) +func (mock *IMeterMock) RegisterCallbackCalls() []struct { + F metric.Callback + Instruments []metric.Observable +} { + var calls []struct { + F metric.Callback + Instruments []metric.Observable + } + mock.lockRegisterCallback.RLock() + calls = mock.calls.RegisterCallback + mock.lockRegisterCallback.RUnlock() return calls } diff --git a/operator/controllers/lifecycle/interfaces/fake/spanitem_mock.go b/operator/controllers/lifecycle/interfaces/fake/spanitem_mock.go index 0b61718c96..bbff62ef33 100644 --- a/operator/controllers/lifecycle/interfaces/fake/spanitem_mock.go +++ b/operator/controllers/lifecycle/interfaces/fake/spanitem_mock.go @@ -4,17 +4,16 @@ package fake import ( - "sync" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" + "sync" ) -// SpanItemMock is a mock implementation of common.SpanItem. +// SpanItemMock is a mock implementation of interfaces.SpanItem. // // func TestSomethingThatUsesSpanItem(t *testing.T) { // -// // make and configure a mocked common.SpanItem +// // make and configure a mocked interfaces.SpanItem // mockedSpanItem := &SpanItemMock{ // GetSpanKeyFunc: func(phase string) string { // panic("mock out the GetSpanKey method") @@ -30,7 +29,7 @@ import ( // }, // } // -// // use mockedSpanItem in code that requires common.SpanItem +// // use mockedSpanItem in code that requires interfaces.SpanItem // // and then make assertions. // // } diff --git a/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_float_mock.go b/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_float_mock.go deleted file mode 100644 index c3ecb2c0ea..0000000000 --- a/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_float_mock.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "sync" - - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" -) - -// ITracerProviderSyncFloat64Mock is a mock implementation of interfaces.ITracerProviderSyncFloat64. -// -// func TestSomethingThatUsesITracerProviderSyncFloat64(t *testing.T) { -// -// // make and configure a mocked interfaces.ITracerProviderSyncFloat64 -// mockedITracerProviderSyncFloat64 := &ITracerProviderSyncFloat64Mock{ -// CounterFunc: func(name string, opts ...instrument.Option) (syncfloat64.Counter, error) { -// panic("mock out the Counter method") -// }, -// HistogramFunc: func(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) { -// panic("mock out the Histogram method") -// }, -// UpDownCounterFunc: func(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) { -// panic("mock out the UpDownCounter method") -// }, -// } -// -// // use mockedITracerProviderSyncFloat64 in code that requires interfaces.ITracerProviderSyncFloat64 -// // and then make assertions. -// -// } -type ITracerProviderSyncFloat64Mock struct { - // CounterFunc mocks the Counter method. - CounterFunc func(name string, opts ...instrument.Option) (syncfloat64.Counter, error) - - // HistogramFunc mocks the Histogram method. - HistogramFunc func(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) - - // UpDownCounterFunc mocks the UpDownCounter method. - UpDownCounterFunc func(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) - - // calls tracks calls to the methods. - calls struct { - // Counter holds details about calls to the Counter method. - Counter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // Histogram holds details about calls to the Histogram method. - Histogram []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // UpDownCounter holds details about calls to the UpDownCounter method. - UpDownCounter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - } - lockCounter sync.RWMutex - lockHistogram sync.RWMutex - lockUpDownCounter sync.RWMutex -} - -// Counter calls CounterFunc. -func (mock *ITracerProviderSyncFloat64Mock) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) { - if mock.CounterFunc == nil { - panic("ITracerProviderSyncFloat64Mock.CounterFunc: method is nil but ITracerProviderSyncFloat64.Counter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockCounter.Lock() - mock.calls.Counter = append(mock.calls.Counter, callInfo) - mock.lockCounter.Unlock() - return mock.CounterFunc(name, opts...) -} - -// CounterCalls gets all the calls that were made to Counter. -// Check the length with: -// -// len(mockedITracerProviderSyncFloat64.CounterCalls()) -func (mock *ITracerProviderSyncFloat64Mock) CounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockCounter.RLock() - calls = mock.calls.Counter - mock.lockCounter.RUnlock() - return calls -} - -// Histogram calls HistogramFunc. -func (mock *ITracerProviderSyncFloat64Mock) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) { - if mock.HistogramFunc == nil { - panic("ITracerProviderSyncFloat64Mock.HistogramFunc: method is nil but ITracerProviderSyncFloat64.Histogram was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockHistogram.Lock() - mock.calls.Histogram = append(mock.calls.Histogram, callInfo) - mock.lockHistogram.Unlock() - return mock.HistogramFunc(name, opts...) -} - -// HistogramCalls gets all the calls that were made to Histogram. -// Check the length with: -// -// len(mockedITracerProviderSyncFloat64.HistogramCalls()) -func (mock *ITracerProviderSyncFloat64Mock) HistogramCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockHistogram.RLock() - calls = mock.calls.Histogram - mock.lockHistogram.RUnlock() - return calls -} - -// UpDownCounter calls UpDownCounterFunc. -func (mock *ITracerProviderSyncFloat64Mock) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) { - if mock.UpDownCounterFunc == nil { - panic("ITracerProviderSyncFloat64Mock.UpDownCounterFunc: method is nil but ITracerProviderSyncFloat64.UpDownCounter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockUpDownCounter.Lock() - mock.calls.UpDownCounter = append(mock.calls.UpDownCounter, callInfo) - mock.lockUpDownCounter.Unlock() - return mock.UpDownCounterFunc(name, opts...) -} - -// UpDownCounterCalls gets all the calls that were made to UpDownCounter. -// Check the length with: -// -// len(mockedITracerProviderSyncFloat64.UpDownCounterCalls()) -func (mock *ITracerProviderSyncFloat64Mock) UpDownCounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockUpDownCounter.RLock() - calls = mock.calls.UpDownCounter - mock.lockUpDownCounter.RUnlock() - return calls -} diff --git a/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_int_mock.go b/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_int_mock.go deleted file mode 100644 index c24380afa8..0000000000 --- a/operator/controllers/lifecycle/interfaces/fake/sync/tracer_provider_int_mock.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package fake - -import ( - "sync" - - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/instrument/syncint64" -) - -// ITracerProviderSyncInt64Mock is a mock implementation of interfaces.ITracerProviderSyncInt64. -// -// func TestSomethingThatUsesITracerProviderSyncInt64(t *testing.T) { -// -// // make and configure a mocked interfaces.ITracerProviderSyncInt64 -// mockedITracerProviderSyncInt64 := &ITracerProviderSyncInt64Mock{ -// CounterFunc: func(name string, opts ...instrument.Option) (syncint64.Counter, error) { -// panic("mock out the Counter method") -// }, -// HistogramFunc: func(name string, opts ...instrument.Option) (syncint64.Histogram, error) { -// panic("mock out the Histogram method") -// }, -// UpDownCounterFunc: func(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) { -// panic("mock out the UpDownCounter method") -// }, -// } -// -// // use mockedITracerProviderSyncInt64 in code that requires interfaces.ITracerProviderSyncInt64 -// // and then make assertions. -// -// } -type ITracerProviderSyncInt64Mock struct { - // CounterFunc mocks the Counter method. - CounterFunc func(name string, opts ...instrument.Option) (syncint64.Counter, error) - - // HistogramFunc mocks the Histogram method. - HistogramFunc func(name string, opts ...instrument.Option) (syncint64.Histogram, error) - - // UpDownCounterFunc mocks the UpDownCounter method. - UpDownCounterFunc func(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) - - // calls tracks calls to the methods. - calls struct { - // Counter holds details about calls to the Counter method. - Counter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // Histogram holds details about calls to the Histogram method. - Histogram []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - // UpDownCounter holds details about calls to the UpDownCounter method. - UpDownCounter []struct { - // Name is the name argument value. - Name string - // Opts is the opts argument value. - Opts []instrument.Option - } - } - lockCounter sync.RWMutex - lockHistogram sync.RWMutex - lockUpDownCounter sync.RWMutex -} - -// Counter calls CounterFunc. -func (mock *ITracerProviderSyncInt64Mock) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) { - if mock.CounterFunc == nil { - panic("ITracerProviderSyncInt64Mock.CounterFunc: method is nil but ITracerProviderSyncInt64.Counter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockCounter.Lock() - mock.calls.Counter = append(mock.calls.Counter, callInfo) - mock.lockCounter.Unlock() - return mock.CounterFunc(name, opts...) -} - -// CounterCalls gets all the calls that were made to Counter. -// Check the length with: -// -// len(mockedITracerProviderSyncInt64.CounterCalls()) -func (mock *ITracerProviderSyncInt64Mock) CounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockCounter.RLock() - calls = mock.calls.Counter - mock.lockCounter.RUnlock() - return calls -} - -// Histogram calls HistogramFunc. -func (mock *ITracerProviderSyncInt64Mock) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) { - if mock.HistogramFunc == nil { - panic("ITracerProviderSyncInt64Mock.HistogramFunc: method is nil but ITracerProviderSyncInt64.Histogram was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockHistogram.Lock() - mock.calls.Histogram = append(mock.calls.Histogram, callInfo) - mock.lockHistogram.Unlock() - return mock.HistogramFunc(name, opts...) -} - -// HistogramCalls gets all the calls that were made to Histogram. -// Check the length with: -// -// len(mockedITracerProviderSyncInt64.HistogramCalls()) -func (mock *ITracerProviderSyncInt64Mock) HistogramCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockHistogram.RLock() - calls = mock.calls.Histogram - mock.lockHistogram.RUnlock() - return calls -} - -// UpDownCounter calls UpDownCounterFunc. -func (mock *ITracerProviderSyncInt64Mock) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) { - if mock.UpDownCounterFunc == nil { - panic("ITracerProviderSyncInt64Mock.UpDownCounterFunc: method is nil but ITracerProviderSyncInt64.UpDownCounter was just called") - } - callInfo := struct { - Name string - Opts []instrument.Option - }{ - Name: name, - Opts: opts, - } - mock.lockUpDownCounter.Lock() - mock.calls.UpDownCounter = append(mock.calls.UpDownCounter, callInfo) - mock.lockUpDownCounter.Unlock() - return mock.UpDownCounterFunc(name, opts...) -} - -// UpDownCounterCalls gets all the calls that were made to UpDownCounter. -// Check the length with: -// -// len(mockedITracerProviderSyncInt64.UpDownCounterCalls()) -func (mock *ITracerProviderSyncInt64Mock) UpDownCounterCalls() []struct { - Name string - Opts []instrument.Option -} { - var calls []struct { - Name string - Opts []instrument.Option - } - mock.lockUpDownCounter.RLock() - calls = mock.calls.UpDownCounter - mock.lockUpDownCounter.RUnlock() - return calls -} diff --git a/operator/controllers/lifecycle/interfaces/meter.go b/operator/controllers/lifecycle/interfaces/meter.go index bca8a83d40..9d136d144d 100644 --- a/operator/controllers/lifecycle/interfaces/meter.go +++ b/operator/controllers/lifecycle/interfaces/meter.go @@ -2,23 +2,15 @@ package interfaces import ( "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" - "go.opentelemetry.io/otel/metric/instrument/asyncint64" - "go.opentelemetry.io/otel/metric/instrument/syncfloat64" - "go.opentelemetry.io/otel/metric/instrument/syncint64" ) //go:generate moq -pkg fake -skip-ensure -out ./fake/meter_mock.go . IMeter -type IMeter = metric.Meter - -//go:generate moq -pkg fake -skip-ensure -out ./fake/async/tracer_provider_int_mock.go . ITracerProviderAsyncInt64 -type ITracerProviderAsyncInt64 = asyncint64.InstrumentProvider - -//go:generate moq -pkg fake -skip-ensure -out ./fake/async/tracer_provider_float_mock.go . ITracerProviderAsyncFloat64 -type ITracerProviderAsyncFloat64 = asyncfloat64.InstrumentProvider - -//go:generate moq -pkg fake -skip-ensure -out ./fake/sync/tracer_provider_int_mock.go . ITracerProviderSyncInt64 -type ITracerProviderSyncInt64 = syncint64.InstrumentProvider - -//go:generate moq -pkg fake -skip-ensure -out ./fake/sync/tracer_provider_float_mock.go . ITracerProviderSyncFloat64 -type ITracerProviderSyncFloat64 = syncfloat64.InstrumentProvider +type IMeter interface { + Int64Counter(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) + Int64Histogram(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) + Float64Counter(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) + Float64Histogram(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) + RegisterCallback(f metric.Callback, instruments ...metric.Observable) (metric.Registration, error) + Int64ObservableGauge(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) + Float64ObservableGauge(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) +} diff --git a/operator/controllers/lifecycle/keptnapp/controller.go b/operator/controllers/lifecycle/keptnapp/controller.go index cfc61323fd..4f85e9a75c 100644 --- a/operator/controllers/lifecycle/keptnapp/controller.go +++ b/operator/controllers/lifecycle/keptnapp/controller.go @@ -23,6 +23,7 @@ import ( "github.com/go-logr/logr" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" @@ -185,9 +186,10 @@ func (r *KeptnAppReconciler) deprecateAppVersions(ctx context.Context, app *klcv lastResultErr = nil for i := app.Generation - 1; i > 0; i-- { deprecatedAppVersion := &klcv1alpha3.KeptnAppVersion{} - if err := r.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: app.Name + "-" + app.Spec.Version + "-" + common.Hash(i)}, deprecatedAppVersion); err != nil { + appVersionName := operatorcommon.CreateResourceName(common.MaxK8sObjectLength, common.MinKLTNameLen, app.Name, app.Spec.Version, common.Hash(i)) + if err := r.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: appVersionName}, deprecatedAppVersion); err != nil { if !errors.IsNotFound(err) { - r.Log.Error(err, fmt.Sprintf("Could not get KeptnAppVersion: %s", app.Name+"-"+app.Spec.Version+"-"+common.Hash(i))) + r.Log.Error(err, fmt.Sprintf("Could not get KeptnAppVersion: %s", appVersionName)) lastResultErr = err } } else if !deprecatedAppVersion.Status.Status.IsDeprecated() { diff --git a/operator/controllers/lifecycle/keptnapp/controller_test.go b/operator/controllers/lifecycle/keptnapp/controller_test.go index 9f37e4d1b3..250ea0cdac 100644 --- a/operator/controllers/lifecycle/keptnapp/controller_test.go +++ b/operator/controllers/lifecycle/keptnapp/controller_test.go @@ -47,6 +47,30 @@ func TestKeptnAppReconciler_createAppVersionSuccess(t *testing.T) { assert.Equal(t, appVersion.Name, fmt.Sprintf("%s-%s-%s", app.Name, app.Spec.Version, apicommon.Hash(app.Generation))) } +func TestKeptnAppReconciler_createAppVersionWithLongName(t *testing.T) { + //nolint:gci + longName := `loremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypesettingindustryloremloremax` + //nolint:gci + trimmedName := `loremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypesettingindustryloremipsumissimplydummytextoftheprintingandtypeset-version-5feceb66` + + app := &lfcv1alpha3.KeptnApp{ + ObjectMeta: metav1.ObjectMeta{ + Name: longName, + }, + Spec: lfcv1alpha3.KeptnAppSpec{ + Version: "version", + }, + } + r, _, _ := setupReconciler() + + appVersion, err := r.createAppVersion(context.Background(), app) + if err != nil { + t.Errorf("Error creating app version: %s", err.Error()) + } + t.Log("Verifying app name length is not greater than MaxK8sObjectLen") + assert.Equal(t, appVersion.ObjectMeta.Name, trimmedName) +} + func TestKeptnAppReconciler_reconcile(t *testing.T) { r, eventChannel, tracer := setupReconciler() diff --git a/operator/controllers/lifecycle/keptnappversion/controller.go b/operator/controllers/lifecycle/keptnappversion/controller.go index 99c5b4476f..6d37e9d752 100644 --- a/operator/controllers/lifecycle/keptnappversion/controller.go +++ b/operator/controllers/lifecycle/keptnappversion/controller.go @@ -28,6 +28,7 @@ import ( controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/api/errors" @@ -188,7 +189,7 @@ func (r *KeptnAppVersionReconciler) finishKeptnAppVersionReconcile(ctx context.C // metrics: add app duration duration := appVersion.Status.EndTime.Time.Sub(appVersion.Status.StartTime.Time) - r.Meters.AppDuration.Record(ctx, duration.Seconds(), attrs...) + r.Meters.AppDuration.Record(ctx, duration.Seconds(), metric.WithAttributes(attrs...)) spanAppTrace.AddEvent(appVersion.Name + " has finished") spanAppTrace.SetStatus(codes.Ok, "Finished") @@ -215,7 +216,7 @@ func (r *KeptnAppVersionReconciler) setupSpansContexts(ctx context.Context, appV if appVersion.IsEndTimeSet() { r.Log.Info("Increasing app count") attrs := appVersion.GetMetricsAttributes() - r.Meters.AppCount.Add(ctx, 1, attrs...) + r.Meters.AppCount.Add(ctx, 1, metric.WithAttributes(attrs...)) } span.End() } diff --git a/operator/controllers/lifecycle/keptnappversion/controller_test.go b/operator/controllers/lifecycle/keptnappversion/controller_test.go index 2b022a25f6..85cb565d05 100644 --- a/operator/controllers/lifecycle/keptnappversion/controller_test.go +++ b/operator/controllers/lifecycle/keptnappversion/controller_test.go @@ -19,6 +19,7 @@ import ( "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + k8sfake "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/log/zap" ) @@ -160,7 +161,7 @@ func setupReconcilerWithMeters() *KeptnAppVersionReconciler { return r } -func setupReconciler() (*KeptnAppVersionReconciler, chan string, *fake.ITracerMock, *fake.ISpanHandlerMock) { +func setupReconciler(objs ...client.Object) (*KeptnAppVersionReconciler, chan string, *fake.ITracerMock, *fake.ISpanHandlerMock) { //setup logger opts := zap.Options{ Development: true, @@ -185,7 +186,13 @@ func setupReconciler() (*KeptnAppVersionReconciler, chan string, *fake.ITracerMo UnbindSpanFunc: func(reconcileObject client.Object, phase string) error { return nil }, } - fakeClient := fake.NewClient() + workloadInstanceIndexer := func(obj client.Object) []string { + workloadInstance, _ := obj.(*lfcv1alpha3.KeptnWorkloadInstance) + return []string{workloadInstance.Spec.AppName} + } + + fake.SetupSchemes() + fakeClient := k8sfake.NewClientBuilder().WithObjects(objs...).WithScheme(scheme.Scheme).WithObjects().WithIndex(&lfcv1alpha3.KeptnWorkloadInstance{}, "spec.app", workloadInstanceIndexer).Build() recorder := record.NewFakeRecorder(100) r := &KeptnAppVersionReconciler{ diff --git a/operator/controllers/lifecycle/keptnappversion/reconcile_workloadsstate.go b/operator/controllers/lifecycle/keptnappversion/reconcile_workloadsstate.go index 1ca2a9ca3f..367dcbed3b 100644 --- a/operator/controllers/lifecycle/keptnappversion/reconcile_workloadsstate.go +++ b/operator/controllers/lifecycle/keptnappversion/reconcile_workloadsstate.go @@ -5,9 +5,9 @@ import ( klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) func (r *KeptnAppVersionReconciler) reconcileWorkloads(ctx context.Context, appVersion *klcv1alpha3.KeptnAppVersion) (apicommon.KeptnState, error) { @@ -20,18 +20,32 @@ func (r *KeptnAppVersionReconciler) reconcileWorkloads(ctx context.Context, appV LongName: "Reconcile Workloads", } - var newStatus []klcv1alpha3.WorkloadStatus + workloadInstanceList, err := r.getWorkloadInstanceList(ctx, appVersion.Namespace, appVersion.Spec.AppName) + if err != nil { + r.Log.Error(err, "Could not get workloads of appVersion '%s'", appVersion.Name) + return apicommon.StateUnknown, r.handleUnaccessibleWorkloadInstanceList(ctx, appVersion) + } + + newStatus := make([]klcv1alpha3.WorkloadStatus, 0, len(appVersion.Spec.Workloads)) for _, w := range appVersion.Spec.Workloads { r.Log.Info("Reconciling workload " + w.Name) - workload, err := r.getWorkloadInstance(ctx, getWorkloadInstanceName(appVersion.Namespace, appVersion.Spec.AppName, w.Name, w.Version)) - if err != nil && errors.IsNotFound(err) { + workloadStatus := apicommon.StatePending + found := false + instanceName := getWorkloadInstanceName(appVersion.Spec.AppName, w.Name, w.Version) + for _, i := range workloadInstanceList.Items { + // additional filtering of the retrieved WIs is needed, as the List() method retrieves all + // WIs for a specific KeptnApp. The result can contain also WIs, that are not part of the + // latest KeptnAppVersion, so it's needed to double check them + // no need to compare version, as it is part of WI name + if instanceName == i.Name { + found = true + workloadStatus = i.Status.Status + } + } + + if !found { controllercommon.RecordEvent(r.Recorder, phase, "Warning", appVersion, "NotFound", "workloadInstance not found", appVersion.GetVersion()) - workload.Status.Status = apicommon.StatePending - } else if err != nil { - r.Log.Error(err, "Could not get workload") - workload.Status.Status = apicommon.StateUnknown } - workloadStatus := workload.Status.Status newStatus = append(newStatus, klcv1alpha3.WorkloadStatus{ Workload: w, @@ -48,16 +62,31 @@ func (r *KeptnAppVersionReconciler) reconcileWorkloads(ctx context.Context, appV r.Log.Info("Workload status", "status", appVersion.Status.WorkloadStatus) // Write Status Field - err := r.Client.Status().Update(ctx, appVersion) + err = r.Client.Status().Update(ctx, appVersion) return overallState, err } -func (r *KeptnAppVersionReconciler) getWorkloadInstance(ctx context.Context, workload types.NamespacedName) (klcv1alpha3.KeptnWorkloadInstance, error) { - workloadInstance := &klcv1alpha3.KeptnWorkloadInstance{} - err := r.Get(ctx, workload, workloadInstance) - return *workloadInstance, err +func (r *KeptnAppVersionReconciler) getWorkloadInstanceList(ctx context.Context, namespace string, appName string) (*klcv1alpha3.KeptnWorkloadInstanceList, error) { + workloadInstanceList := &klcv1alpha3.KeptnWorkloadInstanceList{} + err := r.Client.List(ctx, workloadInstanceList, client.InNamespace(namespace), client.MatchingFields{ + "spec.app": appName, + }) + return workloadInstanceList, err +} + +func (r *KeptnAppVersionReconciler) handleUnaccessibleWorkloadInstanceList(ctx context.Context, appVersion *klcv1alpha3.KeptnAppVersion) error { + newStatus := make([]klcv1alpha3.WorkloadStatus, 0, len(appVersion.Spec.Workloads)) + for _, w := range appVersion.Spec.Workloads { + newStatus = append(newStatus, klcv1alpha3.WorkloadStatus{ + Workload: w, + Status: apicommon.StateUnknown, + }) + } + appVersion.Status.WorkloadOverallStatus = apicommon.StateUnknown + appVersion.Status.WorkloadStatus = newStatus + return r.Client.Status().Update(ctx, appVersion) } -func getWorkloadInstanceName(namespace string, appName string, workloadName string, version string) types.NamespacedName { - return types.NamespacedName{Namespace: namespace, Name: appName + "-" + workloadName + "-" + version} +func getWorkloadInstanceName(appName string, workloadName string, version string) string { + return operatorcommon.CreateResourceName(apicommon.MaxK8sObjectLength, apicommon.MinKLTNameLen, appName, workloadName, version) } diff --git a/operator/controllers/lifecycle/keptnappversion/reconcile_workloadstate_test.go b/operator/controllers/lifecycle/keptnappversion/reconcile_workloadstate_test.go new file mode 100644 index 0000000000..40edc38a48 --- /dev/null +++ b/operator/controllers/lifecycle/keptnappversion/reconcile_workloadstate_test.go @@ -0,0 +1,220 @@ +package keptnappversion + +import ( + "context" + "testing" + + lfcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + "github.com/stretchr/testify/require" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +//nolint:dogsled +func TestKeptnAppVersionReconciler_reconcileWorkloads_noWorkloads(t *testing.T) { + appVersion := &lfcv1alpha3.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Name: "appversion", + Namespace: "default", + }, + Spec: lfcv1alpha3.KeptnAppVersionSpec{ + AppName: "app", + }, + } + r, _, _, _ := setupReconciler(appVersion) + + state, err := r.reconcileWorkloads(context.TODO(), appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateSucceeded, state) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateSucceeded, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 0) +} + +//nolint:dogsled +func TestKeptnAppVersionReconciler_reconcileWorkloads(t *testing.T) { + appVersion := &lfcv1alpha3.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Name: "appversion", + Namespace: "default", + }, + Spec: lfcv1alpha3.KeptnAppVersionSpec{ + KeptnAppSpec: lfcv1alpha3.KeptnAppSpec{ + Workloads: []lfcv1alpha3.KeptnWorkloadRef{ + { + Name: "workload", + Version: "ver1", + }, + }, + }, + AppName: "app", + }, + } + r, _, _, _ := setupReconciler(appVersion) + + // No workloadInstances are created yet, should stay in Pending state + + state, err := r.reconcileWorkloads(context.TODO(), appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StatePending, state) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StatePending, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 1) + require.Equal(t, []lfcv1alpha3.WorkloadStatus{ + { + Workload: lfcv1alpha3.KeptnWorkloadRef{ + Name: "workload", + Version: "ver1", + }, + Status: apicommon.StatePending, + }, + }, appVersion.Status.WorkloadStatus) + + // Creating WorkloadInstace that is not part of the App -> should stay Pending + + wi1 := &lfcv1alpha3.KeptnWorkloadInstance{ + ObjectMeta: v1.ObjectMeta{ + Name: "workload", + Namespace: "default", + }, + Spec: lfcv1alpha3.KeptnWorkloadInstanceSpec{ + KeptnWorkloadSpec: lfcv1alpha3.KeptnWorkloadSpec{ + AppName: "app2", + }, + }, + } + + err = r.Client.Create(context.TODO(), wi1) + require.Nil(t, err) + + state, err = r.reconcileWorkloads(context.TODO(), appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StatePending, state) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StatePending, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 1) + require.Equal(t, []lfcv1alpha3.WorkloadStatus{ + { + Workload: lfcv1alpha3.KeptnWorkloadRef{ + Name: "workload", + Version: "ver1", + }, + Status: apicommon.StatePending, + }, + }, appVersion.Status.WorkloadStatus) + + // Creating WorkloadInstance of App with progressing state -> appVersion should be Progressing + + wi2 := &lfcv1alpha3.KeptnWorkloadInstance{ + ObjectMeta: v1.ObjectMeta{ + Name: "app-workload-ver1", + Namespace: "default", + }, + Spec: lfcv1alpha3.KeptnWorkloadInstanceSpec{ + KeptnWorkloadSpec: lfcv1alpha3.KeptnWorkloadSpec{ + AppName: "app", + }, + }, + } + + err = r.Client.Create(context.TODO(), wi2) + require.Nil(t, err) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: wi2.Namespace, Name: wi2.Name}, wi2) + require.Nil(t, err) + + wi2.Status.Status = apicommon.StateProgressing + err = r.Client.Update(context.TODO(), wi2) + require.Nil(t, err) + + state, err = r.reconcileWorkloads(context.TODO(), appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateProgressing, state) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateProgressing, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 1) + require.Equal(t, []lfcv1alpha3.WorkloadStatus{ + { + Workload: lfcv1alpha3.KeptnWorkloadRef{ + Name: "workload", + Version: "ver1", + }, + Status: apicommon.StateProgressing, + }, + }, appVersion.Status.WorkloadStatus) + + // Updating WorkloadInstance of App with succeeded state -> appVersion should be Succeeded + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: wi2.Namespace, Name: wi2.Name}, wi2) + require.Nil(t, err) + + wi2.Status.Status = apicommon.StateSucceeded + err = r.Client.Update(context.TODO(), wi2) + require.Nil(t, err) + + state, err = r.reconcileWorkloads(context.TODO(), appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateSucceeded, state) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateSucceeded, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 1) + require.Equal(t, []lfcv1alpha3.WorkloadStatus{ + { + Workload: lfcv1alpha3.KeptnWorkloadRef{ + Name: "workload", + Version: "ver1", + }, + Status: apicommon.StateSucceeded, + }, + }, appVersion.Status.WorkloadStatus) +} + +//nolint:dogsled +func TestKeptnAppVersionReconciler_handleUnaccessibleWorkloadInstanceList(t *testing.T) { + appVersion := &lfcv1alpha3.KeptnAppVersion{ + ObjectMeta: v1.ObjectMeta{ + Name: "appversion", + Namespace: "default", + }, + Spec: lfcv1alpha3.KeptnAppVersionSpec{ + KeptnAppSpec: lfcv1alpha3.KeptnAppSpec{ + Workloads: []lfcv1alpha3.KeptnWorkloadRef{ + { + Name: "workload", + Version: "ver1", + }, + }, + }, + AppName: "app", + }, + } + r, _, _, _ := setupReconciler(appVersion) + + err := r.handleUnaccessibleWorkloadInstanceList(context.TODO(), appVersion) + require.Nil(t, err) + + err = r.Client.Get(context.TODO(), types.NamespacedName{Namespace: appVersion.Namespace, Name: appVersion.Name}, appVersion) + require.Nil(t, err) + require.Equal(t, apicommon.StateUnknown, appVersion.Status.WorkloadOverallStatus) + require.Len(t, appVersion.Status.WorkloadStatus, 1) + require.Equal(t, []lfcv1alpha3.WorkloadStatus{ + { + Workload: lfcv1alpha3.KeptnWorkloadRef{ + Name: "workload", + Version: "ver1", + }, + Status: apicommon.StateUnknown, + }, + }, appVersion.Status.WorkloadStatus) +} diff --git a/operator/controllers/lifecycle/keptnevaluation/controller.go b/operator/controllers/lifecycle/keptnevaluation/controller.go index 1202211007..7f126e182d 100644 --- a/operator/controllers/lifecycle/keptnevaluation/controller.go +++ b/operator/controllers/lifecycle/keptnevaluation/controller.go @@ -29,6 +29,7 @@ import ( controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/api/errors" @@ -248,11 +249,11 @@ func (r *KeptnEvaluationReconciler) updateFinishedEvaluationMetrics(ctx context. r.Log.Info("Increasing evaluation count") // metrics: increment evaluation counter - r.Meters.EvaluationCount.Add(ctx, 1, attrs...) + r.Meters.EvaluationCount.Add(ctx, 1, metric.WithAttributes(attrs...)) // metrics: add evaluation duration duration := evaluation.Status.EndTime.Time.Sub(evaluation.Status.StartTime.Time) - r.Meters.EvaluationDuration.Record(ctx, duration.Seconds(), attrs...) + r.Meters.EvaluationDuration.Record(ctx, duration.Seconds(), metric.WithAttributes(attrs...)) return nil } diff --git a/operator/controllers/lifecycle/keptnevaluation/controller_test.go b/operator/controllers/lifecycle/keptnevaluation/controller_test.go index 0628838095..f63900019f 100644 --- a/operator/controllers/lifecycle/keptnevaluation/controller_test.go +++ b/operator/controllers/lifecycle/keptnevaluation/controller_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/go-logr/logr" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" diff --git a/operator/controllers/lifecycle/keptntask/container_builder.go b/operator/controllers/lifecycle/keptntask/container_builder.go new file mode 100644 index 0000000000..098db9bb61 --- /dev/null +++ b/operator/controllers/lifecycle/keptntask/container_builder.go @@ -0,0 +1,53 @@ +package keptntask + +import ( + klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + "golang.org/x/net/context" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +// ContainerBuilder implements container builder interface for python +type ContainerBuilder struct { + taskDef *klcv1alpha3.KeptnTaskDefinition +} + +func NewContainerBuilder(taskDef *klcv1alpha3.KeptnTaskDefinition) *ContainerBuilder { + return &ContainerBuilder{ + taskDef: taskDef, + } +} + +func (c *ContainerBuilder) CreateContainerWithVolumes(ctx context.Context) (*corev1.Container, []corev1.Volume, error) { + return c.taskDef.Spec.Container.Container, c.generateVolumes(), nil +} + +func (c *ContainerBuilder) getVolumeSource() *corev1.EmptyDirVolumeSource { + quantity, ok := c.taskDef.Spec.Container.Resources.Limits["memory"] + if ok { + return &corev1.EmptyDirVolumeSource{ + SizeLimit: &quantity, + Medium: corev1.StorageMedium("Memory"), + } + } + + return &corev1.EmptyDirVolumeSource{ + // Default 50% of the memory of the node, max 1Gi + SizeLimit: resource.NewQuantity(1, resource.Format("Gi")), + Medium: corev1.StorageMedium("Memory"), + } +} + +func (c *ContainerBuilder) generateVolumes() []corev1.Volume { + if !c.taskDef.IsVolumeMountPresent() { + return []corev1.Volume{} + } + return []corev1.Volume{ + { + Name: c.taskDef.Spec.Container.VolumeMounts[0].Name, + VolumeSource: corev1.VolumeSource{ + EmptyDir: c.getVolumeSource(), + }, + }, + } +} diff --git a/operator/controllers/lifecycle/keptntask/container_builder_test.go b/operator/controllers/lifecycle/keptntask/container_builder_test.go new file mode 100644 index 0000000000..bcdaee78c1 --- /dev/null +++ b/operator/controllers/lifecycle/keptntask/container_builder_test.go @@ -0,0 +1,244 @@ +package keptntask + +import ( + "context" + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func TestContainerBuilder_CreateContainerWithVolumes(t *testing.T) { + tests := []struct { + name string + builder ContainerBuilder + wantContainer *v1.Container + wantVolumes []v1.Volume + }{ + { + name: "defined without volumes", + builder: ContainerBuilder{ + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + }, + }, + }, + }, + }, + wantContainer: &v1.Container{ + Image: "image", + }, + wantVolumes: []v1.Volume{}, + }, + { + name: "defined with volume", + builder: ContainerBuilder{ + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{ + { + Name: "test-volume", + MountPath: "path", + }, + }, + }, + }, + }, + }, + }, + wantContainer: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{ + { + Name: "test-volume", + MountPath: "path", + }, + }, + }, + wantVolumes: []v1.Volume{ + { + Name: "test-volume", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{ + SizeLimit: resource.NewQuantity(1, resource.Format("Gi")), + Medium: v1.StorageMedium("Memory"), + }, + }, + }, + }, + }, + { + name: "defined with volume and limits", + builder: ContainerBuilder{ + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "memory": *resource.NewQuantity(100, resource.Format("Mi")), + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "test-volume", + MountPath: "path", + }, + }, + }, + }, + }, + }, + }, + wantContainer: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{ + { + Name: "test-volume", + MountPath: "path", + }, + }, + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "memory": *resource.NewQuantity(100, resource.Format("Mi")), + }, + }, + }, + wantVolumes: []v1.Volume{ + { + Name: "test-volume", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{ + SizeLimit: resource.NewQuantity(100, resource.Format("Mi")), + Medium: v1.StorageMedium("Memory"), + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + container, volumes, _ := tt.builder.CreateContainerWithVolumes(context.TODO()) + require.Equal(t, tt.wantContainer, container) + require.Equal(t, tt.wantVolumes, volumes) + }) + } +} + +func Test_GenerateVolumes(t *testing.T) { + tests := []struct { + name string + taskDef *v1alpha3.KeptnTaskDefinition + want []v1.Volume + }{ + { + name: "defined", + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + VolumeMounts: []v1.VolumeMount{ + { + Name: "name", + MountPath: "path", + }, + }, + }, + }, + }, + }, + want: []v1.Volume{ + { + Name: "name", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{ + SizeLimit: resource.NewQuantity(1, resource.Format("Gi")), + Medium: v1.StorageMedium("Memory"), + }, + }, + }, + }, + }, + { + name: "empty", + taskDef: &v1alpha3.KeptnTaskDefinition{}, + want: []v1.Volume{}, + }, + } + for _, tt := range tests { + builder := ContainerBuilder{ + taskDef: tt.taskDef, + } + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, builder.generateVolumes()) + }) + } +} + +func Test_GetVolumeSource(t *testing.T) { + tests := []struct { + name string + taskDef *v1alpha3.KeptnTaskDefinition + want *v1.EmptyDirVolumeSource + }{ + { + name: "not set limits", + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{}, + }, + }, + }, + }, + }, + want: &v1.EmptyDirVolumeSource{ + SizeLimit: resource.NewQuantity(1, resource.Format("Gi")), + Medium: v1.StorageMedium("Memory"), + }, + }, + { + name: "set limits", + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "memory": *resource.NewQuantity(100, resource.Format("Mi")), + }, + }, + }, + }, + }, + }, + want: &v1.EmptyDirVolumeSource{ + SizeLimit: resource.NewQuantity(100, resource.Format("Mi")), + Medium: v1.StorageMedium("Memory"), + }, + }, + } + for _, tt := range tests { + builder := ContainerBuilder{ + taskDef: tt.taskDef, + } + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, builder.getVolumeSource()) + }) + } +} diff --git a/operator/controllers/lifecycle/keptntask/controller.go b/operator/controllers/lifecycle/keptntask/controller.go index cd6ab1d428..c6100ccc17 100644 --- a/operator/controllers/lifecycle/keptntask/controller.go +++ b/operator/controllers/lifecycle/keptntask/controller.go @@ -18,16 +18,15 @@ package keptntask import ( "context" - "fmt" "time" "github.com/go-logr/logr" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" - controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" batchv1 "k8s.io/api/batch/v1" @@ -90,14 +89,14 @@ func (r *KeptnTaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } }(task) - jobExists, err := r.JobExists(ctx, *task, req.Namespace) - if err != nil { + job, err := r.getJob(ctx, task.Status.JobName, req.Namespace) + if err != nil && !errors.IsNotFound(err) { r.Log.Error(err, "Could not check if job is running") span.SetStatus(codes.Error, err.Error()) return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil } - if !jobExists { + if job == nil { err = r.createJob(ctx, req, task) if err != nil { span.SetStatus(codes.Error, err.Error()) @@ -127,11 +126,11 @@ func (r *KeptnTaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( r.Log.Info("Increasing task count") // metrics: increment task counter - r.Meters.TaskCount.Add(ctx, 1, attrs...) + r.Meters.TaskCount.Add(ctx, 1, metric.WithAttributes(attrs...)) // metrics: add task duration duration := task.Status.EndTime.Time.Sub(task.Status.StartTime.Time) - r.Meters.TaskDuration.Record(ctx, duration.Seconds(), attrs...) + r.Meters.TaskDuration.Record(ctx, duration.Seconds(), metric.WithAttributes(attrs...)) return ctrl.Result{}, nil } @@ -145,29 +144,6 @@ func (r *KeptnTaskReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *KeptnTaskReconciler) JobExists(ctx context.Context, task klcv1alpha3.KeptnTask, namespace string) (bool, error) { - jobList := &batchv1.JobList{} - - jobLabels := client.MatchingLabels{} - for k, v := range task.CreateKeptnLabels() { - jobLabels[k] = v - } - - if len(jobLabels) == 0 { - return false, fmt.Errorf(controllererrors.ErrNoLabelsFoundTask, task.Name) - } - - if err := r.Client.List(ctx, jobList, client.InNamespace(namespace), jobLabels); err != nil { - return false, err - } - - if len(jobList.Items) > 0 { - return true, nil - } - - return false, nil -} - func (r *KeptnTaskReconciler) getTracer() controllercommon.ITracer { return r.TracerFactory.GetTracer(traceComponentName) } diff --git a/operator/controllers/lifecycle/keptntask/job_runner_builder.go b/operator/controllers/lifecycle/keptntask/job_runner_builder.go new file mode 100644 index 0000000000..d177c0cae6 --- /dev/null +++ b/operator/controllers/lifecycle/keptntask/job_runner_builder.go @@ -0,0 +1,37 @@ +package keptntask + +import ( + "github.com/go-logr/logr" + klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + "golang.org/x/net/context" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// JobRunnerBuilder is the interface that describes the operations needed to help build job specs of a task +type JobRunnerBuilder interface { + // CreateContainerWithVolumes returns a job container and volumes based on the task definition spec + CreateContainerWithVolumes(ctx context.Context) (*corev1.Container, []corev1.Volume, error) +} + +// BuilderOptions contains everything needed to build the current job +type BuilderOptions struct { + client.Client + recorder record.EventRecorder + req ctrl.Request + Log logr.Logger + task *klcv1alpha3.KeptnTask + taskDef *klcv1alpha3.KeptnTaskDefinition +} + +func getJobRunnerBuilder(options BuilderOptions) JobRunnerBuilder { + if options.taskDef.IsJSSpecDefined() { + return NewJSBuilder(options) + } + if options.taskDef.IsContainerSpecDefined() { + return NewContainerBuilder(options.taskDef) + } + return nil +} diff --git a/operator/controllers/lifecycle/keptntask/job_runner_builder_test.go b/operator/controllers/lifecycle/keptntask/job_runner_builder_test.go new file mode 100644 index 0000000000..36f23442e1 --- /dev/null +++ b/operator/controllers/lifecycle/keptntask/job_runner_builder_test.go @@ -0,0 +1,62 @@ +package keptntask + +import ( + "testing" + + "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" +) + +func Test_getJobRunnerBuilder(t *testing.T) { + jsBuilderOptions := BuilderOptions{ + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Function: &v1alpha3.FunctionSpec{ + Inline: v1alpha3.Inline{ + Code: "some code", + }, + }, + }, + }, + } + containerBuilderOptions := BuilderOptions{ + taskDef: &v1alpha3.KeptnTaskDefinition{ + Spec: v1alpha3.KeptnTaskDefinitionSpec{ + Container: &v1alpha3.ContainerSpec{ + Container: &v1.Container{ + Image: "image", + }, + }, + }, + }, + } + tests := []struct { + name string + options BuilderOptions + want JobRunnerBuilder + }{ + { + name: "js builder", + options: jsBuilderOptions, + want: NewJSBuilder(jsBuilderOptions), + }, + { + name: "container builder", + options: containerBuilderOptions, + want: NewContainerBuilder(containerBuilderOptions.taskDef), + }, + { + name: "invalid builder", + options: BuilderOptions{ + taskDef: &v1alpha3.KeptnTaskDefinition{}, + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, getJobRunnerBuilder(tt.options)) + }) + } +} diff --git a/operator/controllers/lifecycle/keptntask/job_utils.go b/operator/controllers/lifecycle/keptntask/job_utils.go index a57874927b..8d58bb96dd 100644 --- a/operator/controllers/lifecycle/keptntask/job_utils.go +++ b/operator/controllers/lifecycle/keptntask/job_utils.go @@ -3,15 +3,17 @@ package keptntask import ( "context" "fmt" - "reflect" - "github.com/imdario/mergo" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" + controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) func (r *KeptnTaskReconciler) createJob(ctx context.Context, req ctrl.Request, task *klcv1alpha3.KeptnTask) error { @@ -22,7 +24,7 @@ func (r *KeptnTaskReconciler) createJob(ctx context.Context, req ctrl.Request, t return err } - if !reflect.DeepEqual(definition.Spec.Function, klcv1alpha3.FunctionSpec{}) { + if definition.SpecExists() { jobName, err = r.createFunctionJob(ctx, req, task, definition) if err != nil { return err @@ -40,31 +42,8 @@ func (r *KeptnTaskReconciler) createJob(ctx context.Context, req ctrl.Request, t } func (r *KeptnTaskReconciler) createFunctionJob(ctx context.Context, req ctrl.Request, task *klcv1alpha3.KeptnTask, definition *klcv1alpha3.KeptnTaskDefinition) (string, error) { - params, hasParent, err := r.parseFunctionTaskDefinition(definition) - if err != nil { - return "", err - } - if hasParent { - if err := r.handleParent(ctx, req, task, definition, params); err != nil { - return "", err - } - } - - params.Context = setupTaskContext(task) - - if len(task.Spec.Parameters.Inline) > 0 { - err = mergo.Merge(¶ms.Parameters, task.Spec.Parameters.Inline) - if err != nil { - controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") - return "", err - } - } - - if task.Spec.SecureParameters.Secret != "" { - params.SecureParameters = task.Spec.SecureParameters.Secret - } - job, err := r.generateFunctionJob(task, params) + job, err := r.generateJob(ctx, task, definition, req) if err != nil { return "", err } @@ -105,7 +84,7 @@ func (r *KeptnTaskReconciler) getJob(ctx context.Context, jobName string, namesp job := &batchv1.Job{} err := r.Client.Get(ctx, types.NamespacedName{Name: jobName, Namespace: namespace}, job) if err != nil { - return job, err + return nil, err } return job, nil } @@ -127,21 +106,53 @@ func setupTaskContext(task *klcv1alpha3.KeptnTask) klcv1alpha3.TaskContext { return taskContext } -func (r *KeptnTaskReconciler) handleParent(ctx context.Context, req ctrl.Request, task *klcv1alpha3.KeptnTask, definition *klcv1alpha3.KeptnTaskDefinition, params FunctionExecutionParams) error { - var parentJobParams FunctionExecutionParams - parentDefinition, err := controllercommon.GetTaskDefinition(r.Client, r.Log, ctx, definition.Spec.Function.FunctionReference.Name, req.Namespace) +func (r *KeptnTaskReconciler) generateJob(ctx context.Context, task *klcv1alpha3.KeptnTask, definition *klcv1alpha3.KeptnTaskDefinition, request ctrl.Request) (*batchv1.Job, error) { + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: apicommon.GenerateJobName(task.Name), + Namespace: task.Namespace, + Labels: task.Labels, + Annotations: task.CreateKeptnAnnotations(), + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: task.Labels, + Annotations: task.Annotations, + }, + Spec: corev1.PodSpec{ + RestartPolicy: "OnFailure", + }, + }, + BackoffLimit: task.Spec.Retries, + ActiveDeadlineSeconds: task.GetActiveDeadlineSeconds(), + }, + } + err := controllerutil.SetControllerReference(task, job, r.Scheme) if err != nil { - controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionNotFound", fmt.Sprintf("could not find KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") - return err + r.Log.Error(err, "could not set controller reference:") } - parentJobParams, _, err = r.parseFunctionTaskDefinition(parentDefinition) - if err != nil { - return err + + builderOpt := BuilderOptions{ + Client: r.Client, + req: request, + Log: r.Log, + task: task, + taskDef: definition, + recorder: r.Recorder, + } + + builder := getJobRunnerBuilder(builderOpt) + if builder == nil { + return nil, controllererrors.ErrNoTaskDefinitionSpec } - err = mergo.Merge(¶ms, parentJobParams) + + container, volumes, err := builder.CreateContainerWithVolumes(ctx) if err != nil { - controllercommon.RecordEvent(r.Recorder, apicommon.PhaseCreateTask, "Warning", task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", task.Spec.TaskDefinition), "") - return err + return nil, controllererrors.ErrCannotMarshalParams } - return nil + + job.Spec.Template.Spec.Containers = []corev1.Container{*container} + job.Spec.Template.Spec.Volumes = volumes + return job, nil } diff --git a/operator/controllers/lifecycle/keptntask/job_utils_test.go b/operator/controllers/lifecycle/keptntask/job_utils_test.go index 3ed1e2c4b5..5d18417361 100644 --- a/operator/controllers/lifecycle/keptntask/job_utils_test.go +++ b/operator/controllers/lifecycle/keptntask/job_utils_test.go @@ -80,14 +80,14 @@ func TestKeptnTaskReconciler_createJob(t *testing.T) { require.Len(t, resultingJob.Spec.Template.Spec.Containers, 1) require.Len(t, resultingJob.Spec.Template.Spec.Containers[0].Env, 4) require.Equal(t, map[string]string{ - "label1": "label2", + "label1": "label2", + }, resultingJob.Labels) + require.Equal(t, map[string]string{ + "annotation1": "annotation2", "keptn.sh/app": "my-app", "keptn.sh/task-name": "my-task", "keptn.sh/version": "", "keptn.sh/workload": "my-workload", - }, resultingJob.Labels) - require.Equal(t, map[string]string{ - "annotation1": "annotation2", }, resultingJob.Annotations) } @@ -154,14 +154,14 @@ func TestKeptnTaskReconciler_createJob_withTaskDefInDefaultNamespace(t *testing. require.Len(t, resultingJob.Spec.Template.Spec.Containers, 1) require.Len(t, resultingJob.Spec.Template.Spec.Containers[0].Env, 4) require.Equal(t, map[string]string{ - "label1": "label2", + "label1": "label2", + }, resultingJob.Labels) + require.Equal(t, map[string]string{ + "annotation1": "annotation2", "keptn.sh/app": "my-app", "keptn.sh/task-name": "my-task", "keptn.sh/version": "", "keptn.sh/workload": "my-workload", - }, resultingJob.Labels) - require.Equal(t, map[string]string{ - "annotation1": "annotation2", }, resultingJob.Annotations) } @@ -272,7 +272,7 @@ func makeTaskDefinitionWithConfigmapRef(name, namespace, configMapName string) * }, }, Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ - Function: klcv1alpha3.FunctionSpec{ + Function: &klcv1alpha3.FunctionSpec{ ConfigMapReference: klcv1alpha3.ConfigMapReference{ Name: configMapName, }, diff --git a/operator/controllers/lifecycle/keptntask/function_utils.go b/operator/controllers/lifecycle/keptntask/js_builder.go similarity index 50% rename from operator/controllers/lifecycle/keptntask/function_utils.go rename to operator/controllers/lifecycle/keptntask/js_builder.go index 877d0467a3..b6fb1083e8 100644 --- a/operator/controllers/lifecycle/keptntask/function_utils.go +++ b/operator/controllers/lifecycle/keptntask/js_builder.go @@ -3,18 +3,29 @@ package keptntask import ( "encoding/json" "fmt" - "math/rand" "os" + "github.com/imdario/mergo" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" + controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" - batchv1 "k8s.io/api/batch/v1" + "golang.org/x/net/context" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) +// JSBuilder implements container builder interface for javascript deno +type JSBuilder struct { + options BuilderOptions +} + +func NewJSBuilder(options BuilderOptions) *JSBuilder { + return &JSBuilder{ + options: options, + } +} + +// FunctionExecutionParams stores parametersrelatedto js deno container creation type FunctionExecutionParams struct { ConfigMap string Parameters map[string]string @@ -23,35 +34,7 @@ type FunctionExecutionParams struct { Context klcv1alpha3.TaskContext } -func (r *KeptnTaskReconciler) generateFunctionJob(task *klcv1alpha3.KeptnTask, params FunctionExecutionParams) (*batchv1.Job, error) { - randomId := rand.Intn(99999-10000) + 10000 - jobId := fmt.Sprintf("klc-%s-%d", apicommon.TruncateString(task.Name, apicommon.MaxTaskNameLength), randomId) - job := &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: jobId, - Namespace: task.Namespace, - Labels: task.CreateKeptnLabels(), - Annotations: task.Annotations, - }, - Spec: batchv1.JobSpec{ - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: task.Labels, - Annotations: task.Annotations, - }, - Spec: corev1.PodSpec{ - RestartPolicy: "OnFailure", - }, - }, - BackoffLimit: task.Spec.Retries, - ActiveDeadlineSeconds: task.GetActiveDeadlineSeconds(), - }, - } - err := controllerutil.SetControllerReference(task, job, r.Scheme) - if err != nil { - r.Log.Error(err, "could not set controller reference:") - } - +func (js *JSBuilder) CreateContainerWithVolumes(ctx context.Context) (*corev1.Container, []corev1.Volume, error) { container := corev1.Container{ Name: "keptn-function-runner", Image: os.Getenv("FUNCTION_RUNNER_IMAGE"), @@ -59,17 +42,21 @@ func (r *KeptnTaskReconciler) generateFunctionJob(task *klcv1alpha3.KeptnTask, p var envVars []corev1.EnvVar + params, err := js.getParams(ctx) + if err != nil { + return nil, nil, err + } if len(params.Parameters) > 0 { jsonParams, err := json.Marshal(params.Parameters) if err != nil { - return job, controllererrors.ErrCannotMarshalParams + return nil, nil, err } envVars = append(envVars, corev1.EnvVar{Name: "DATA", Value: string(jsonParams)}) } jsonParams, err := json.Marshal(params.Context) if err != nil { - return job, controllererrors.ErrCannotMarshalParams + return nil, nil, err } envVars = append(envVars, corev1.EnvVar{Name: "CONTEXT", Value: string(jsonParams)}) @@ -84,24 +71,23 @@ func (r *KeptnTaskReconciler) generateFunctionJob(task *klcv1alpha3.KeptnTask, p }, }) } - + var jobVolumes []corev1.Volume // 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, - }, + jobVolumes = append(jobVolumes, corev1.Volume{ + Name: "function-mount", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: params.ConfigMap, }, }, }, - } + }) + container.VolumeMounts = []corev1.VolumeMount{ { Name: "function-mount", @@ -115,13 +101,38 @@ func (r *KeptnTaskReconciler) generateFunctionJob(task *klcv1alpha3.KeptnTask, p } container.Env = envVars - job.Spec.Template.Spec.Containers = []corev1.Container{ - container, + return &container, jobVolumes, nil + +} + +func (js *JSBuilder) getParams(ctx context.Context) (*FunctionExecutionParams, error) { + params, hasParent, err := js.parseFunctionTaskDefinition(js.options.taskDef) + if err != nil { + return nil, err + } + if hasParent { + if err := js.handleParent(ctx, ¶ms); err != nil { + return nil, err + } + } + + params.Context = setupTaskContext(js.options.task) + + if len(js.options.task.Spec.Parameters.Inline) > 0 { + err = mergo.Merge(¶ms.Parameters, js.options.task.Spec.Parameters.Inline) + if err != nil { + controllercommon.RecordEvent(js.options.recorder, apicommon.PhaseCreateTask, "Warning", js.options.task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", js.options.task.Spec.TaskDefinition), "") + return nil, err + } } - return job, nil + + if js.options.task.Spec.SecureParameters.Secret != "" { + params.SecureParameters = js.options.task.Spec.SecureParameters.Secret + } + return ¶ms, nil } -func (r *KeptnTaskReconciler) parseFunctionTaskDefinition(definition *klcv1alpha3.KeptnTaskDefinition) (FunctionExecutionParams, bool, error) { +func (js *JSBuilder) parseFunctionTaskDefinition(definition *klcv1alpha3.KeptnTaskDefinition) (FunctionExecutionParams, bool, error) { params := FunctionExecutionParams{} // Firstly check if this task definition has a parent object @@ -131,7 +142,7 @@ func (r *KeptnTaskReconciler) parseFunctionTaskDefinition(definition *klcv1alpha } 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)) + js.options.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 @@ -156,3 +167,22 @@ func (r *KeptnTaskReconciler) parseFunctionTaskDefinition(definition *klcv1alpha } return params, hasParent, nil } + +func (js *JSBuilder) handleParent(ctx context.Context, params *FunctionExecutionParams) error { + var parentJobParams FunctionExecutionParams + parentDefinition, err := controllercommon.GetTaskDefinition(js.options.Client, js.options.Log, ctx, js.options.taskDef.Spec.Function.FunctionReference.Name, js.options.req.Namespace) + if err != nil { + controllercommon.RecordEvent(js.options.recorder, apicommon.PhaseCreateTask, "Warning", js.options.task, "TaskDefinitionNotFound", fmt.Sprintf("could not find KeptnTaskDefinition: %s ", js.options.task.Spec.TaskDefinition), "") + return err + } + parentJobParams, _, err = js.parseFunctionTaskDefinition(parentDefinition) + if err != nil { + return err + } + err = mergo.Merge(params, parentJobParams) + if err != nil { + controllercommon.RecordEvent(js.options.recorder, apicommon.PhaseCreateTask, "Warning", js.options.task, "TaskDefinitionMergeFailure", fmt.Sprintf("could not merge KeptnTaskDefinition: %s ", js.options.task.Spec.TaskDefinition), "") + return err + } + return nil +} diff --git a/operator/controllers/lifecycle/keptntask/js_builder_test.go b/operator/controllers/lifecycle/keptntask/js_builder_test.go new file mode 100644 index 0000000000..067f235aa1 --- /dev/null +++ b/operator/controllers/lifecycle/keptntask/js_builder_test.go @@ -0,0 +1,234 @@ +package keptntask + +import ( + "testing" + + "github.com/go-logr/logr/testr" + klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" + "github.com/keptn/lifecycle-toolkit/operator/controllers/common/fake" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" +) + +func TestJSBuilder_handleParent(t *testing.T) { + + def := &klcv1alpha3.KeptnTaskDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mytaskdef", + Namespace: "default", + }, + Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ + Function: &klcv1alpha3.FunctionSpec{ + FunctionReference: klcv1alpha3.FunctionReference{ + Name: "mytaskdef", + }}}, + } + paramDef := &klcv1alpha3.KeptnTaskDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mytd", + Namespace: "default", + }, + Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ + Function: &klcv1alpha3.FunctionSpec{ + FunctionReference: klcv1alpha3.FunctionReference{ + Name: "mytd"}, + Parameters: klcv1alpha3.TaskParameters{ + Inline: map[string]string{"DATA": "mydata"}, + }, + SecureParameters: klcv1alpha3.SecureParameters{ + Secret: "mysecret", + }, + }, + }, + } + + tests := []struct { + name string + options BuilderOptions + params FunctionExecutionParams + wantErr bool + err string + }{ + { + name: "no definition", + options: BuilderOptions{ + Client: fake.NewClient(), + recorder: &record.FakeRecorder{}, + req: ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: "default"}, + }, + Log: testr.New(t), + taskDef: def, + task: makeTask("myt", "default", def.Name), + }, + params: FunctionExecutionParams{}, + wantErr: true, + err: "not found", + }, + { + name: "definition exists, recursive", + options: BuilderOptions{ + Client: fake.NewClient(def), + recorder: &record.FakeRecorder{}, + req: ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: "default"}, + }, + Log: testr.New(t), + taskDef: def, + task: makeTask("myt2", "default", def.Name), + }, + params: FunctionExecutionParams{}, + wantErr: false, + }, + { + name: "definition exists, with parameters and secrets", + options: BuilderOptions{ + Client: fake.NewClient(paramDef, def), + recorder: &record.FakeRecorder{}, + req: ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: "default"}, + }, + Log: testr.New(t), + taskDef: paramDef, + task: makeTask("myt3", "default", paramDef.Name), + }, + params: FunctionExecutionParams{}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + js := &JSBuilder{ + options: tt.options, + } + err := js.handleParent(context.TODO(), &tt.params) + if !tt.wantErr { + require.Nil(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.err) + } + + }) + } +} +func TestJSBuilder_hasParams(t *testing.T) { + + def := &klcv1alpha3.KeptnTaskDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mytaskdef", + Namespace: "default", + }, + Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ + Function: &klcv1alpha3.FunctionSpec{ + HttpReference: klcv1alpha3.HttpReference{Url: "donothing"}, + Parameters: klcv1alpha3.TaskParameters{ + Inline: map[string]string{"DATA2": "mydata2"}, + }, + SecureParameters: klcv1alpha3.SecureParameters{ + Secret: "mysecret2", + }, + }}, + } + paramDef := &klcv1alpha3.KeptnTaskDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mytd", + Namespace: "default", + }, + Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ + Function: &klcv1alpha3.FunctionSpec{ + HttpReference: klcv1alpha3.HttpReference{Url: "something"}, + FunctionReference: klcv1alpha3.FunctionReference{ + Name: "mytaskdef"}, + Parameters: klcv1alpha3.TaskParameters{ + Inline: map[string]string{"DATA1": "user"}, + }, + SecureParameters: klcv1alpha3.SecureParameters{ + Secret: "pw", + }, + }, + }, + } + + tests := []struct { + name string + options BuilderOptions + params *FunctionExecutionParams + wantErr bool + err string + }{ + { + name: "definition exists, no parent", + options: BuilderOptions{ + Client: fake.NewClient(def), + recorder: &record.FakeRecorder{}, + req: ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: "default"}, + }, + Log: testr.New(t), + taskDef: def, + task: makeTask("myt2", "default", def.Name), + }, + params: &FunctionExecutionParams{ + ConfigMap: "", + Parameters: map[string]string{ + "DATA2": "mydata2", + }, + SecureParameters: "mysecret2", + URL: "donothing", + Context: klcv1alpha3.TaskContext{ + WorkloadName: "my-workload", + AppName: "my-app", + ObjectType: "Workload"}, + }, + wantErr: false, + }, + { + name: "definition exists, parent with parameters and secrets", + options: BuilderOptions{ + Client: fake.NewClient(paramDef, def), + recorder: &record.FakeRecorder{}, + req: ctrl.Request{ + NamespacedName: types.NamespacedName{Namespace: "default"}, + }, + Log: testr.New(t), + taskDef: paramDef, + task: makeTask("myt3", "default", paramDef.Name), + }, + params: &FunctionExecutionParams{ + ConfigMap: "", + Parameters: map[string]string{ //maps should be merged + "DATA2": "mydata2", + "DATA1": "user", + }, + URL: "something", //we support a single URL so the original should be taken not the parent one + SecureParameters: "pw", //we support a single secret so the original task secret should be taken not the parent one + Context: klcv1alpha3.TaskContext{ + WorkloadName: "my-workload", + AppName: "my-app", + ObjectType: "Workload"}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + js := &JSBuilder{ + options: tt.options, + } + params, err := js.getParams(context.TODO()) + if !tt.wantErr { + require.Nil(t, err) + require.Equal(t, tt.params, params) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.err) + } + + }) + } +} diff --git a/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go b/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go index 321dda631f..6f1f612e82 100644 --- a/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go +++ b/operator/controllers/lifecycle/keptntaskdefinition/reconcile_function.go @@ -18,6 +18,9 @@ import ( ) func (r *KeptnTaskDefinitionReconciler) reconcileFunction(ctx context.Context, req ctrl.Request, definition *klcv1alpha3.KeptnTaskDefinition) error { + if !definition.IsJSSpecDefined() { + return nil + } if definition.Spec.Function.Inline != (klcv1alpha3.Inline{}) { err := r.reconcileFunctionInline(ctx, req, definition) if err != nil { diff --git a/operator/controllers/lifecycle/keptnworkloadinstance/controller.go b/operator/controllers/lifecycle/keptnworkloadinstance/controller.go index aa923a67b6..4548caf6fa 100644 --- a/operator/controllers/lifecycle/keptnworkloadinstance/controller.go +++ b/operator/controllers/lifecycle/keptnworkloadinstance/controller.go @@ -28,6 +28,7 @@ import ( controllererrors "github.com/keptn/lifecycle-toolkit/operator/controllers/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/api/errors" @@ -195,7 +196,7 @@ func (r *KeptnWorkloadInstanceReconciler) finishKeptnWorkloadInstanceReconcile(c // metrics: add deployment duration duration := workloadInstance.Status.EndTime.Time.Sub(workloadInstance.Status.StartTime.Time) - r.Meters.DeploymentDuration.Record(ctx, duration.Seconds(), attrs...) + r.Meters.DeploymentDuration.Record(ctx, duration.Seconds(), metric.WithAttributes(attrs...)) spanWorkloadTrace.AddEvent(workloadInstance.Name + " has finished") spanWorkloadTrace.SetStatus(codes.Ok, "Finished") @@ -211,6 +212,12 @@ func (r *KeptnWorkloadInstanceReconciler) finishKeptnWorkloadInstanceReconcile(c // SetupWithManager sets up the controller with the Manager. func (r *KeptnWorkloadInstanceReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &klcv1alpha3.KeptnWorkloadInstance{}, "spec.app", func(rawObj client.Object) []string { + workloadInstance := rawObj.(*klcv1alpha3.KeptnWorkloadInstance) + return []string{workloadInstance.Spec.AppName} + }); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). // predicate disabling the auto reconciliation after updating the object status For(&klcv1alpha3.KeptnWorkloadInstance{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). @@ -236,7 +243,7 @@ func (r *KeptnWorkloadInstanceReconciler) setupSpansContexts(ctx context.Context if workloadInstance.IsEndTimeSet() { r.Log.Info("Increasing deployment count") attrs := workloadInstance.GetMetricsAttributes() - r.Meters.DeploymentCount.Add(ctx, 1, attrs...) + r.Meters.DeploymentCount.Add(ctx, 1, metric.WithAttributes(attrs...)) } span.End() } @@ -305,6 +312,7 @@ func (r *KeptnWorkloadInstanceReconciler) getAppVersionForWorkloadInstance(ctx c return false, latestVersion, err } + // If the latest version is empty or the workload is not found, return false and empty result if latestVersion.Spec.Version == "" || !workloadFound { return false, klcv1alpha3.KeptnAppVersion{}, nil } diff --git a/operator/go.mod b/operator/go.mod index 84da008aeb..0bf496f094 100644 --- a/operator/go.mod +++ b/operator/go.mod @@ -3,42 +3,42 @@ module github.com/keptn/lifecycle-toolkit/operator go 1.20 require ( - github.com/argoproj/argo-rollouts v1.4.1 - github.com/benbjohnson/clock v1.3.3 + github.com/argoproj/argo-rollouts v1.5.0 + github.com/benbjohnson/clock v1.3.5 github.com/go-logr/logr v1.2.4 github.com/imdario/mergo v0.3.15 github.com/kelseyhightower/envconfig v1.4.0 - github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230413082525-dd15d4a0e0e4 + github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0 + github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230523140051-57fdcddcf73c github.com/magiconair/properties v1.8.7 - github.com/onsi/ginkgo/v2 v2.9.2 - github.com/onsi/gomega v1.27.6 + github.com/onsi/ginkgo/v2 v2.9.5 + github.com/onsi/gomega v1.27.7 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.15.0 - github.com/spf13/afero v1.9.5 - github.com/stretchr/testify v1.8.2 - go.opentelemetry.io/otel v1.11.2 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 - go.opentelemetry.io/otel/exporters/prometheus v0.34.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2 - go.opentelemetry.io/otel/metric v0.34.0 - go.opentelemetry.io/otel/sdk v1.11.2 - go.opentelemetry.io/otel/sdk/metric v0.34.0 - go.opentelemetry.io/otel/trace v1.11.2 - google.golang.org/grpc v1.54.0 - k8s.io/api v0.26.4 - k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery v0.26.4 - k8s.io/apiserver v0.26.4 - k8s.io/client-go v0.26.4 + github.com/prometheus/client_golang v1.15.1 + github.com/stretchr/testify v1.8.3 + go.opentelemetry.io/otel v1.15.1 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1 + go.opentelemetry.io/otel/exporters/prometheus v0.38.1 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 + go.opentelemetry.io/otel/metric v0.38.1 + go.opentelemetry.io/otel/sdk v1.15.1 + go.opentelemetry.io/otel/sdk/metric v0.38.1 + go.opentelemetry.io/otel/trace v1.15.1 + google.golang.org/grpc v1.54.1 + k8s.io/api v0.26.5 + k8s.io/apiextensions-apiserver v0.26.5 + k8s.io/apimachinery v0.26.5 + k8s.io/apiserver v0.26.5 + k8s.io/client-go v0.26.5 sigs.k8s.io/controller-runtime v0.14.6 ) require ( github.com/prometheus/common v0.42.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -47,7 +47,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect @@ -79,24 +79,24 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/spf13/afero v1.9.5 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/component-base v0.26.4 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + k8s.io/component-base v0.26.5 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/operator/go.sum b/operator/go.sum index 1511b2ec8b..9c8fd31910 100644 --- a/operator/go.sum +++ b/operator/go.sum @@ -40,16 +40,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/argoproj/argo-rollouts v1.4.1 h1:P+aTqdjMmWJDJfAbyVkCbONIzoGXSRVRBvim6VWxMJo= -github.com/argoproj/argo-rollouts v1.4.1/go.mod h1:KR9pcBicOYmPOu50bBLRQfp/UQVkRGoUkidHVsyjV1Q= +github.com/argoproj/argo-rollouts v1.5.0 h1:asKpzMuFSDGsXK5cGsbH/TGXUJD/v1mvM1D3o5Xwz34= +github.com/argoproj/argo-rollouts v1.5.0/go.mod h1:OaOf+oZawsss6fy+9WEDy4IaSbwuRteBj1X2QiVfqdA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= -github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -206,8 +206,10 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230413082525-dd15d4a0e0e4 h1:LI+iOb7v1zIAtHQum79CbV+4HB1PCAim+TuCCRRsW7o= -github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230413082525-dd15d4a0e0e4/go.mod h1:8rQ1flqblBWy43k4xJnoaMUA7e50zP95QIab3z6NCw4= +github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0 h1:utqqOMPptCXMBUaU9oRBsXAQE1US5Bz9fbXxzOAk1YI= +github.com/keptn/lifecycle-toolkit/klt-cert-manager v0.0.0-20230523133947-65b41399b2e0/go.mod h1:sZUFv7ZVPq4cMxcMQc77vjhEtem48BzV6fHbmXdVjQg= +github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230523140051-57fdcddcf73c h1:to6Fy5UHqL1qL+2C5/kSNst0O2saTU1Zp4tE3ibv3CQ= +github.com/keptn/lifecycle-toolkit/metrics-operator v0.0.0-20230523140051-57fdcddcf73c/go.mod h1:47Sx5OCHdEkQmU55/ymmOYw3me5nUAMgEPeaO84AP0E= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -232,18 +234,18 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= @@ -254,7 +256,6 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= @@ -262,17 +263,13 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -287,26 +284,26 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= -go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2 h1:htgM8vZIF8oPSCxa341e3IZ4yr/sKxgu8KZYllByiVY= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.2/go.mod h1:rqbht/LlhVBgn5+k3M5QK96K5Xb0DvXpMJ5SFQpY6uw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2 h1:fqR1kli93643au1RKo0Uma3d2aPQKT+WBKfTSBaKbOc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.2/go.mod h1:5Qn6qvgkMsLDX+sYK64rHb1FPhpn0UtxF+ouX1uhyJE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2 h1:ERwKPn9Aer7Gxsc0+ZlutlH1bEEAUXAUhqm3Y45ABbk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.2/go.mod h1:jWZUM2MWhWCJ9J9xVbRx7tzK1mXKpAlze4CeulycwVY= -go.opentelemetry.io/otel/exporters/prometheus v0.34.0 h1:L5D+HxdaC/ORB47ribbTBbkXRZs9JzPjq0EoIOMWncM= -go.opentelemetry.io/otel/exporters/prometheus v0.34.0/go.mod h1:6gUoJyfhoWqF0tOLaY0ZmKgkQRcvEQx6p5rVlKHp3s4= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2 h1:BhEVgvuE1NWLLuMLvC6sif791F45KFHi5GhOs1KunZU= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.11.2/go.mod h1:bx//lU66dPzNT+Y0hHA12ciKoMOH9iixEwCqC1OeQWQ= -go.opentelemetry.io/otel/metric v0.34.0 h1:MCPoQxcg/26EuuJwpYN1mZTeCYAUGx8ABxfW07YkjP8= -go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2bR7bw5JqUm/AP8= -go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= -go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= -go.opentelemetry.io/otel/sdk/metric v0.34.0 h1:7ElxfQpXCFZlRTvVRTkcUvK8Gt5DC8QzmzsLsO2gdzo= -go.opentelemetry.io/otel/sdk/metric v0.34.0/go.mod h1:l4r16BIqiqPy5rd14kkxllPy/fOI4tWo1jkpD9Z3ffQ= -go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= -go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= +go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8= +go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 h1:XYDQtNzdb2T4uM1pku2m76eSMDJgqhJ+6KzkqgQBALc= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1/go.mod h1:uOTV75+LOzV+ODmL8ahRLWkFA3eQcSC2aAsbxIu4duk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 h1:tyoeaUh8REKay72DVYsSEBYV18+fGONe+YYPaOxgLoE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1/go.mod h1:HUSnrjQQ19KX9ECjpQxufsF+3ioD3zISPMlauTPZu2g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1 h1:pIfoG5IAZFzp9EUlJzdSkpUwpaUAAnD+Ru1nBLTACIQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1/go.mod h1:poNKBqF5+nR/6ke2oGTDjHfksrsHDOHXAl2g4+9ONsY= +go.opentelemetry.io/otel/exporters/prometheus v0.38.1 h1:GwalIvFIx91qIA8qyAyqYj9lql5Ba2Oxj/jDG6+3UoU= +go.opentelemetry.io/otel/exporters/prometheus v0.38.1/go.mod h1:6K7aBvWHXRUcNYFSj6Hi5hHwzA1jYflG/T8snrX4dYM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 h1:2PunuO5SbkN5MhCbuHCd3tC6qrcaj+uDAkX/qBU5BAs= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1/go.mod h1:q8+Tha+5LThjeSU8BW93uUC5w5/+DnYHMKBMpRCsui0= +go.opentelemetry.io/otel/metric v0.38.1 h1:2MM7m6wPw9B8Qv8iHygoAgkbejed59uUR6ezR5T3X2s= +go.opentelemetry.io/otel/metric v0.38.1/go.mod h1:FwqNHD3I/5iX9pfrRGZIlYICrJv0rHEUl2Ln5vdIVnQ= +go.opentelemetry.io/otel/sdk v1.15.1 h1:5FKR+skgpzvhPQHIEfcwMYjCBr14LWzs3uSqKiQzETI= +go.opentelemetry.io/otel/sdk v1.15.1/go.mod h1:8rVtxQfrbmbHKfqzpQkT5EzZMcbMBwTzNAggbEAM0KA= +go.opentelemetry.io/otel/sdk/metric v0.38.1 h1:EkO5wI4NT/fUaoPMGc0fKV28JaWe7q4vfVpEVasGb+8= +go.opentelemetry.io/otel/sdk/metric v0.38.1/go.mod h1:Rn4kSXFF9ZQZ5lL1pxQjCbK4seiO+U7s0ncmIFJaj34= +go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY= +go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -314,7 +311,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -362,6 +359,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -425,8 +423,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= @@ -498,8 +497,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -594,8 +593,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.54.1 h1:zQZQNqQZU9cHv2vLdDhB2mFeDZ2hGpgYM1A0PKjFsSM= +google.golang.org/grpc v1.54.1/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -635,24 +634,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= -k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= -k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= -k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= -k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= -k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/apiserver v0.26.4 h1:3Oq4mnJv0mzVX7BR/Nod+8KjlELf/3Ljvu9ZWDyLUoA= -k8s.io/apiserver v0.26.4/go.mod h1:yAY3O1vBM4/0OIGAGeWcdfzQvgdwJ188VirLcuSAVnw= -k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= -k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= -k8s.io/component-base v0.26.4 h1:Bg2xzyXNKL3eAuiTEu3XE198d6z22ENgFgGQv2GGOUk= -k8s.io/component-base v0.26.4/go.mod h1:lTuWL1Xz/a4e80gmIC3YZG2JCO4xNwtKWHJWeJmsq20= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/api v0.26.5 h1:Npao/+sMSng6nkEcNydgH3BNo4s5YoBg7iw35HM7Hcw= +k8s.io/api v0.26.5/go.mod h1:O7ICW7lj6+ZQQQ3cxekgCoW+fnGo5kWT0nTHkLZ5grc= +k8s.io/apiextensions-apiserver v0.26.5 h1:VJ946z9RjyCPn3qiz4Kus/UYjCRrdn1xUvEsJFvN5Yo= +k8s.io/apiextensions-apiserver v0.26.5/go.mod h1:Olsde7ZNWnyz9rsL13iXYXmL1h7kWujtKeC3yWVCDPo= +k8s.io/apimachinery v0.26.5 h1:hTQVhJao2piX7vSgCn4Lwd6E0o/+TJIH4NqRf+q4EmE= +k8s.io/apimachinery v0.26.5/go.mod h1:HUvk6wrOP4v22AIYqeCGSQ6xWCHo41J9d6psb3temAg= +k8s.io/apiserver v0.26.5 h1:SBzyDpIXXPR4v+mpSU44p9fQerBMkpOH6lmSPCD1wmo= +k8s.io/apiserver v0.26.5/go.mod h1:OSbw98Y1bDSbA2izYIKqhi10vb4KWP9b4siiCRFkBVE= +k8s.io/client-go v0.26.5 h1:e8Z44pafL/c6ayF/6qYEypbJoDSakaFxhJ9lqULEJEo= +k8s.io/client-go v0.26.5/go.mod h1:/CYyNt+ZLMvWqMF8h1SvkUXz2ujFWQLwdDrdiQlZ5X0= +k8s.io/component-base v0.26.5 h1:nHAzDvXQ4whYpOqrQGWrDIYI/GIeXkuxzqC/iVICfZo= +k8s.io/component-base v0.26.5/go.mod h1:wvfNAS05EtKdPeUxFceo8WNh8bGPcFY8QfPhv5MYjA4= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/operator/main.go b/operator/main.go index f022405e00..3c1b5f2378 100644 --- a/operator/main.go +++ b/operator/main.go @@ -25,13 +25,14 @@ import ( argov1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/kelseyhightower/envconfig" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/certificates" + certCommon "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/common" + "github.com/keptn/lifecycle-toolkit/klt-cert-manager/pkg/webhook" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" lifecyclev1alpha1 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha1" lifecyclev1alpha2 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha2" lifecyclev1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" optionsv1alpha1 "github.com/keptn/lifecycle-toolkit/operator/apis/options/v1alpha1" - cmdConfig "github.com/keptn/lifecycle-toolkit/operator/cmd/config" - "github.com/keptn/lifecycle-toolkit/operator/cmd/webhook" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/keptnapp" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/keptnappcreationrequest" @@ -42,7 +43,9 @@ import ( "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/keptnworkload" "github.com/keptn/lifecycle-toolkit/operator/controllers/lifecycle/keptnworkloadinstance" controlleroptions "github.com/keptn/lifecycle-toolkit/operator/controllers/options" + "github.com/keptn/lifecycle-toolkit/operator/webhooks/pod_mutator" "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel" otelprom "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/sdk/metric" corev1 "k8s.io/api/core/v1" @@ -53,6 +56,7 @@ import ( ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + ctrlWebhook "sigs.k8s.io/controller-runtime/pkg/webhook" ) var ( @@ -318,10 +322,31 @@ func main() { webhookBuilder := webhook.NewWebhookBuilder(). SetNamespace(env.PodNamespace). SetPodName(env.PodName). - SetConfigProvider(cmdConfig.NewKubeConfigProvider()) + SetManagerProvider( + webhook.NewWebhookManagerProvider( + mgr.GetWebhookServer().CertDir, "tls.key", "tls.crt"), + ). + SetCertificateWatcher( + certificates.NewCertificateWatcher( + mgr.GetAPIReader(), + mgr.GetWebhookServer().CertDir, + env.PodNamespace, + certCommon.SecretName, + setupLog, + ), + ) setupLog.Info("starting webhook and manager") - if err1 := webhookBuilder.Run(mgr); err1 != nil { + if err := webhookBuilder.Run(mgr, map[string]*ctrlWebhook.Admission{ + "/mutate-v1-pod": { + Handler: &pod_mutator.PodMutatingWebhook{ + Client: mgr.GetClient(), + Tracer: otel.Tracer("keptn/webhook"), + Recorder: mgr.GetEventRecorderFor("keptn/webhook"), + Log: ctrl.Log.WithName("Mutating Webhook"), + }, + }, + }); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) } diff --git a/operator/test/component/common/common.go b/operator/test/component/common/common.go index 14e1737e49..3f78d1408a 100644 --- a/operator/test/component/common/common.go +++ b/operator/test/component/common/common.go @@ -8,7 +8,7 @@ import ( "strings" "time" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" @@ -16,9 +16,8 @@ import ( ginkgotypes "github.com/onsi/ginkgo/v2/types" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" - "go.opentelemetry.io/otel/metric/instrument" - "go.opentelemetry.io/otel/metric/unit" - "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" otelsdk "go.opentelemetry.io/otel/sdk/trace" sdktest "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" @@ -36,16 +35,16 @@ import ( ) func InitKeptnMeters() apicommon.KeptnMeters { - provider := metric.NewMeterProvider() + provider := sdkmetric.NewMeterProvider() meter := provider.Meter("keptn/task") - deploymentCount, _ := meter.SyncInt64().Counter("keptn.deployment.count", instrument.WithDescription("a simple counter for Keptn Deployments")) - deploymentDuration, _ := meter.SyncFloat64().Histogram("keptn.deployment.duration", instrument.WithDescription("a histogram of duration for Keptn Deployments"), instrument.WithUnit(unit.Unit("s"))) - taskCount, _ := meter.SyncInt64().Counter("keptn.task.count", instrument.WithDescription("a simple counter for Keptn Tasks")) - taskDuration, _ := meter.SyncFloat64().Histogram("keptn.task.duration", instrument.WithDescription("a histogram of duration for Keptn Tasks"), instrument.WithUnit(unit.Unit("s"))) - appCount, _ := meter.SyncInt64().Counter("keptn.app.count", instrument.WithDescription("a simple counter for Keptn Apps")) - appDuration, _ := meter.SyncFloat64().Histogram("keptn.app.duration", instrument.WithDescription("a histogram of duration for Keptn Apps"), instrument.WithUnit(unit.Unit("s"))) - evaluationCount, _ := meter.SyncInt64().Counter("keptn.evaluation.count", instrument.WithDescription("a simple counter for Keptn Evaluations")) - evaluationDuration, _ := meter.SyncFloat64().Histogram("keptn.evaluation.duration", instrument.WithDescription("a histogram of duration for Keptn Evaluations"), instrument.WithUnit(unit.Unit("s"))) + deploymentCount, _ := meter.Int64Counter("keptn.deployment.count", metric.WithDescription("a simple counter for Keptn Deployments")) + deploymentDuration, _ := meter.Float64Histogram("keptn.deployment.duration", metric.WithDescription("a histogram of duration for Keptn Deployments"), metric.WithUnit("s")) + taskCount, _ := meter.Int64Counter("keptn.task.count", metric.WithDescription("a simple counter for Keptn Tasks")) + taskDuration, _ := meter.Float64Histogram("keptn.task.duration", metric.WithDescription("a histogram of duration for Keptn Tasks"), metric.WithUnit("s")) + appCount, _ := meter.Int64Counter("keptn.app.count", metric.WithDescription("a simple counter for Keptn Apps")) + appDuration, _ := meter.Float64Histogram("keptn.app.duration", metric.WithDescription("a histogram of duration for Keptn Apps"), metric.WithUnit("s")) + evaluationCount, _ := meter.Int64Counter("keptn.evaluation.count", metric.WithDescription("a simple counter for Keptn Evaluations")) + evaluationDuration, _ := meter.Float64Histogram("keptn.evaluation.duration", metric.WithDescription("a histogram of duration for Keptn Evaluations"), metric.WithUnit("s")) meters := apicommon.KeptnMeters{ TaskCount: taskCount, diff --git a/operator/test/component/evaluation/evaluation_test.go b/operator/test/component/evaluation/evaluation_test.go index ba8e41e4f8..9cef296874 100644 --- a/operator/test/component/evaluation/evaluation_test.go +++ b/operator/test/component/evaluation/evaluation_test.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha2" + metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3" klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" diff --git a/operator/test/component/task/task_test.go b/operator/test/component/task/task_test.go index 9954f9f7d2..883709320d 100644 --- a/operator/test/component/task/task_test.go +++ b/operator/test/component/task/task_test.go @@ -156,17 +156,17 @@ var _ = Describe("Task", Ordered, func() { Expect(err).To(BeNil()) Expect(createdJob.Annotations).To(Equal(map[string]string{ - "annotation1": "annotation2", - })) - - Expect(createdJob.Labels).To(Equal(map[string]string{ + "annotation1": "annotation2", "keptn.sh/task-name": task.Name, "keptn.sh/version": "", "keptn.sh/workload": "my-workload", - "label1": "label2", "keptn.sh/app": "my-app", })) + Expect(createdJob.Labels).To(Equal(map[string]string{ + "label1": "label2", + })) + val, ok := createdJob.Spec.Template.Labels["label1"] Expect(ok && val == "label2").To(BeTrue()) @@ -237,7 +237,7 @@ func makeTaskDefinition(taskDefinitionName, namespace string) *klcv1alpha3.Keptn Namespace: namespace, }, Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ - Function: klcv1alpha3.FunctionSpec{ + Function: &klcv1alpha3.FunctionSpec{ ConfigMapReference: klcv1alpha3.ConfigMapReference{ Name: cmName, }, diff --git a/operator/test/component/taskdefinition/taskdefinition_test.go b/operator/test/component/taskdefinition/taskdefinition_test.go index adc55b5f84..da33ac77a6 100644 --- a/operator/test/component/taskdefinition/taskdefinition_test.go +++ b/operator/test/component/taskdefinition/taskdefinition_test.go @@ -41,7 +41,7 @@ var _ = Describe("Taskdefinition", Ordered, func() { Namespace: namespace, }, Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ - Function: klcv1alpha3.FunctionSpec{ + Function: &klcv1alpha3.FunctionSpec{ Inline: klcv1alpha3.Inline{ Code: "console.log(Hello);", }, @@ -90,7 +90,7 @@ var _ = Describe("Taskdefinition", Ordered, func() { Namespace: namespace, }, Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ - Function: klcv1alpha3.FunctionSpec{ + Function: &klcv1alpha3.FunctionSpec{ ConfigMapReference: klcv1alpha3.ConfigMapReference{ Name: "my-configmap", }, @@ -141,7 +141,7 @@ var _ = Describe("Taskdefinition", Ordered, func() { Namespace: namespace, }, Spec: klcv1alpha3.KeptnTaskDefinitionSpec{ - Function: klcv1alpha3.FunctionSpec{ + Function: &klcv1alpha3.FunctionSpec{ ConfigMapReference: klcv1alpha3.ConfigMapReference{ Name: "my-configmap-non-existing", }, diff --git a/operator/webhooks/pod_mutator/pod_mutating_webhook.go b/operator/webhooks/pod_mutator/pod_mutating_webhook.go index 0d7d4a65ef..447a4dbc77 100644 --- a/operator/webhooks/pod_mutator/pod_mutating_webhook.go +++ b/operator/webhooks/pod_mutator/pod_mutating_webhook.go @@ -14,6 +14,7 @@ import ( klcv1alpha3 "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3" apicommon "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/common" "github.com/keptn/lifecycle-toolkit/operator/apis/lifecycle/v1alpha3/semconv" + operatorcommon "github.com/keptn/lifecycle-toolkit/operator/common" controllercommon "github.com/keptn/lifecycle-toolkit/operator/controllers/common" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" @@ -83,21 +84,12 @@ func (a *PodMutatingWebhook) Handle(ctx context.Context, req admission.Request) logger.Info(fmt.Sprintf("Pod annotations: %v", pod.Annotations)) - podIsAnnotated, err := a.isPodAnnotated(pod) + podIsAnnotated := a.isPodAnnotated(pod) logger.Info("Checked if pod is annotated.") - if err != nil { - span.SetStatus(codes.Error, InvalidAnnotationMessage) - return admission.Errored(http.StatusBadRequest, err) - } - if !podIsAnnotated { logger.Info("Pod is not annotated, check for parent annotations...") - podIsAnnotated, err = a.copyAnnotationsIfParentAnnotated(ctx, &req, pod) - if err != nil { - span.SetStatus(codes.Error, InvalidAnnotationMessage) - return admission.Errored(http.StatusBadRequest, err) - } + podIsAnnotated = a.copyAnnotationsIfParentAnnotated(ctx, &req, pod) } if podIsAnnotated { @@ -105,11 +97,7 @@ 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) - if err != nil { - span.SetStatus(codes.Error, InvalidAnnotationMessage) - return admission.Errored(http.StatusBadRequest, err) - } + isAppAnnotationPresent := a.isAppAnnotationPresent(pod) semconv.AddAttributeFromAnnotations(span, pod.Annotations) logger.Info("Attributes from annotations set") @@ -144,13 +132,9 @@ func (a *PodMutatingWebhook) InjectDecoder(d *admission.Decoder) error { return nil } -func (a *PodMutatingWebhook) isPodAnnotated(pod *corev1.Pod) (bool, error) { - workload, gotWorkloadAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.WorkloadAnnotation, apicommon.K8sRecommendedWorkloadAnnotations) - version, gotVersionAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.VersionAnnotation, apicommon.K8sRecommendedVersionAnnotations) - - if len(workload) > apicommon.MaxWorkloadNameLength || len(version) > apicommon.MaxVersionLength { - return false, ErrTooLongAnnotations - } +func (a *PodMutatingWebhook) isPodAnnotated(pod *corev1.Pod) bool { + _, gotWorkloadAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.WorkloadAnnotation, apicommon.K8sRecommendedWorkloadAnnotations) + _, gotVersionAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.VersionAnnotation, apicommon.K8sRecommendedVersionAnnotations) if gotWorkloadAnnotation { if !gotVersionAnnotation { @@ -159,28 +143,28 @@ func (a *PodMutatingWebhook) isPodAnnotated(pod *corev1.Pod) (bool, error) { } pod.Annotations[apicommon.VersionAnnotation] = a.calculateVersion(pod) } - return true, nil + return true } - return false, nil + return false } -func (a *PodMutatingWebhook) copyAnnotationsIfParentAnnotated(ctx context.Context, req *admission.Request, pod *corev1.Pod) (bool, error) { +func (a *PodMutatingWebhook) copyAnnotationsIfParentAnnotated(ctx context.Context, req *admission.Request, pod *corev1.Pod) bool { podOwner := a.getOwnerReference(&pod.ObjectMeta) if podOwner.UID == "" { - return false, nil + return false } switch podOwner.Kind { case "ReplicaSet": rs := &appsv1.ReplicaSet{} if err := a.Client.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: podOwner.Name}, rs); err != nil { - return false, nil + return false } a.Log.Info("Done fetching RS") rsOwner := a.getOwnerReference(&rs.ObjectMeta) if rsOwner.UID == "" { - return false, nil + return false } if rsOwner.Kind == "Rollout" { @@ -197,13 +181,13 @@ func (a *PodMutatingWebhook) copyAnnotationsIfParentAnnotated(ctx context.Contex ds := &appsv1.DaemonSet{} return a.fetchParentObjectAndCopyLabels(ctx, podOwner.Name, req.Namespace, pod, ds) default: - return false, nil + return false } } -func (a *PodMutatingWebhook) fetchParentObjectAndCopyLabels(ctx context.Context, name string, namespace string, pod *corev1.Pod, objectContainer client.Object) (bool, error) { +func (a *PodMutatingWebhook) fetchParentObjectAndCopyLabels(ctx context.Context, name string, namespace string, pod *corev1.Pod, objectContainer client.Object) bool { if err := a.Client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, objectContainer); err != nil { - return false, nil + return false } objectContainerMetaData := metav1.ObjectMeta{ Labels: objectContainer.GetLabels(), @@ -212,7 +196,7 @@ func (a *PodMutatingWebhook) fetchParentObjectAndCopyLabels(ctx context.Context, return a.copyResourceLabelsIfPresent(&objectContainerMetaData, pod) } -func (a *PodMutatingWebhook) copyResourceLabelsIfPresent(sourceResource *metav1.ObjectMeta, targetPod *corev1.Pod) (bool, error) { +func (a *PodMutatingWebhook) copyResourceLabelsIfPresent(sourceResource *metav1.ObjectMeta, targetPod *corev1.Pod) bool { var workloadName, appName, version, preDeploymentChecks, postDeploymentChecks, preEvaluationChecks, postEvaluationChecks string var gotWorkloadName, gotVersion bool @@ -224,10 +208,6 @@ func (a *PodMutatingWebhook) copyResourceLabelsIfPresent(sourceResource *metav1. preEvaluationChecks, _ = getLabelOrAnnotation(sourceResource, apicommon.PreDeploymentEvaluationAnnotation, "") postEvaluationChecks, _ = getLabelOrAnnotation(sourceResource, apicommon.PostDeploymentEvaluationAnnotation, "") - if len(workloadName) > apicommon.MaxWorkloadNameLength || len(version) > apicommon.MaxVersionLength { - return false, ErrTooLongAnnotations - } - if len(targetPod.Annotations) == 0 { targetPod.Annotations = make(map[string]string) } @@ -247,26 +227,23 @@ func (a *PodMutatingWebhook) copyResourceLabelsIfPresent(sourceResource *metav1. setMapKey(targetPod.Annotations, apicommon.PreDeploymentEvaluationAnnotation, preEvaluationChecks) setMapKey(targetPod.Annotations, apicommon.PostDeploymentEvaluationAnnotation, postEvaluationChecks) - return true, nil + return true } - return false, nil + return false } -func (a *PodMutatingWebhook) isAppAnnotationPresent(pod *corev1.Pod) (bool, error) { - app, gotAppAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.AppAnnotation, apicommon.K8sRecommendedAppAnnotations) +func (a *PodMutatingWebhook) isAppAnnotationPresent(pod *corev1.Pod) bool { + _, gotAppAnnotation := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.AppAnnotation, apicommon.K8sRecommendedAppAnnotations) if gotAppAnnotation { - if len(app) > apicommon.MaxAppNameLength { - return false, ErrTooLongAnnotations - } - return true, nil + return true } if len(pod.Annotations) == 0 { pod.Annotations = make(map[string]string) } pod.Annotations[apicommon.AppAnnotation], _ = getLabelOrAnnotation(&pod.ObjectMeta, apicommon.WorkloadAnnotation, apicommon.K8sRecommendedWorkloadAnnotations) - return false, nil + return false } func (a *PodMutatingWebhook) calculateVersion(pod *corev1.Pod) string { @@ -465,7 +442,7 @@ func (a *PodMutatingWebhook) generateAppCreationRequest(ctx context.Context, pod func (a *PodMutatingWebhook) getWorkloadName(pod *corev1.Pod) string { workloadName, _ := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.WorkloadAnnotation, apicommon.K8sRecommendedWorkloadAnnotations) applicationName, _ := getLabelOrAnnotation(&pod.ObjectMeta, apicommon.AppAnnotation, apicommon.K8sRecommendedAppAnnotations) - return strings.ToLower(applicationName + "-" + workloadName) + return operatorcommon.CreateResourceName(apicommon.MaxK8sObjectLength, apicommon.MinKLTNameLen, applicationName, workloadName) } func (a *PodMutatingWebhook) getAppName(pod *corev1.Pod) string { diff --git a/operator/webhooks/pod_mutator/pod_mutating_webhook_test.go b/operator/webhooks/pod_mutator/pod_mutating_webhook_test.go index af4da8548e..1babb0abd2 100644 --- a/operator/webhooks/pod_mutator/pod_mutating_webhook_test.go +++ b/operator/webhooks/pod_mutator/pod_mutating_webhook_test.go @@ -379,24 +379,8 @@ func TestPodMutatingWebhook_isPodAnnotated(t *testing.T) { fields fields args args want bool - wantErr bool wantedPod *corev1.Pod }{ - { - name: "Test error when workload name is too long", - args: args{ - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - apicommon.AppAnnotation: "SOME-APP-NAME-ANNOTATION", - apicommon.WorkloadAnnotation: "workload-name-that-is-too-loooooooooooooooooooooooooooooooooooooooooooooooooong", - }, - }, - }, - }, - want: false, - wantErr: true, - }, { name: "Test return true when pod has workload annotation", args: args{ @@ -408,8 +392,7 @@ func TestPodMutatingWebhook_isPodAnnotated(t *testing.T) { }, }, }, - want: true, - wantErr: false, + want: true, }, { name: "Test return true and initialize annotations when labels are set", @@ -429,8 +412,7 @@ func TestPodMutatingWebhook_isPodAnnotated(t *testing.T) { }, }, }, - want: true, - wantErr: false, + want: true, wantedPod: &corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -460,8 +442,7 @@ func TestPodMutatingWebhook_isPodAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, } for _, tt := range tests { @@ -473,11 +454,7 @@ func TestPodMutatingWebhook_isPodAnnotated(t *testing.T) { Recorder: tt.fields.Recorder, Log: tt.fields.Log, } - got, err := a.isPodAnnotated(tt.args.pod) - if (err != nil) != tt.wantErr { - t.Errorf("isPodAnnotated() error = %v, wantErr %v", err, tt.wantErr) - return - } + got := a.isPodAnnotated(tt.args.pod) if got != tt.want { t.Errorf("isPodAnnotated() got = %v, want %v", got, tt.want) } @@ -571,11 +548,10 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { pod *corev1.Pod } tests := []struct { - name string - fields fields - args args - want bool - wantErr bool + name string + fields fields + args args + want bool }{ { name: "Test that nothing happens if owner UID is pod UID", @@ -596,8 +572,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, { name: "Test fetching of replicaset owner of pod and deployment owner of replicaset", @@ -625,8 +600,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, { name: "Test fetching of statefulset owner of pod", @@ -654,8 +628,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, { name: "Test fetching of daemonset owner of pod", @@ -683,8 +656,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, { name: "Test that method returns without doing anything when we get a pod with replicaset without owner", @@ -712,8 +684,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, }, } for _, tt := range tests { @@ -725,11 +696,7 @@ func TestPodMutatingWebhook_copyAnnotationsIfParentAnnotated(t *testing.T) { Recorder: tt.fields.Recorder, Log: tt.fields.Log, } - got, err := a.copyAnnotationsIfParentAnnotated(tt.args.ctx, tt.args.req, tt.args.pod) - if (err != nil) != tt.wantErr { - t.Errorf("copyAnnotationsIfParentAnnotated() error = %v, wantErr %v", err, tt.wantErr) - return - } + got := a.copyAnnotationsIfParentAnnotated(tt.args.ctx, tt.args.req, tt.args.pod) if got != tt.want { t.Errorf("copyAnnotationsIfParentAnnotated() got = %v, want %v", got, tt.want) } @@ -754,7 +721,6 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { fields fields args args want bool - wantErr bool wantedPod *corev1.Pod }{ { @@ -779,8 +745,7 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { Status: corev1.PodStatus{}, }, }, - want: true, - wantErr: false, + want: true, wantedPod: &corev1.Pod{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -813,8 +778,7 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { }, targetPod: &corev1.Pod{}, }, - want: true, - wantErr: false, + want: true, wantedPod: &corev1.Pod{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -857,8 +821,7 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { Status: corev1.PodStatus{}, }, }, - want: true, - wantErr: false, + want: true, wantedPod: &corev1.Pod{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -882,20 +845,6 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { Status: corev1.PodStatus{}, }, }, - { - name: "Test that error is return with too long workload name", - args: args{ - sourceResource: &metav1.ObjectMeta{ - Name: "testSourceObject", - Labels: map[string]string{ - apicommon.WorkloadAnnotation: "some-workload-name-that-is-very-looooooooooooooooooooooong", - }, - }, - targetPod: &corev1.Pod{}, - }, - want: false, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -906,11 +855,7 @@ func TestPodMutatingWebhook_copyResourceLabelsIfPresent(t *testing.T) { Recorder: tt.fields.Recorder, Log: tt.fields.Log, } - got, err := a.copyResourceLabelsIfPresent(tt.args.sourceResource, tt.args.targetPod) - if (err != nil) != tt.wantErr { - t.Errorf("copyResourceLabelsIfPresent() error = %v, wantErr %v", err, tt.wantErr) - return - } + got := a.copyResourceLabelsIfPresent(tt.args.sourceResource, tt.args.targetPod) if got != tt.want { t.Errorf("copyResourceLabelsIfPresent() got = %v, want %v", got, tt.want) } @@ -937,7 +882,6 @@ func TestPodMutatingWebhook_isAppAnnotationPresent(t *testing.T) { fields fields args args want bool - wantErr bool wantedPod *corev1.Pod }{ { @@ -951,30 +895,14 @@ func TestPodMutatingWebhook_isAppAnnotationPresent(t *testing.T) { }, }, }, - want: true, - wantErr: false, + want: true, }, { name: "Test return false when app annotation is not present", args: args{ pod: &corev1.Pod{}, }, - want: false, - wantErr: false, - }, - { - name: "Test return error when app annotation is too long", - args: args{ - pod: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - apicommon.AppAnnotation: "some-app-annotation-that-is-very-looooooooooooooooooooong", - }, - }, - }, - }, - want: false, - wantErr: true, + want: false, }, { name: "Test that app name is copied when only workload name is present", @@ -987,8 +915,7 @@ func TestPodMutatingWebhook_isAppAnnotationPresent(t *testing.T) { }, }, }, - want: false, - wantErr: false, + want: false, wantedPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -1008,11 +935,7 @@ func TestPodMutatingWebhook_isAppAnnotationPresent(t *testing.T) { Recorder: tt.fields.Recorder, Log: tt.fields.Log, } - got, err := a.isAppAnnotationPresent(tt.args.pod) - if (err != nil) != tt.wantErr { - t.Errorf("isAppAnnotationPresent() error = %v, wantErr %v", err, tt.wantErr) - return - } + got := a.isAppAnnotationPresent(tt.args.pod) if got != tt.want { t.Errorf("isAppAnnotationPresent() got = %v, want %v", got, tt.want) } diff --git a/python-runtime/Dockerfile b/python-runtime/Dockerfile new file mode 100644 index 0000000000..062f7301a4 --- /dev/null +++ b/python-runtime/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.9 AS production + +LABEL org.opencontainers.image.source="https://github.com/keptn/lifecycle-toolkit" \ + org.opencontainers.image.url="https://keptn.sh" \ + org.opencontainers.image.title="Keptn Python Runtime" \ + org.opencontainers.image.vendor="Keptn" \ + org.opencontainers.image.licenses="Apache-2.0" + +RUN pip install -q --disable-pip-version-check pyyaml GitPython requests + +COPY entrypoint.sh /entrypoint.sh + +USER 1000:1000 + +ENV CMD_ARGS="" +ENV SCRIPT="" + +ENTRYPOINT /entrypoint.sh diff --git a/python-runtime/README.md b/python-runtime/README.md new file mode 100644 index 0000000000..506be18caa --- /dev/null +++ b/python-runtime/README.md @@ -0,0 +1,68 @@ +# Keptn Lifecycle Controller - Function Runtime + +## Build + +```shell +docker build -t lifecycle-toolkit/python-runtime:${VERSION} . +``` + +## Usage + +The Keptn python runtime uses python3, and enables the follwing packages: requests, json, git, yaml + +The Keptn Lifecycle Toolkit uses this runtime to run [KeptnTask](https://lifecycle.keptn.sh/docs/tasks/write-tasks/) +for pre- and post-checks. + +`KeptnTask`s can be tested locally with the runtime using the following commands. +Replace `${VERSION}` with the KLT version of your choice. +`SCRIPT` should refer to either a python file mounted locally in the container or to a url containing the file. + +### mounting a python file + +```shell +docker run -v $(pwd)/samples/hellopy.py:/hellopy.py -e "SCRIPT=hellopy.py" -it lifecycle-toolkit/python-runtime:${VERSION} +``` + +Where the file in sample/hellopy.py contains python3 code: + +```python3 +import os + +print("Hello, World!") +print(os.environ) +``` + +This should print in your shell, something like: + +```shell +Hello, World! +environ({'HOSTNAME': 'myhost', 'PYTHON_VERSION': '3.9.16', 'PWD': '/', 'CMD_ARGS': '','SCRIPT': 'hellopy.py', ...}) +``` + +### Pass command line arguments to the python command + +You can pass python command line arguments by specifying `CMD_ARGS`. +The following example will print the help of python3: + +```shell +docker run -e "CMD_ARGS= -help" -it lifecycle-toolkit/python-runtime:${VERSION} +``` + +### Pass arguments to your python script + +In this example we pass one argument (-i test.txt) to the script + +```shell +docker run -v $(pwd)/samples/args.py:/args.py -e "SCRIPT=args.py -i test.txt" -it lifecycle-toolkit/python-runtime:${VERSION} +``` + +### Use a script from url + +We can call the hellopy.py script downloading it directly from github + +```shell +docker run -e "SCRIPT=https://raw.githubusercontent.com/keptn/lifecycle-toolkit/main/python-runtime/samples/hellopy.py" -it lifecycle-toolkit/python-runtime:${VERSION} +``` + + + diff --git a/python-runtime/entrypoint.sh b/python-runtime/entrypoint.sh new file mode 100755 index 0000000000..d1628f229e --- /dev/null +++ b/python-runtime/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -eu + +regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]' + +if [[ $SCRIPT =~ $regex ]] +then + curl -s $SCRIPT | python3 $CMD_ARGS - +else + python3 $CMD_ARGS $SCRIPT +fi diff --git a/python-runtime/samples/args.py b/python-runtime/samples/args.py new file mode 100644 index 0000000000..0dc3aba098 --- /dev/null +++ b/python-runtime/samples/args.py @@ -0,0 +1,12 @@ +import sys, getopt + +def main(argv): + inputfile = '' + opts, _ = getopt.getopt(argv,"i:",["ifile="]) + for opt, arg in opts: + if opt in ("-i", "--ifile"): + inputfile = arg + print ('Input file is ', inputfile) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/python-runtime/samples/hellopy.py b/python-runtime/samples/hellopy.py new file mode 100644 index 0000000000..0d973a7557 --- /dev/null +++ b/python-runtime/samples/hellopy.py @@ -0,0 +1,4 @@ +import os + +print("Hello, World!") +print(os.environ) diff --git a/renovate.json b/renovate.json index 25af678023..94473248ed 100644 --- a/renovate.json +++ b/renovate.json @@ -23,11 +23,11 @@ "**/tests/**" ], "ignoreDeps": [ - "ghcr.keptn.sh/keptn/lifecycle-operator", - "ghcr.keptn.sh/keptn/scheduler", - "ghcr.keptn.sh/keptn/functions-runtime", - "ghcr.keptn.sh/keptn/certificate-operator", - "ghcr.keptn.sh/keptn/metrics-operator" + "ghcr.io/keptn/lifecycle-operator", + "ghcr.io/keptn/scheduler", + "ghcr.io/keptn/functions-runtime", + "ghcr.io/keptn/certificate-operator", + "ghcr.io/keptn/metrics-operator" ], "packageRules": [ { diff --git a/scheduler/Dockerfile b/scheduler/Dockerfile index 7a33ae4552..2d49156692 100644 --- a/scheduler/Dockerfile +++ b/scheduler/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.20.3-alpine3.16 AS builder +FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine3.16 AS builder ENV CGO_ENABLED=0 diff --git a/scheduler/Makefile b/scheduler/Makefile index a7de6efa6c..55917be1a5 100644 --- a/scheduler/Makefile +++ b/scheduler/Makefile @@ -46,7 +46,7 @@ $(LOCALBIN): ## Tool Versions # renovate: datasource=github-tags depName=kubernetes-sigs/kustomize -KUSTOMIZE_VERSION ?= v5.0.1 +KUSTOMIZE_VERSION ?= v5.0.3 ## Tool Binaries KUSTOMIZE ?= $(LOCALBIN)/kustomize diff --git a/scheduler/go.mod b/scheduler/go.mod index 4626f58884..e79c6b8eb8 100644 --- a/scheduler/go.mod +++ b/scheduler/go.mod @@ -4,23 +4,23 @@ go 1.20 require ( github.com/kelseyhightower/envconfig v1.4.0 - github.com/onsi/ginkgo/v2 v2.9.2 - github.com/onsi/gomega v1.27.6 + github.com/onsi/ginkgo/v2 v2.9.5 + github.com/onsi/gomega v1.27.7 github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 go.opentelemetry.io/otel v0.20.0 go.opentelemetry.io/otel/exporters/otlp v0.20.0 go.opentelemetry.io/otel/exporters/stdout v0.20.0 go.opentelemetry.io/otel/sdk v0.20.0 go.opentelemetry.io/otel/trace v0.20.0 - google.golang.org/grpc v1.54.0 - k8s.io/api v0.25.9 - k8s.io/apimachinery v0.25.9 - k8s.io/apiserver v0.25.9 - k8s.io/client-go v0.25.9 - k8s.io/component-base v0.25.9 - k8s.io/klog/v2 v2.90.1 - k8s.io/kubernetes v1.25.9 + google.golang.org/grpc v1.54.1 + k8s.io/api v0.25.10 + k8s.io/apimachinery v0.25.10 + k8s.io/apiserver v0.25.10 + k8s.io/client-go v0.25.10 + k8s.io/component-base v0.25.10 + k8s.io/klog/v2 v2.100.1 + k8s.io/kubernetes v1.25.10 sigs.k8s.io/controller-runtime v0.13.1 ) @@ -41,7 +41,7 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -91,14 +91,14 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/protobuf v1.28.1 // indirect @@ -108,13 +108,13 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/cloud-provider v0.25.4 // indirect - k8s.io/component-helpers v0.25.9 // indirect + k8s.io/component-helpers v0.25.10 // indirect k8s.io/csi-translation-lib v0.25.4 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/kube-scheduler v0.0.0 // indirect k8s.io/mount-utils v0.25.4 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect @@ -123,30 +123,30 @@ require ( replace ( github.com/keptn/lifecycle-toolkit/scheduler/pkg/klcpermit => /pkg/klcpermit golang.org/x/net => golang.org/x/net v0.9.0 - k8s.io/api => k8s.io/api v0.25.9 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.25.9 - k8s.io/apimachinery => k8s.io/apimachinery v0.25.9 - k8s.io/apiserver => k8s.io/apiserver v0.25.9 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.25.9 - k8s.io/client-go => k8s.io/client-go v0.25.9 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.25.9 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.25.9 - k8s.io/code-generator => k8s.io/code-generator v0.25.9 - k8s.io/component-base => k8s.io/component-base v0.25.9 - k8s.io/component-helpers => k8s.io/component-helpers v0.25.9 - k8s.io/controller-manager => k8s.io/controller-manager v0.25.9 - k8s.io/cri-api => k8s.io/cri-api v0.25.9 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.25.9 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.25.9 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.25.9 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.25.9 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.25.9 - k8s.io/kubectl => k8s.io/kubectl v0.25.9 - k8s.io/kubelet => k8s.io/kubelet v0.25.9 - k8s.io/kubernetes => k8s.io/kubernetes v1.25.9 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.25.9 - k8s.io/metrics => k8s.io/metrics v0.25.9 - k8s.io/mount-utils => k8s.io/mount-utils v0.25.9 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.25.9 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.25.9 + k8s.io/api => k8s.io/api v0.25.10 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.25.10 + k8s.io/apimachinery => k8s.io/apimachinery v0.25.10 + k8s.io/apiserver => k8s.io/apiserver v0.25.10 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.25.10 + k8s.io/client-go => k8s.io/client-go v0.25.10 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.25.10 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.25.10 + k8s.io/code-generator => k8s.io/code-generator v0.25.10 + k8s.io/component-base => k8s.io/component-base v0.25.10 + k8s.io/component-helpers => k8s.io/component-helpers v0.25.10 + k8s.io/controller-manager => k8s.io/controller-manager v0.25.10 + k8s.io/cri-api => k8s.io/cri-api v0.25.10 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.25.10 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.25.10 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.25.10 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.25.10 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.25.10 + k8s.io/kubectl => k8s.io/kubectl v0.25.10 + k8s.io/kubelet => k8s.io/kubelet v0.25.10 + k8s.io/kubernetes => k8s.io/kubernetes v1.25.10 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.25.10 + k8s.io/metrics => k8s.io/metrics v0.25.10 + k8s.io/mount-utils => k8s.io/mount-utils v0.25.10 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.25.10 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.25.10 ) diff --git a/scheduler/go.sum b/scheduler/go.sum index 96cfb687fb..b6a11da39e 100644 --- a/scheduler/go.sum +++ b/scheduler/go.sum @@ -117,8 +117,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -270,10 +270,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU= @@ -325,18 +325,14 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -439,6 +435,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -461,8 +458,9 @@ 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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -506,8 +504,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= @@ -573,8 +572,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -654,8 +653,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.54.1 h1:zQZQNqQZU9cHv2vLdDhB2mFeDZ2hGpgYM1A0PKjFsSM= +google.golang.org/grpc v1.54.1/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -705,42 +704,42 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.9 h1:XuJ2bz2F52jZmp3YjUcp/pozH8kY1BlBHdXnoOXBP3U= -k8s.io/api v0.25.9/go.mod h1:9YRWzD0cRHzfsnf9e5OQsQ4Un6cbZ//Xv3jo44YKm2Y= -k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8= -k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M= -k8s.io/apimachinery v0.25.9 h1:MPjgTz4dbAKJ/KiHIvDeYkFfIn7ueihqvT520HkV7v4= -k8s.io/apimachinery v0.25.9/go.mod h1:ZTl0drTQaFi5gMM3snYI5tWV1XJmRH1gfnDx2QCLsxk= -k8s.io/apiserver v0.25.9 h1:1tuxeA28SnoK30bhOa48c6tOCQypcJJYlsGE8BJpUko= -k8s.io/apiserver v0.25.9/go.mod h1:FHU743u4KKL79IpiQU/d8MiwA+JdHX26vfhG7gBJSYo= -k8s.io/client-go v0.25.9 h1:U0S3nc71NRfHXiA0utyCkPt3Mv1SWpQw0g5VfBCv5xg= -k8s.io/client-go v0.25.9/go.mod h1:tmPyOtpbbkneXj65EYZ4sXun1BE/2F2XlRABVj9CBgc= -k8s.io/cloud-provider v0.25.9 h1:o8yJ3E5PmCTZ9jh86Nob5wYJk8TjtY705j6NpnReyaQ= -k8s.io/cloud-provider v0.25.9/go.mod h1:X5gJ477bpXjgRkx4OJy5jgheMY6wQOVgstjklu4HiLw= -k8s.io/component-base v0.25.9 h1:4ZZX907RLQus98m6O66FEp2mrh6ul2+xYcW17dLx/0s= -k8s.io/component-base v0.25.9/go.mod h1:h/Sf4qyuoIwFA14ff4TT6anxT3ugtWjh8J9NQj/U0hc= -k8s.io/component-helpers v0.25.9 h1:WfuDvS0xO+ADmIacqYuM2O8qHq9bUZCYdYCjTCB2jwM= -k8s.io/component-helpers v0.25.9/go.mod h1:o9yuVdUiyKe0ubYP78veYtYxThJx66PsGzpRandVwf0= -k8s.io/csi-translation-lib v0.25.9 h1:82oc8yYCapshWTMQVmecBEWSdbhrmpuEOBQUSlh1x5I= -k8s.io/csi-translation-lib v0.25.9/go.mod h1:oWiY3igOmast1WmTreVezMXyOVRMoB1Eaa5j8CwtwEI= +k8s.io/api v0.25.10 h1:YfcmWMKDnWpzKV2byP+fu0v00yNTS4+cqw4g0ndUsJA= +k8s.io/api v0.25.10/go.mod h1:7inWacs1rgsi5uLOONfUmo4on+tVkkuJZNsMLouGAhA= +k8s.io/apiextensions-apiserver v0.25.10 h1:10aKN++Nrzql7/nw9X+OdALaPq2okw1Bj9KxKm5CpsM= +k8s.io/apiextensions-apiserver v0.25.10/go.mod h1:MpvczMBurvgfZ7MPVrUt1IOGvi/K3eJipATUoLWNfFw= +k8s.io/apimachinery v0.25.10 h1:uvPXar0BVg9g2R5a5kTjMuHCjLxC5LiAclSrLOP8Q20= +k8s.io/apimachinery v0.25.10/go.mod h1:PJ+6cm50BMETqCCJx1RXQIXaq937SUdAq2vVKCGDZXU= +k8s.io/apiserver v0.25.10 h1:fhyXPhSK5Gl4eOE4GHl9U7zjTDHySed5BVQymehTqwE= +k8s.io/apiserver v0.25.10/go.mod h1:+1kB0D6V6WGlX0OaRZ/og2dBtC4+fvvjBmnG0nNS9tI= +k8s.io/client-go v0.25.10 h1:FhTgEpCDboGjByXnoEj/kiHK12SC+fjRMrkNKn72/aU= +k8s.io/client-go v0.25.10/go.mod h1:zqpG8XvdsDK7q/Dh83v2M3LgTVj8sAbT3BT0JnANjME= +k8s.io/cloud-provider v0.25.10 h1:PKJzAjQakHS6oxkXF4ZE7cHUbx7hJu/mPEmfdXUovZU= +k8s.io/cloud-provider v0.25.10/go.mod h1:sRDRQDxyQyfluYs/KxmfpyrBx7cq+x9q8olsM3+FQ2I= +k8s.io/component-base v0.25.10 h1:OjsAFfzmeDqiB9rZyGOqv72KcPMNpStViwNpEg8F0/k= +k8s.io/component-base v0.25.10/go.mod h1:cWsBkPnnIX6v7WDjCs72P8q3l0b00KIDgyfcjLXT/eQ= +k8s.io/component-helpers v0.25.10 h1:VUZZS0eqNz/7f63fhhNSt2DlD+1wFsUis5aI7N5sr+4= +k8s.io/component-helpers v0.25.10/go.mod h1:fIvM88ONeNa/e4ytDIUhM+oBSL8jsgteVEmosWEnvWU= +k8s.io/csi-translation-lib v0.25.10 h1:aPA/UAGffC5+4cHgeWORZe2w1ytrpXGg96TQyxLf2zU= +k8s.io/csi-translation-lib v0.25.10/go.mod h1:kCLPsbj4GvdyjlYy57obi1wDjAAhuQ9wH+EqaV0qcr4= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/kube-scheduler v0.25.9 h1:1niibmG0cU/yGG8O+ORRUhbisQ6k7d+g7O6Fo1CZSNs= -k8s.io/kube-scheduler v0.25.9/go.mod h1:diz6iLAyovBUF/tqgb+Pw/mmutfLb8p3GI+LxmlVSFY= -k8s.io/kubernetes v1.25.9 h1:ecLjIq630FVMRgisW7jO0rr+vf2PLSbIGKgol2xlAx4= -k8s.io/kubernetes v1.25.9/go.mod h1:wP+j1DisuLPNLGK0YrsFIQdBtz04xQB7wwM7IbmrKRw= -k8s.io/mount-utils v0.25.9 h1:gGWKc7u+gVppGSBPVMs1rh7/w1ElM9XA1EVfpGPdOkY= -k8s.io/mount-utils v0.25.9/go.mod h1:vojU37mpuNwtcRmJiVso5mgFvdo5wI8No6RLp2O1yhg= +k8s.io/kube-scheduler v0.25.10 h1:wpQZ8G2AE+7tC5ue3FVQox/UgpKSVDeUaE0EVxN0mQg= +k8s.io/kube-scheduler v0.25.10/go.mod h1:qxG3EaVXCNzYhu0aOt20gMjgYKTCzDfdMU430XpMLLg= +k8s.io/kubernetes v1.25.10 h1:y2+mX7XpdBUwr3c2uGxF+sxF4c3WLR4vPLtc5s026oo= +k8s.io/kubernetes v1.25.10/go.mod h1:USGoemphFvArsdVC5SC9jPMggic3scS3nUBuXurz97w= +k8s.io/mount-utils v0.25.10 h1:ZlMrV9cs6YAyiZrYCpUcLZMv6keq5cXi+50SZMXJNTo= +k8s.io/mount-utils v0.25.10/go.mod h1:IM9QOFh15E1a4Nb6Rcn8FJ9Z1PbBpuyAPCty/qvKSAw= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36 h1:PUuX1qIFv309AT8hF/CdPKDmsG/hn/L8zRX7VvISM3A= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 h1:fAPTNEpzQMOLMGwOHNbUkR2xXTQwMJOZYNx+/mLlOh0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37/go.mod h1:vfnxT4FXNT8eGvO+xi/DsyC/qHmdujqwrUa1WSspCsk= sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= diff --git a/scheduler/pkg/klcpermit/workflow_manager.go b/scheduler/pkg/klcpermit/workflow_manager.go index 3a9f0a77ae..e3b49b979d 100644 --- a/scheduler/pkg/klcpermit/workflow_manager.go +++ b/scheduler/pkg/klcpermit/workflow_manager.go @@ -41,6 +41,9 @@ const ( StateDeprecated KeptnState = "Deprecated" ) +const MinKLTNameLen = 80 +const MaxK8sObjectLength = 253 + const WorkloadAnnotation = "keptn.sh/workload" const VersionAnnotation = "keptn.sh/version" const AppAnnotation = "keptn.sh/app" @@ -122,14 +125,59 @@ func (sMgr *WorkloadManager) getSpan(ctx context.Context, crd *unstructured.Unst return ctx, span } +// CreateResourceName is a function that concatenates the parts from the +// input and checks, if the resulting string matches the maxLen condition. +// If it does not match, it reduces the subparts, starting with the first +// one (but leaving its length at least in minSubstrLen so it's not deleted +// completely) adn continuing with the rest if needed. +// Let's take WorkloadInstance as an example (3 parts: app, workload, version). +// First the app name is reduced if needed (only to minSubstrLen), +// afterwards workload and the version is not reduced at all. This pattern is +// chosen to not reduce only one part of the name (that can be completely gone +// afterwards), but to include all of the parts in the resulting string. +func createResourceName(maxLen int, minSubstrLen int, str ...string) string { + // if the minSubstrLen is too long for the number of parts, + // needs to be reduced + for len(str)*minSubstrLen > maxLen { + minSubstrLen = minSubstrLen / 2 + } + // looping through the subparts and checking if the resulting string + // matches the maxLen condition + for i := 0; i < len(str)-1; i++ { + newStr := strings.Join(str, "-") + if len(newStr) > maxLen { + // if the part is already smaller than the minSubstrLen, + // this part cannot be reduced anymore, so we continue + if len(str[i]) <= minSubstrLen { + continue + } + // part needs to be reduced + cut := len(newStr) - maxLen + // if the needed reduction is bigger than the allowed + // reduction on the part, it's reduced to the minimum + if cut > len(str[i])-minSubstrLen { + str[i] = str[i][:minSubstrLen] + } else { + // the needed reduction can be completed fully on this + // part, so it's reduced accordingly + str[i] = str[i][:len(str[i])-cut] + } + } else { + return strings.ToLower(newStr) + } + } + + return strings.ToLower(strings.Join(str, "-")) +} + func getCRDName(pod *corev1.Pod) string { application, _ := getLabelOrAnnotation(pod, AppAnnotation, K8sRecommendedAppAnnotations) - workloadInstance, _ := getLabelOrAnnotation(pod, WorkloadAnnotation, K8sRecommendedWorkloadAnnotations) + workload, _ := getLabelOrAnnotation(pod, WorkloadAnnotation, K8sRecommendedWorkloadAnnotations) version, versionExists := getLabelOrAnnotation(pod, VersionAnnotation, K8sRecommendedVersionAnnotations) if !versionExists { version = calculateVersion(pod) } - return application + "-" + workloadInstance + "-" + version + return createResourceName(MaxK8sObjectLength, MinKLTNameLen, application, workload, version) } func (sMgr *WorkloadManager) unbindSpan(pod *corev1.Pod) { diff --git a/scheduler/pkg/klcpermit/workflow_manager_test.go b/scheduler/pkg/klcpermit/workflow_manager_test.go index e4ac3bae3e..674568e8b5 100644 --- a/scheduler/pkg/klcpermit/workflow_manager_test.go +++ b/scheduler/pkg/klcpermit/workflow_manager_test.go @@ -89,6 +89,99 @@ func Test_getCRDName(t *testing.T) { } } +func Test_CreateResourceName(t *testing.T) { + tests := []struct { + Name string + Input []string + Max int + Min int + Want string + }{ + { + Name: "parts not exceeding max, not min", + Input: []string{ + "str1", + "str2", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1-str2-str3", + }, + { + Name: "1 part exceeding max", + Input: []string{ + "str1111111111111111111111", + "str2", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1111111-str2-str3", + }, + { + Name: "2 part exceeding max", + Input: []string{ + "str1", + "str222222222222222222222222", + "str3", + }, + Max: 20, + Min: 5, + Want: "str1-str2222222-str3", + }, + { + Name: "1 and 2 part exceeding max", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 5, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 10, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 20, + Want: "str11-str222222-str3", + }, + { + Name: "1 and 2 part exceeding max, min needs to be reduced", + Input: []string{ + "str111111111111111111111", + "str22222222", + "str3", + }, + Max: 20, + Min: 100, + Want: "str111-str22222-str3", + }, + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + require.Equal(t, tt.Want, createResourceName(tt.Max, tt.Min, tt.Input...)) + }) + } +} + func Test_getLabelOrAnnotation(t *testing.T) { tests := []struct { name string diff --git a/test/integration/container-runtime/00-assert.yaml b/test/integration/container-runtime/00-assert.yaml new file mode 100644 index 0000000000..109db6ff9f --- /dev/null +++ b/test/integration/container-runtime/00-assert.yaml @@ -0,0 +1,64 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: test + name: test +status: + readyReplicas: 1 +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnWorkload +metadata: + name: waiter-waiter +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnWorkloadInstance +metadata: + name: waiter-waiter-0.4 +status: + currentPhase: Completed + deploymentStatus: Succeeded + postDeploymentEvaluationStatus: Succeeded + postDeploymentStatus: Succeeded + preDeploymentEvaluationStatus: Succeeded + preDeploymentStatus: Succeeded + preDeploymentTaskStatus: + - definitionName: pre-deployment-sleep + status: Succeeded + status: Succeeded +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnApp +metadata: + name: waiter +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnAppVersion +metadata: + name: waiter-1b899b6ce1-6b86b273 +status: + currentPhase: Completed + status: Succeeded +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTask +metadata: + annotations: + container: test +status: + status: Succeeded +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + container: test + keptn.sh/app: waiter + keptn.sh/version: '0.4' + keptn.sh/workload: waiter-waiter +status: + conditions: + - type: Complete + status: 'True' + succeeded: 1 diff --git a/test/integration/container-runtime/00-install.yaml b/test/integration/container-runtime/00-install.yaml new file mode 100644 index 0000000000..356aaa942f --- /dev/null +++ b/test/integration/container-runtime/00-install.yaml @@ -0,0 +1,40 @@ +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: pre-deployment-sleep + annotations: + container: test +spec: + container: + name: testy-test + image: busybox:1.36.0 + command: + - 'sh' + - '-c' + - 'sleep 30' +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: test + name: test +spec: + replicas: 1 + selector: + matchLabels: + app: test + strategy: {} + template: + metadata: + labels: + app: test + annotations: + keptn.sh/workload: waiter + keptn.sh/version: "0.4" + keptn.sh/pre-deployment-tasks: pre-deployment-sleep + spec: + containers: + - image: busybox + name: busybox + command: ['sh', '-c', 'echo The app is running! && sleep infinity'] diff --git a/test/integration/container-runtime/00-teststep.yaml b/test/integration/container-runtime/00-teststep.yaml new file mode 100644 index 0000000000..ad4f1d95d5 --- /dev/null +++ b/test/integration/container-runtime/00-teststep.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1 +kind: TestStep +commands: + - script: kubectl annotate ns $NAMESPACE keptn.sh/lifecycle-toolkit='enabled' diff --git a/test/integration/expose-keptn-metric/job-existing-metric.yaml b/test/integration/expose-keptn-metric/job-existing-metric.yaml index fadc409bb9..a2b210fbb4 100644 --- a/test/integration/expose-keptn-metric/job-existing-metric.yaml +++ b/test/integration/expose-keptn-metric/job-existing-metric.yaml @@ -20,7 +20,7 @@ spec: spec: containers: - name: test-prometheus - image: curlimages/curl:8.00.1 + image: curlimages/curl:8.1.1 args: - /bin/sh - -ec @@ -33,7 +33,7 @@ spec: fi exit 1 - name: test-api-endpoint - image: curlimages/curl:8.00.1 + image: curlimages/curl:8.1.1 # yamllint disable rule:line-length args: - /bin/sh diff --git a/test/integration/expose-keptn-metric/job-no-metric.yaml b/test/integration/expose-keptn-metric/job-no-metric.yaml index 4dd12ce3fd..749f17e3c1 100644 --- a/test/integration/expose-keptn-metric/job-no-metric.yaml +++ b/test/integration/expose-keptn-metric/job-no-metric.yaml @@ -9,7 +9,7 @@ spec: spec: containers: - name: test-prometheus - image: curlimages/curl:8.00.1 + image: curlimages/curl:8.1.1 args: - /bin/sh - -ec @@ -20,7 +20,7 @@ spec: exit 1 fi - name: test-api-endpoint - image: curlimages/curl:8.00.1 + image: curlimages/curl:8.1.1 args: - /bin/sh - -ec diff --git a/test/integration/podtato-head-application-auto-app-discovery/00-install.yaml b/test/integration/podtato-head-application-auto-app-discovery/00-install.yaml index 255636461f..0d9c78e535 100644 --- a/test/integration/podtato-head-application-auto-app-discovery/00-install.yaml +++ b/test/integration/podtato-head-application-auto-app-discovery/00-install.yaml @@ -20,7 +20,7 @@ spec: terminationGracePeriodSeconds: 5 initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] containers: - name: server diff --git a/test/integration/podtato-head-application/00-install.yaml b/test/integration/podtato-head-application/00-install.yaml index 3282ad348d..b8a6f95c68 100644 --- a/test/integration/podtato-head-application/00-install.yaml +++ b/test/integration/podtato-head-application/00-install.yaml @@ -52,7 +52,7 @@ spec: terminationGracePeriodSeconds: 5 initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] containers: - name: server diff --git a/test/integration/restartable-app/00-install.yaml b/test/integration/restartable-app/00-install.yaml index 40ba6a75d3..5c2ea28ded 100644 --- a/test/integration/restartable-app/00-install.yaml +++ b/test/integration/restartable-app/00-install.yaml @@ -45,7 +45,7 @@ spec: terminationGracePeriodSeconds: 5 initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] containers: - name: server diff --git a/test/integration/simple-deployment-annotated/00-install.yaml b/test/integration/simple-deployment-annotated/00-install.yaml index 2eb550a682..76231e3a64 100644 --- a/test/integration/simple-deployment-annotated/00-install.yaml +++ b/test/integration/simple-deployment-annotated/00-install.yaml @@ -37,5 +37,5 @@ spec: command: ['sh', '-c', 'echo The app is running! && sleep infinity'] initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] diff --git a/test/integration/simple-deployment-evaluation/00-install.yaml b/test/integration/simple-deployment-evaluation/00-install.yaml index f0a22ddc8d..bc4f50f190 100644 --- a/test/integration/simple-deployment-evaluation/00-install.yaml +++ b/test/integration/simple-deployment-evaluation/00-install.yaml @@ -55,5 +55,5 @@ spec: command: ['sh', '-c', 'echo The app is running! && sleep infinity'] initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 10'] diff --git a/test/integration/simple-deployment-recursive-task/00-assert.yaml b/test/integration/simple-deployment-recursive-task/00-assert.yaml new file mode 100644 index 0000000000..e6f7dd679b --- /dev/null +++ b/test/integration/simple-deployment-recursive-task/00-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: test + name: test +status: + readyReplicas: 1 diff --git a/test/integration/simple-deployment-recursive-task/00-install.yaml b/test/integration/simple-deployment-recursive-task/00-install.yaml new file mode 100644 index 0000000000..f08baa5dd6 --- /dev/null +++ b/test/integration/simple-deployment-recursive-task/00-install.yaml @@ -0,0 +1,72 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mysecret +type: Opaque +data: + SECURE_DATA: dG9rZW46IG15dG9rZW4= +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: pre-deployment-hello +spec: + function: + functionRef: + name: pre-deployment-parent + secureParameters: + secret: mysecret + parameters: + map: + user: "myuser" + data: "mydata" +--- +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnTaskDefinition +metadata: + name: pre-deployment-parent +spec: + function: + parameters: + map: + user: "myotheruser" + data: "myotherdata" + other: "data" + inline: + code: | + console.log("Parent Task has been executed"); + + let foo = Deno.env.get('DATA'); + console.log(foo); + Deno.exit(0); +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: test + name: test +spec: + replicas: 1 + selector: + matchLabels: + app: test + strategy: {} + template: + metadata: + labels: + app: test + annotations: + keptn.sh/workload: waiter + keptn.sh/version: "0.4" + keptn.sh/pre-deployment-tasks: pre-deployment-hello + keptn.sh/post-deployment-tasks: pre-deployment-parent + spec: + containers: + - image: busybox + name: busybox + command: ['sh', '-c', 'echo The app is running! && sleep infinity'] + initContainers: + - name: init-myservice + image: busybox:1.36.1 + command: ['sh', '-c', 'sleep 30'] diff --git a/test/integration/simple-deployment-recursive-task/00-teststep.yaml b/test/integration/simple-deployment-recursive-task/00-teststep.yaml new file mode 100644 index 0000000000..ad4f1d95d5 --- /dev/null +++ b/test/integration/simple-deployment-recursive-task/00-teststep.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1 +kind: TestStep +commands: + - script: kubectl annotate ns $NAMESPACE keptn.sh/lifecycle-toolkit='enabled' diff --git a/test/integration/simple-deployment-recursive-task/01-assert.yaml b/test/integration/simple-deployment-recursive-task/01-assert.yaml new file mode 100644 index 0000000000..d02b42e28c --- /dev/null +++ b/test/integration/simple-deployment-recursive-task/01-assert.yaml @@ -0,0 +1,96 @@ +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnWorkload +metadata: + name: waiter-waiter + +--- + +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnWorkloadInstance +metadata: + name: waiter-waiter-0.4 +status: + currentPhase: Completed + deploymentStatus: Succeeded + postDeploymentEvaluationStatus: Succeeded + postDeploymentStatus: Succeeded + postDeploymentTaskStatus: + - status: Succeeded + definitionName: pre-deployment-parent + preDeploymentEvaluationStatus: Succeeded + preDeploymentStatus: Succeeded + preDeploymentTaskStatus: + - status: Succeeded + definitionName: pre-deployment-hello + +--- + +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnApp +metadata: + name: waiter + +--- + +apiVersion: lifecycle.keptn.sh/v1alpha3 +kind: KeptnAppVersion +metadata: + name: waiter-1b899b6ce1-6b86b273 + +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + batch.kubernetes.io/job-tracking: "" + keptn.sh/app: waiter + keptn.sh/version: "0.4" + keptn.sh/workload: waiter-waiter +spec: + template: + spec: + containers: + - env: + - name: DATA + value: '{"data":"myotherdata","other":"data","user":"myotheruser"}' + - name: CONTEXT + value: '{"workloadName":"waiter-waiter","appName":"waiter","appVersion":"","workloadVersion":"0.4","taskType":"","objectType":"Workload"}' + - name: SCRIPT + value: /var/data/function.ts + name: keptn-function-runner +status: + succeeded: 1 + +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + batch.kubernetes.io/job-tracking: "" + keptn.sh/app: waiter + keptn.sh/version: "0.4" + keptn.sh/workload: waiter-waiter + ownerReferences: + - apiVersion: lifecycle.keptn.sh/v1alpha3 + blockOwnerDeletion: true + controller: true + kind: KeptnTask +spec: + template: + spec: + containers: + - env: + - name: DATA + value: '{"data":"mydata","other":"data","user":"myuser"}' + - name: CONTEXT + value: '{"workloadName":"waiter-waiter","appName":"waiter","appVersion":"","workloadVersion":"0.4","taskType":"","objectType":"Workload"}' + - name: SECURE_DATA + valueFrom: + secretKeyRef: + key: SECURE_DATA + name: mysecret + - name: SCRIPT + value: /var/data/function.ts + name: keptn-function-runner +status: + succeeded: 1 diff --git a/test/integration/simple-deployment/00-install.yaml b/test/integration/simple-deployment/00-install.yaml index 1374332978..c96eff8c86 100644 --- a/test/integration/simple-deployment/00-install.yaml +++ b/test/integration/simple-deployment/00-install.yaml @@ -36,5 +36,5 @@ spec: command: ['sh', '-c', 'echo The app is running! && sleep infinity'] initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] diff --git a/test/integration/simple-statefulset-annotated/00-install.yaml b/test/integration/simple-statefulset-annotated/00-install.yaml index dd52f386fd..66e8cc0b1f 100644 --- a/test/integration/simple-statefulset-annotated/00-install.yaml +++ b/test/integration/simple-statefulset-annotated/00-install.yaml @@ -40,5 +40,5 @@ spec: command: ['sh', '-c', 'echo The app is running! && sleep infinity'] initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30'] diff --git a/test/integration/simple-statefulset-annotated/02-install.yaml b/test/integration/simple-statefulset-annotated/02-install.yaml index 560512bf7d..566928e835 100644 --- a/test/integration/simple-statefulset-annotated/02-install.yaml +++ b/test/integration/simple-statefulset-annotated/02-install.yaml @@ -27,5 +27,5 @@ spec: command: ['sh', '-c', 'echo The app is running! && sleep infinity'] initContainers: - name: init-myservice - image: busybox:1.36.0 + image: busybox:1.36.1 command: ['sh', '-c', 'sleep 30']