From 4526502a21b3dd2c8a321a50a64dee8cebe1535f Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+ElaiShalevRH@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:01:21 +0300 Subject: [PATCH 001/117] Added Knative CRDs and Knative CR creation logic (#117) * Added Knative CRDs and Knative CR creation logic * Added maintanence guide for CRDs, removed annotations * CRD disclaimer * Added Knative CRDs, instructions to update them, and subscription channel * Chart version bump * typos, updated README.tmpl * update github test to installcrds upon upgrade * applying crds from local dir * pre-install hooks for namespaces * adding delete hooks to knative * Added pre-delete hooks for CRDs * changing helm hooks in knative resources --- .github/workflows/test.yml | 4 + charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 18 +- charts/orchestrator-infra/README.md.gotmpl | 15 + .../knative-eventing-crd.yaml | 2286 ++++++++++++++++ .../knative-serving/knative-serving-crd.yaml | 2377 +++++++++++++++++ .../templates/serverless/knatives.yaml | 38 +- charts/orchestrator-infra/values.yaml | 2 +- 8 files changed, 4737 insertions(+), 5 deletions(-) create mode 100644 charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml create mode 100644 charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7edc57e7..8af5b939 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -156,6 +156,10 @@ jobs: - name: Run chart-testing run: | + + kubectl apply -f charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml + kubectl apply -f charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml + ct install \ --debug \ --config ct-install.yaml \ diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index b1dd4042..aaadad72 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -13,4 +13,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.0.2 +version: 0.0.3 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index fa0bd6a0..4883c565 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift (Community Version) -![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Logic Operator and OpenShift Serverless Operator. @@ -90,7 +90,7 @@ The command removes all the Kubernetes components associated with the chart and | serverlessLogicOperator.subscription.spec.name | name of the operator package | string | `"logic-operator-rhel8"` | | serverlessLogicOperator.subscription.spec.source | name of the catalog source | string | `"redhat-operators"` | | serverlessLogicOperator.subscription.spec.sourceNamespace | | string | `"openshift-marketplace"` | -| serverlessLogicOperator.subscription.spec.startingCSV | The initial version of the operator | string | `"logic-operator-rhel8.v1.35.0"` | +| serverlessLogicOperator.subscription.spec.startingCSV | The initial version of the operator, must match CRDs installed by the chart | string | `"logic-operator-rhel8.v1.35.0"` | | serverlessOperator.enabled | whether the operator should be deployed by the chart | bool | `true` | | serverlessOperator.subscription.namespace | namespace where the operator should be deployed | string | `"openshift-serverless"` | | serverlessOperator.subscription.spec.channel | channel of an operator package to subscribe to | string | `"stable"` | @@ -101,3 +101,17 @@ The command removes all the Kubernetes components associated with the chart and | tests.enabled | Whether to create the test pod used for testing the Release using `helm test`. | bool | `true` | | tests.image | Test pod image | string | `"bitnami/kubectl:latest"` | +### Installing Knative Eventing and Knative Serving CRDs + +The orchestrator-infra chart requires several CRDs for Knative Eventing and Knative Serving. These CRDs will be applied prior to installing the chart, ensuring that Knative CRs can be created as part of the chart's deployment process. This approach eliminates the need to wait for the OpenShift Serverless Operator's subscription to install them beforehand. + +The KnativeEventing and KnativeServing CRDs are required for this chart to run. These CRDs need to be present under the crds/ directory before running `helm install`. +After installing the openshift-serverless subscription, more Knative CRDs will be installed on the cluster. + +The versions of the CRDs present in the chart and the ones in the subscrtiprion must match. In order to verify the correct CRD, use this following command to extract the CRD: + +```bash +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml + +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml +``` \ No newline at end of file diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index 937d383f..e3764bf7 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -75,3 +75,18 @@ helm uninstall my-orchestrator-infra The command removes all the Kubernetes components associated with the chart and deletes the release. {{ template "chart.valuesSection" . }} + +### Installing Knative Eventing and Knative Serving CRDs + +The orchestrator-infra chart requires several CRDs for Knative Eventing and Knative Serving. These CRDs will be applied prior to installing the chart, ensuring that Knative CRs can be created as part of the chart's deployment process. This approach eliminates the need to wait for the OpenShift Serverless Operator's subscription to install them beforehand. + +The KnativeEventing and KnativeServing CRDs are required for this chart to run. These CRDs need to be present under the crds/ directory before running `helm install`. +After installing the openshift-serverless subscription, more Knative CRDs will be installed on the cluster. + +The versions of the CRDs present in the chart and the ones in the subscrtiprion must match. In order to verify the correct CRD, use this following command to extract the CRD: + +```bash +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml + +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml +``` \ No newline at end of file diff --git a/charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml b/charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml new file mode 100644 index 00000000..ee3bbfd2 --- /dev/null +++ b/charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml @@ -0,0 +1,2286 @@ +# Copyright 2021 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: knativeeventings.operator.knative.dev + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation + labels: + app.kubernetes.io/version: devel + app.kubernetes.io/name: knative-operator +spec: + group: operator.knative.dev + versions: + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: Schema for the knativeeventings API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of KnativeEventing + x-kubernetes-preserve-unknown-fields: true # To allow for some fields we've deleted. + properties: + config: + additionalProperties: + additionalProperties: + type: string + type: object + description: A means to override the corresponding entries in the + upstream configmaps + type: object + defaultBrokerClass: + description: The default broker type to use for the brokers Knative + creates. If no value is provided, MTChannelBasedBroker will be used. + type: string + high-availability: + description: Allows specification of HA control plane + properties: + replicas: + description: The number of replicas that HA parts of the control + plane will be scaled to + minimum: 0 + type: integer + type: object + workloads: + description: A mapping of deployment or statefulset name to override + type: array + items: + type: object + properties: + name: + description: The name of the deployment + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the deployment and its template. + type: object + livenessProbes: + description: LivenessProbes overrides liveness probes for the + containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the deployment and its template. + type: object + env: + description: Env overrides env vars for the containers. + items: + properties: + container: + description: The container name + type: string + envVars: + description: The desired EnvVarRequirements + 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 + 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 + 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 + 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 + type: object + required: + - name + type: object + type: array + required: + - container + type: object + type: array + replicas: + description: The number of replicas that HA parts of the control plane will be scaled to + type: integer + minimum: 0 + nodeSelector: + additionalProperties: + type: string + description: NodeSelector overrides nodeSelector for the deployment. + type: object + readinessProbes: + description: ReadinessProbes overrides readiness probes for + the containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + hostNetwork: + description: Use the host's network namespace if true. Make sure to + understand the security implications if you want to enable it. When + hostNetwork is enabled, this will set dnsPolicy to ClusterFirstWithHostNet + automatically. + type: boolean + topologySpreadConstraints: + description: If specified, the pod's topology spread constraints. + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods may + be unevenly distributed. It''s the maximum permitted difference + between the number of matching pods in any two topology domains + of a given topology type. For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 + to become 1/1/1; scheduling it onto zone1(zone2) would make + the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can be scheduled onto any zone. + It''s a required field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. We consider each + as a "bucket", and try to put balanced number of pods into + each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a + pod if it doesn''t satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it - ScheduleAnyway + tells the scheduler to still schedule it It''s considered + as "Unsatisfiable" if and only if placing incoming pod on + any topology violates "MaxSkew". For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with the same labelSelector + spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod + can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) + as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In + other words, the cluster can still be imbalanced, but scheduler + won''t make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + version: + description: Version the cluster should be on. + type: string + volumeMounts: + description: VolumeMounts allows configuration of additional VolumeMounts + on the output StatefulSet definition. VolumeMounts specified will + be appended to other VolumeMounts in the alertmanager container, + that are generated as a result of StorageSpec objects. + 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 + affinity: + description: If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + resources: + description: If specified, the container's resources. + items: + description: The pod this Resource is used to specify the requests and limits for + a certain container based on the name. + properties: + container: + description: The name of the container + type: string + limits: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + requests: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + type: array + deployments: + description: A mapping of deployment name to override + type: array + items: + type: object + properties: + name: + description: The name of the deployment + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the deployment and its template. + type: object + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the deployment and its template. + type: object + env: + description: Env overrides env vars for the containers. + items: + properties: + container: + description: The container name + type: string + envVars: + description: The desired EnvVarRequirements + 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 + 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 + 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 + 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 + type: object + required: + - name + type: object + type: array + required: + - container + type: object + type: array + livenessProbes: + description: LivenessProbes overrides liveness probes for the + containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + replicas: + description: The number of replicas that HA parts of the control plane will be scaled to + type: integer + minimum: 0 + nodeSelector: + additionalProperties: + type: string + description: NodeSelector overrides nodeSelector for the deployment. + type: object + readinessProbes: + description: ReadinessProbes overrides readiness probes for + the containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + hostNetwork: + description: Use the host's network namespace if true. Make sure to + understand the security implications if you want to enable it. When + hostNetwork is enabled, this will set dnsPolicy to ClusterFirstWithHostNet + automatically. + type: boolean + topologySpreadConstraints: + description: If specified, the pod's topology spread constraints. + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods may + be unevenly distributed. It''s the maximum permitted difference + between the number of matching pods in any two topology domains + of a given topology type. For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 + to become 1/1/1; scheduling it onto zone1(zone2) would make + the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can be scheduled onto any zone. + It''s a required field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. We consider each + as a "bucket", and try to put balanced number of pods into + each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a + pod if it doesn''t satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it - ScheduleAnyway + tells the scheduler to still schedule it It''s considered + as "Unsatisfiable" if and only if placing incoming pod on + any topology violates "MaxSkew". For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with the same labelSelector + spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod + can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) + as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In + other words, the cluster can still be imbalanced, but scheduler + won''t make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + affinity: + description: If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + resources: + description: If specified, the container's resources. + items: + description: The pod this Resource is used to specify the requests and limits for + a certain container based on the name. + properties: + container: + description: The name of the container + type: string + limits: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + requests: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + type: array + services: + description: A mapping of service name to override + type: array + items: + type: object + properties: + name: + description: The name of the service + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the service + type: object + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the service + type: object + selector: + additionalProperties: + type: string + description: Selector overrides selector for the service + type: object + podDisruptionBudgets: + description: A mapping of podDisruptionBudget name to override + type: array + items: + type: object + properties: + name: + description: The name of the podDisruptionBudget + type: string + minAvailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at least "minAvailable" pods selected by "selector" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying "100%". + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at most "maxUnavailable" pods selected by "selector" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with "minAvailable". + x-kubernetes-int-or-string: true + sinkBindingSelectionMode: + description: Specifies the selection mode for the sinkbinding webhook. + If the value is `inclusion`, only namespaces/objects labelled as + `bindings.knative.dev/include:true` will be considered. If `exclusion` + is selected, only `bindings.knative.dev/exclude:true` label is checked + and these will NOT be considered. The default for Openshift Serverless is `inclusion`. + type: string + type: object + status: + properties: + conditions: + description: The latest available observations of a resource's current + state. + items: + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. We use VolatileTime + in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: Severity with which to treat failures of this type + of condition. When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - type + - status + type: object + type: array + manifests: + description: The list of eventing manifests, which have been installed + by the operator + items: + type: string + type: array + observedGeneration: + description: The generation last processed by the controller + type: integer + version: + description: The version of the installed release + type: string + type: object + type: object + additionalPrinterColumns: + - jsonPath: .status.version + name: Version + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Reason + type: string + names: + kind: KnativeEventing + listKind: KnativeEventingList + plural: knativeeventings + singular: knativeeventing + scope: Namespaced + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: ["v1beta1"] + clientConfig: + service: + name: operator-webhook + namespace: knative-operator + path: /resource-conversion diff --git a/charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml b/charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml new file mode 100644 index 00000000..1761801e --- /dev/null +++ b/charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml @@ -0,0 +1,2377 @@ +# Copyright 2021 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: knativeservings.operator.knative.dev + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-weight": "-10" + "helm.sh/hook-delete-policy": before-hook-creation + labels: + app.kubernetes.io/version: devel + app.kubernetes.io/name: knative-operator +spec: + group: operator.knative.dev + versions: + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: Schema for the knativeservings API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of KnativeServing + x-kubernetes-preserve-unknown-fields: true # To allow for some fields we've deleted. + properties: + config: + additionalProperties: + additionalProperties: + type: string + type: object + description: A means to override the corresponding entries in the + upstream configmaps + type: object + controller-custom-certs: + description: Enabling the controller to trust registries with self-signed + certificates + properties: + name: + description: The name of the ConfigMap or Secret + type: string + type: + description: One of ConfigMap or Secret + enum: + - ConfigMap + - Secret + - "" + type: string + type: object + high-availability: + description: Allows specification of HA control plane + properties: + replicas: + description: The number of replicas that HA parts of the control + plane will be scaled to + minimum: 0 + type: integer + type: object + workloads: + description: A mapping of deployment or statefulset name to override + type: array + items: + type: object + properties: + name: + description: The name of the deployment + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the deployment and its template. + type: object + livenessProbes: + description: LivenessProbes overrides liveness probes for the + containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the deployment and its template. + type: object + env: + description: Env overrides env vars for the containers. + items: + properties: + container: + description: The container name + type: string + envVars: + description: The desired EnvVarRequirements + 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 + 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 + 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 + 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 + type: object + required: + - name + type: object + type: array + required: + - container + type: object + type: array + replicas: + description: The number of replicas that HA parts of the control plane will be scaled to + type: integer + minimum: 0 + nodeSelector: + additionalProperties: + type: string + description: NodeSelector overrides nodeSelector for the deployment. + type: object + readinessProbes: + description: ReadinessProbes overrides readiness probes for + the containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + hostNetwork: + description: Use the host's network namespace if true. Make sure to + understand the security implications if you want to enable it. When + hostNetwork is enabled, this will set dnsPolicy to ClusterFirstWithHostNet + automatically. + type: boolean + topologySpreadConstraints: + description: If specified, the pod's topology spread constraints. + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods may + be unevenly distributed. It''s the maximum permitted difference + between the number of matching pods in any two topology domains + of a given topology type. For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 + to become 1/1/1; scheduling it onto zone1(zone2) would make + the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can be scheduled onto any zone. + It''s a required field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. We consider each + as a "bucket", and try to put balanced number of pods into + each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a + pod if it doesn''t satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it - ScheduleAnyway + tells the scheduler to still schedule it It''s considered + as "Unsatisfiable" if and only if placing incoming pod on + any topology violates "MaxSkew". For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with the same labelSelector + spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod + can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) + as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In + other words, the cluster can still be imbalanced, but scheduler + won''t make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + version: + description: Version the cluster should be on. + type: string + volumeMounts: + description: VolumeMounts allows configuration of additional VolumeMounts + on the output StatefulSet definition. VolumeMounts specified will + be appended to other VolumeMounts in the alertmanager container, + that are generated as a result of StorageSpec objects. + 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 + affinity: + description: If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + resources: + description: If specified, the container's resources. + items: + description: The pod this Resource is used to specify the requests and limits for + a certain container based on the name. + properties: + container: + description: The name of the container + type: string + limits: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + requests: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + type: array + deployments: + description: A mapping of deployment name to override + type: array + items: + type: object + properties: + name: + description: The name of the deployment + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the deployment and its template. + type: object + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the deployment and its template. + type: object + env: + description: Env overrides env vars for the containers. + items: + properties: + container: + description: The container name + type: string + envVars: + description: The desired EnvVarRequirements + 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 + 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 + 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 + 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 + type: object + required: + - name + type: object + type: array + required: + - container + type: object + type: array + livenessProbes: + description: LivenessProbes overrides liveness probes for the + containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + replicas: + description: The number of replicas that HA parts of the control plane will be scaled to + type: integer + minimum: 0 + nodeSelector: + additionalProperties: + type: string + description: NodeSelector overrides nodeSelector for the deployment. + type: object + readinessProbes: + description: ReadinessProbes overrides readiness probes for + the containers. + items: + description: ProbesRequirementsOverride enables the user to + override any container's env vars. + properties: + container: + description: The container name + type: string + 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 + 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 + 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 + required: + - container + type: object + type: array + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + hostNetwork: + description: Use the host's network namespace if true. Make sure to + understand the security implications if you want to enable it. When + hostNetwork is enabled, this will set dnsPolicy to ClusterFirstWithHostNet + automatically. + type: boolean + topologySpreadConstraints: + description: If specified, the pod's topology spread constraints. + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods may + be unevenly distributed. It''s the maximum permitted difference + between the number of matching pods in any two topology domains + of a given topology type. For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 + to become 1/1/1; scheduling it onto zone1(zone2) would make + the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can be scheduled onto any zone. + It''s a required field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. We consider each + as a "bucket", and try to put balanced number of pods into + each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a + pod if it doesn''t satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it - ScheduleAnyway + tells the scheduler to still schedule it It''s considered + as "Unsatisfiable" if and only if placing incoming pod on + any topology violates "MaxSkew". For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with the same labelSelector + spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod + can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) + as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In + other words, the cluster can still be imbalanced, but scheduler + won''t make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + affinity: + description: If specified, the pod's scheduling constraints. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + resources: + description: If specified, the container's resources. + items: + description: The pod this Resource is used to specify the requests and limits for + a certain container based on the name. + properties: + container: + description: The name of the container + type: string + limits: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + requests: + properties: + cpu: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + memory: + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + type: array + services: + description: A mapping of service name to override + type: array + items: + type: object + properties: + name: + description: The name of the service + type: string + labels: + additionalProperties: + type: string + description: Labels overrides labels for the service + type: object + annotations: + additionalProperties: + type: string + description: Annotations overrides labels for the service + type: object + selector: + additionalProperties: + type: string + description: Selector overrides selector for the service + type: object + podDisruptionBudgets: + description: A mapping of podDisruptionBudget name to override + type: array + items: + type: object + properties: + name: + description: The name of the podDisruptionBudget + type: string + minAvailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at least "minAvailable" pods selected by "selector" will still be available after the eviction, i.e. even in the absence of the evicted pod. So for example you can prevent all voluntary evictions by specifying "100%". + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at most "maxUnavailable" pods selected by "selector" are unavailable after the eviction, i.e. even in absence of the evicted pod. For example, one can prevent all voluntary evictions by specifying 0. This is a mutually exclusive setting with "minAvailable". + x-kubernetes-int-or-string: true + ingress: + description: The ingress configuration for Knative Serving + x-kubernetes-preserve-unknown-fields: true # To allow for some fields we've deleted. + properties: + istio: + description: Istio settings + properties: + enabled: + type: boolean + knative-local-gateway: + description: A means to override the knative-local-gateway + properties: + selector: + additionalProperties: + type: string + description: The selector for the ingress-gateway. + type: object + servers: + description: A list of server specifications. + items: + properties: + hosts: + description: One or more hosts exposed by this gateway. + items: + format: string + type: string + type: array + port: + properties: + name: + description: Label assigned to the port. + format: string + type: string + number: + description: A valid non-negative integer port number. + type: integer + target_port: + description: A valid non-negative integer target port number. + type: integer + protocol: + description: The protocol exposed on the port. + format: string + type: string + type: object + tls: + properties: + mode: + description: TLS mode can be SIMPLE, MUTUAL, ISTIO_MUTUAL. + format: string + type: string + credentialName: + description: TLS certificate name. + format: string + type: string + type: object + type: object + type: array + type: object + type: object + kourier: + description: Kourier settings + properties: + enabled: + type: boolean + service-type: + type: string + service-load-balancer-ip: + type: string + bootstrap-configmap: + type: string + http-port: + type: integer + https-port: + type: integer + type: object + type: object + security: + description: The security configuration for Knative Serving + properties: + securityGuard: + description: Security Guard settings + properties: + enabled: + type: boolean + type: object + type: object + type: object + status: + description: Status defines the observed state of KnativeServing + properties: + conditions: + description: The latest available observations of a resource's current + state. + items: + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. We use VolatileTime + in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: Severity with which to treat failures of this type + of condition. When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - type + - status + type: object + type: array + manifests: + description: The list of serving manifests, which have been installed + by the operator + items: + type: string + type: array + observedGeneration: + description: The generation last processed by the controller + type: integer + version: + description: The version of the installed release + type: string + type: object + type: object + additionalPrinterColumns: + - jsonPath: .status.version + name: Version + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].reason + name: Reason + type: string + names: + kind: KnativeServing + listKind: KnativeServingList + plural: knativeservings + singular: knativeserving + scope: Namespaced + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: ["v1beta1"] + clientConfig: + service: + name: operator-webhook + namespace: knative-operator + path: /resource-conversion diff --git a/charts/orchestrator-infra/templates/serverless/knatives.yaml b/charts/orchestrator-infra/templates/serverless/knatives.yaml index e05ee822..630a97af 100644 --- a/charts/orchestrator-infra/templates/serverless/knatives.yaml +++ b/charts/orchestrator-infra/templates/serverless/knatives.yaml @@ -6,6 +6,10 @@ apiVersion: v1 kind: Namespace metadata: name: knative-serving + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "-5" + "helm.sh/resource-policy": keep {{- end }} {{- $unmanagedNamespaceExists := include "unmanaged-resource-exists" (list "v1" "Namespace" "" "knative-eventing" .Release.Name .Capabilities.APIVersions) }} @@ -15,5 +19,37 @@ apiVersion: v1 kind: Namespace metadata: name: knative-eventing + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "-5" + "helm.sh/resource-policy": keep +{{- end }} + +{{- $unmanagedKnativeEventingExists := include "unmanaged-resource-exists" (list "operator.knative.dev/v1beta1" "KnativeEventing" "knative-eventing" "knative-eventing" .Release.Name .Capabilities.APIVersions) }} +{{- if eq $unmanagedKnativeEventingExists "false" }} +--- +apiVersion: operator.knative.dev/v1beta1 +kind: KnativeEventing +metadata: + name: knative-eventing + namespace: knative-eventing +spec: + Registry: +{{- end }} + +{{- $unmanagedKnativeServingExists := include "unmanaged-resource-exists" (list "operator.knative.dev/v1beta1" "KnativeServing" "knative-serving" "knative-serving" .Release.Name .Capabilities.APIVersions) }} +{{- if eq $unmanagedKnativeServingExists "false" }} +--- +apiVersion: operator.knative.dev/v1beta1 +kind: KnativeServing +metadata: + name: knative-serving + namespace: knative-serving +spec: + controller-custom-certs: + name: "" + type: "" + registry: {} + +{{- end }} {{- end }} -{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-infra/values.yaml b/charts/orchestrator-infra/values.yaml index 967a8df6..158becaf 100644 --- a/charts/orchestrator-infra/values.yaml +++ b/charts/orchestrator-infra/values.yaml @@ -14,7 +14,7 @@ serverlessLogicOperator: # -- name of the catalog source source: redhat-operators sourceNamespace: openshift-marketplace - # -- The initial version of the operator + # -- The initial version of the operator, must match CRDs installed by the chart startingCSV: logic-operator-rhel8.v1.35.0 serverlessOperator: From f2a096cf3e427dd5d3dff92e50378680e69b38d7 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 3 Apr 2025 10:26:09 +0200 Subject: [PATCH 002/117] ci: Revert manually applying the CRDs from the orchestrator-infra chart (#122) * ci: Revert manually applying the CRDs from the orchestrator-infra chart As discussed in [1], now that 0.0.3 (with the CRDs) is the baseline for the upgrade checks, this is no longer needed. [1] https://github.com/redhat-developer/rhdh-chart/pull/117#issuecomment-2771569063 * [TO REMOVE] Test the changes by bumping the orchestrator-chart version * Revert "[TO REMOVE] Test the changes by bumping the orchestrator-chart version" This reverts commit 2a284d919a7ca11df0524a416cc3e7c94d5c927c. --- .github/workflows/test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8af5b939..7edc57e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -156,10 +156,6 @@ jobs: - name: Run chart-testing run: | - - kubectl apply -f charts/orchestrator-infra/crds/knative-eventing/knative-eventing-crd.yaml - kubectl apply -f charts/orchestrator-infra/crds/knative-serving/knative-serving-crd.yaml - ct install \ --debug \ --config ct-install.yaml \ From 321c776c64a28540b23b6a75d3fb954e80d56e6b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 3 Apr 2025 12:07:39 +0200 Subject: [PATCH 003/117] ci: Refactor the GH Workflows for better separation (#123) * ci: Refactor the GH Workflows for better separation * Rename the workflows consistently with the .yaml file extension --- .../{bump-version.yml => bump-version.yaml} | 0 .github/workflows/lint.yaml | 52 +++++++++++++++++++ .github/workflows/pre-commit.yaml | 44 ++++++++++++++++ .../workflows/{release.yml => release.yaml} | 0 .github/workflows/{test.yml => test.yaml} | 42 ++------------- 5 files changed, 101 insertions(+), 37 deletions(-) rename .github/workflows/{bump-version.yml => bump-version.yaml} (100%) create mode 100644 .github/workflows/lint.yaml create mode 100644 .github/workflows/pre-commit.yaml rename .github/workflows/{release.yml => release.yaml} (100%) rename .github/workflows/{test.yml => test.yaml} (79%) diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yaml similarity index 100% rename from .github/workflows/bump-version.yml rename to .github/workflows/bump-version.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 00000000..0dbfb024 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,52 @@ +name: Lint Charts + +on: + pull_request: + branches: + - main + - rhdh-1.[0-9]+ + - 1.[0-9]+.x + - release-1.[0-9]+ + paths: + - "charts/**" + - ".github/workflows/lint.yaml" + - "ct-install.yaml" + - "ct.yaml" + - ".helmignore" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }} + cancel-in-progress: true + +jobs: + + lint-chart: + name: Lint Metadata + runs-on: ubuntu-latest + env: + GO111MODULE: on + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # renovate: tag=v3.5 + with: + version: v3.10.0 + + - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + with: + python-version: 3.13 + + - name: Set up chart-testing + uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 + + - name: Lint charts + run: | + ct lint \ + --debug \ + --config ct.yaml \ + --target-branch "${{ github.event.pull_request.base.ref }}" diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 00000000..78fc74fc --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,44 @@ +name: Pre-commit + +on: + pull_request: + branches: + - main + - rhdh-1.[0-9]+ + - 1.[0-9]+.x + - release-1.[0-9]+ + paths: + - "charts/**" + - ".github/workflows/pre-commit.yaml" + - ".pre-commit/**" + - ".pre-commit-config.yaml" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }} + cancel-in-progress: true + +jobs: + pre-commit: + name: Pre-commit + runs-on: ubuntu-latest + env: + GO111MODULE: on + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + with: + python-version: 3.13 + + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 + with: + go-version: ^1 + + - name: Setup helm-docs + run: go install github.com/norwoodj/helm-docs/cmd/helm-docs@latest + + - name: Run pre-commit + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: --show-diff-on-failure diff --git a/.github/workflows/release.yml b/.github/workflows/release.yaml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/release.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yaml similarity index 79% rename from .github/workflows/test.yml rename to .github/workflows/test.yaml index 7edc57e7..4b6cc958 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yaml @@ -8,41 +8,17 @@ on: - 1.[0-9]+.x - release-1.[0-9]+ paths: - - "charts/**" - - ".github/**" - - ".pre-commit/**" - - "ct-install.yaml" - - "ct.yaml" + - 'charts/**' + - '.github/workflows/test.yaml' + - 'ct-install.yaml' + - 'ct.yaml' + - '.helmignore' concurrency: group: ${{ github.workflow }}-${{ github.event.number }} cancel-in-progress: true jobs: - check-metadata: - name: Lint Metadata - runs-on: ubuntu-latest - env: - GO111MODULE: on - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 - with: - python-version: 3.13 - - - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 - with: - go-version: ^1 - - - name: Setup helm-docs - run: go install github.com/norwoodj/helm-docs/cmd/helm-docs@latest - - - name: Run pre-commit - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 - with: - extra_args: --show-diff-on-failure test-chart: strategy: @@ -88,14 +64,6 @@ jobs: helm repo add backstage https://backstage.github.io/charts helm repo update - - name: Lint chart - run: | - ct lint \ - --debug \ - --config ct.yaml \ - --target-branch "${{ github.event.pull_request.base.ref }}" \ - --helm-extra-args="--set upstream.backstage.image.tag=${{ matrix.version }} --set upstream.ingress.enabled=true" - - name: Generate KinD Config run: | cat < /tmp/kind-config.yaml From 837c81309d27fd6344fd4f7bffe002e4bca39c1d Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 3 Apr 2025 12:23:33 +0200 Subject: [PATCH 004/117] ci: Fix the pre-commit checks (#124) For some reason, they used to skip the files to check --- .github/workflows/pre-commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 78fc74fc..1e243c22 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -41,4 +41,4 @@ jobs: - name: Run pre-commit uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: - extra_args: --show-diff-on-failure + extra_args: --verbose --all-files --show-diff-on-failure From 4b48f1fa3aa246d10777c12c26dc4a0ffcecab33 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 3 Apr 2025 12:28:17 +0200 Subject: [PATCH 005/117] ci: Always run the required PR checks (#125) Otherwise, they will be stuck as Pending in the list of checks on the PR view --- .github/workflows/lint.yaml | 6 ------ .github/workflows/pre-commit.yaml | 5 ----- .github/workflows/test.yaml | 6 ------ 3 files changed, 17 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0dbfb024..6c55e18b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -7,12 +7,6 @@ on: - rhdh-1.[0-9]+ - 1.[0-9]+.x - release-1.[0-9]+ - paths: - - "charts/**" - - ".github/workflows/lint.yaml" - - "ct-install.yaml" - - "ct.yaml" - - ".helmignore" concurrency: group: ${{ github.workflow }}-${{ github.event.number }} diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 1e243c22..d098d1fc 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -7,11 +7,6 @@ on: - rhdh-1.[0-9]+ - 1.[0-9]+.x - release-1.[0-9]+ - paths: - - "charts/**" - - ".github/workflows/pre-commit.yaml" - - ".pre-commit/**" - - ".pre-commit-config.yaml" concurrency: group: ${{ github.workflow }}-${{ github.event.number }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4b6cc958..694dd24c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,12 +7,6 @@ on: - rhdh-1.[0-9]+ - 1.[0-9]+.x - release-1.[0-9]+ - paths: - - 'charts/**' - - '.github/workflows/test.yaml' - - 'ct-install.yaml' - - 'ct.yaml' - - '.helmignore' concurrency: group: ${{ github.workflow }}-${{ github.event.number }} From 4b8672beae9816ada6864207a1889147f31a07f0 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 3 Apr 2025 12:46:08 +0200 Subject: [PATCH 006/117] ci: Skip running some expensive steps (like cluster creation) if there are no changed charts (#126) * ci: Skip running some expensive steps (like cluster creation) if there are no changed charts * [TO REMOVE] Test the changes by bumping the orchestrator-chart version * Revert "[TO REMOVE] Test the changes by bumping the orchestrator-chart version" This reverts commit 9f91bb7726ab5a3781e37166d2fceaf74f45f408. --- .github/workflows/lint.yaml | 11 ++++++++++- .github/workflows/test.yaml | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 6c55e18b..6745d350 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -38,7 +38,16 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 - - name: Lint charts + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --target-branch "${{ github.event.pull_request.base.ref }}") + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Run chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' run: | ct lint \ --debug \ diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 694dd24c..6ed37964 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -51,7 +51,16 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --target-branch "${{ github.event.pull_request.base.ref }}") + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + - name: "Add NGINX Ingress and Bitnami Repository" + if: steps.list-changed.outputs.changed == 'true' run: | helm repo add ingress-nginx "https://kubernetes.github.io/ingress-nginx" helm repo add bitnami "https://charts.bitnami.com/bitnami" @@ -59,6 +68,7 @@ jobs: helm repo update - name: Generate KinD Config + if: steps.list-changed.outputs.changed == 'true' run: | cat < /tmp/kind-config.yaml apiVersion: kind.x-k8s.io/v1alpha4 @@ -75,11 +85,13 @@ jobs: EOF - name: Create KIND Cluster + if: steps.list-changed.outputs.changed == 'true' uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 with: config: /tmp/kind-config.yaml - name: Create custom storage class + if: steps.list-changed.outputs.changed == 'true' run: | export defaultScProvisioner=$(kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].provisioner}') if [[ -z "$defaultScProvisioner" ]]; then @@ -102,12 +114,14 @@ jobs: kubectl get storageclass custom-sc -o yaml - name: Install Ingress Controller + if: steps.list-changed.outputs.changed == 'true' run: | helm install ingress-nginx/ingress-nginx --generate-name \ --set controller.service.type='NodePort' \ --set controller.admissionWebhooks.enabled=false - name: Install Operator Lifecycle Manager (OLM) + if: steps.list-changed.outputs.changed == 'true' # In case we need to install additional Operators env: OLM_VERSION: "v0.31.0" @@ -116,7 +130,8 @@ jobs: chmod +x install-olm.sh ./install-olm.sh "${OLM_VERSION}" - - name: Run chart-testing + - name: Run chart-testing (install) + if: steps.list-changed.outputs.changed == 'true' run: | ct install \ --debug \ From 8e64bba2c5448606ffa125339d20d69ce3226296 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 11 Apr 2025 10:52:24 +0200 Subject: [PATCH 007/117] ci: Add missing Helm repos for the lint job to pass (#128) --- .github/workflows/lint.yaml | 7 +++++++ .github/workflows/test.yaml | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 6745d350..fc665629 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -46,6 +46,13 @@ jobs: echo "changed=true" >> "$GITHUB_OUTPUT" fi + - name: Add Helm Repositories + if: steps.list-changed.outputs.changed == 'true' + run: | + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo add backstage https://backstage.github.io/charts + helm repo update + - name: Run chart-testing (lint) if: steps.list-changed.outputs.changed == 'true' run: | diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6ed37964..6b26e0d7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -59,11 +59,11 @@ jobs: echo "changed=true" >> "$GITHUB_OUTPUT" fi - - name: "Add NGINX Ingress and Bitnami Repository" + - name: Add Helm Repositories if: steps.list-changed.outputs.changed == 'true' run: | - helm repo add ingress-nginx "https://kubernetes.github.io/ingress-nginx" - helm repo add bitnami "https://charts.bitnami.com/bitnami" + helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx + helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo update From fcbe47433db3ecb8d415e798194c9b6704719da8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 09:25:50 +0000 Subject: [PATCH 008/117] chore(deps): update helm release backstage to v2.5.0 (#127) * chore(deps): update helm release backstage to v2.5.0 * Bump version to 3.3.0 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: Armel Soro --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 29 +++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 4956e5c8..0a794f2e 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.27.0 - name: backstage repository: https://backstage.github.io/charts - version: 2.4.1 -digest: sha256:a39b54ac16f5d3e0479374516bea34e027a0a966a63ca07e34437b2683ef1035 -generated: "2025-03-31T03:48:25.883707785Z" + version: 2.5.0 +digest: sha256:225fc06041353e1a4036a0e741f61c7b8ffc179cf86443ea2c7acc2dc839cfa6 +generated: "2025-04-10T19:06:25.467234613Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index f54cf00e..a7d3fc7e 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -24,7 +24,7 @@ dependencies: version: "2.27.0" - name: backstage repository: https://backstage.github.io/charts - version: "2.4.1" + version: "2.5.0" alias: upstream home: https://redhat-developer.github.io/rhdh-chart/ icon: https://avatars.githubusercontent.com/u/117844786 @@ -44,4 +44,4 @@ sources: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 3.2.3 +version: 3.3.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index a4372a17..36bf48fb 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -2,7 +2,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 3.2.3](https://img.shields.io/badge/Version-3.2.3-informational?style=flat-square) +![Version: 3.3.0](https://img.shields.io/badge/Version-3.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -174,7 +174,7 @@ Kubernetes: `>= 1.25.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.4.1 | +| https://backstage.github.io/charts | upstream(backstage) | 2.5.0 | | https://charts.bitnami.com/bitnami | common | 2.27.0 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 2d8538d4..5b9b1f82 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -5794,6 +5794,35 @@ "title": "Node labels for pod assignment", "type": "object" }, + "pdb": { + "additionalProperties": false, + "properties": { + "create": { + "default": false, + "description": "Create a PDB", + "title": "Backstage PDB", + "type": "boolean" + }, + "maxUnavailable": { + "default": "", + "title": "Backstage PDB maxUnavailable", + "type": [ + "string", + "integer" + ] + }, + "minAvailable": { + "default": "", + "title": "Backstage PDB minAvailable", + "type": [ + "string", + "integer" + ] + } + }, + "title": "PDB parameters", + "type": "object" + }, "podAnnotations": { "additionalProperties": { "type": "string" From 224f562c3429e3e8cf050e6c3d8955b64652d7aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 13:08:14 +0000 Subject: [PATCH 009/117] chore(deps): update helm release backstage to v2.5.1 (#129) * chore(deps): update helm release backstage to v2.5.1 * Bump version to 3.3.1 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 0a794f2e..30d3157f 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.27.0 - name: backstage repository: https://backstage.github.io/charts - version: 2.5.0 -digest: sha256:225fc06041353e1a4036a0e741f61c7b8ffc179cf86443ea2c7acc2dc839cfa6 -generated: "2025-04-10T19:06:25.467234613Z" + version: 2.5.1 +digest: sha256:8b338c0fa46d275882bd248e8819bd505e32293abfcc7edecb048cb8c7fc5e69 +generated: "2025-04-14T16:36:55.762004212Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index a7d3fc7e..a86af94a 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -24,7 +24,7 @@ dependencies: version: "2.27.0" - name: backstage repository: https://backstage.github.io/charts - version: "2.5.0" + version: "2.5.1" alias: upstream home: https://redhat-developer.github.io/rhdh-chart/ icon: https://avatars.githubusercontent.com/u/117844786 @@ -44,4 +44,4 @@ sources: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 3.3.0 +version: 3.3.1 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 36bf48fb..dd08a9f6 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -2,7 +2,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 3.3.0](https://img.shields.io/badge/Version-3.3.0-informational?style=flat-square) +![Version: 3.3.1](https://img.shields.io/badge/Version-3.3.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -174,7 +174,7 @@ Kubernetes: `>= 1.25.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.5.0 | +| https://backstage.github.io/charts | upstream(backstage) | 2.5.1 | | https://charts.bitnami.com/bitnami | common | 2.27.0 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 5b9b1f82..44b14c44 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -6731,7 +6731,7 @@ } ], "default": "http-metrics", - "description": "The port where the metrics are exposed. If using OpenTelemetry as [documented here](https://backstage.io/docs/tutorials/setup-opentelemetry/), then the port needs to be explicitely specificed. OpenTelemetry's default port is 9464.", + "description": "The port where the metrics are exposed. If using OpenTelemetry as [documented here](https://backstage.io/docs/tutorials/setup-opentelemetry/), then the port needs to be explicitly specified. OpenTelemetry's default port is 9464.", "title": "ServiceMonitor endpoint port" } }, From 7dd4b5eefcdfb9750a636f1f9da794b7107ad8af Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 17 Apr 2025 11:49:47 +0100 Subject: [PATCH 010/117] chore(chart): fix Orchestrator Infra Chart metadata to avoid conflicts with RHDH Chart (#131) * Update metadata of the RHDH Orch Chart Signed-off-by: Fortune-Ndlovu * chore: bump chart version to 0.0.4 * Remove -Ci suffix Signed-off-by: Fortune-Ndlovu * Clarify description: both required to configure Red Hat Developer Hub to use the Orchestrator. Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- charts/orchestrator-infra/Chart.yaml | 9 +++++---- charts/orchestrator-infra/README.md | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index aaadad72..d191a570 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -1,11 +1,12 @@ annotations: - charts.openshift.io/name: Orchestrator-Infra + charts.openshift.io/name: Orchestrator Infrastructure for Red Hat Developer Hub charts.openshift.io/provider: Red Hat Developer Hub Team charts.openshift.io/supportURL: https://issues.redhat.com/browse/RHIDP apiVersion: v2 -name: orchestrator-infra +name: redhat-developer-hub-orchestrator-infra description: > - Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Logic Operator and OpenShift Serverless Operator. + Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including + OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. kubeVersion: ">= 1.25.0-0" maintainers: - name: Red Hat Developer Hub Team @@ -13,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.0.3 +version: 0.0.4 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 4883c565..c6556b8e 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,10 +1,10 @@ # Orchestrator Infra Chart for OpenShift (Community Version) -![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) +![Version: 0.0.4](https://img.shields.io/badge/Version-0.0.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) -Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Logic Operator and OpenShift Serverless Operator. +Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. ## Maintainers From 252039a033f3413305977eff1efeac21a290beaf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:53:57 +0200 Subject: [PATCH 011/117] chore(deps): update stefanzweifel/git-auto-commit-action digest to b863ae1 (#132) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index fbb69139..9af15113 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -109,7 +109,7 @@ jobs: - name: Commit pre-commit changes if: steps.command.outputs.command-name == 'bump' - uses: stefanzweifel/git-auto-commit-action@e348103e9026cc0eee72ae06630dbe30c8bf7a79 # v5 + uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5 with: commit_message: Bump version to ${{ steps.semver.outputs.new_version }} commit_options: '-s' From 051aeb750b2b30fcbde67dbe3a1ba768e30075d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:56:04 +0200 Subject: [PATCH 012/117] chore(deps): update helm release common to v2.30.0 (#133) * chore(deps): update helm release common to v2.30.0 * Bump version to 3.4.0 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 30d3157f..1cfc71e0 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: https://charts.bitnami.com/bitnami - version: 2.27.0 + version: 2.30.0 - name: backstage repository: https://backstage.github.io/charts version: 2.5.1 -digest: sha256:8b338c0fa46d275882bd248e8819bd505e32293abfcc7edecb048cb8c7fc5e69 -generated: "2025-04-14T16:36:55.762004212Z" +digest: sha256:1919b6bd7d69f0c2f34cf29c5560427893eafeb92352e1da57b7b52885242b60 +generated: "2025-04-22T13:07:58.036321179Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index a86af94a..0f639968 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -21,7 +21,7 @@ dependencies: repository: https://charts.bitnami.com/bitnami tags: - bitnami-common - version: "2.27.0" + version: "2.30.0" - name: backstage repository: https://backstage.github.io/charts version: "2.5.1" @@ -44,4 +44,4 @@ sources: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 3.3.1 +version: 3.4.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index dd08a9f6..900edc44 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -2,7 +2,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 3.3.1](https://img.shields.io/badge/Version-3.3.1-informational?style=flat-square) +![Version: 3.4.0](https://img.shields.io/badge/Version-3.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -175,7 +175,7 @@ Kubernetes: `>= 1.25.0-0` | Repository | Name | Version | |------------|------|---------| | https://backstage.github.io/charts | upstream(backstage) | 2.5.1 | -| https://charts.bitnami.com/bitnami | common | 2.27.0 | +| https://charts.bitnami.com/bitnami | common | 2.30.0 | ## Values From 43d10cb8091847695c1890eb912340c0ac63880a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:08:20 +0200 Subject: [PATCH 013/117] chore(deps): update sigstore/cosign-installer action to v3.8.2 (#134) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bd5caf68..cc9c23ff 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Install Cosign - uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1 + uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 - name: Install Oras uses: oras-project/setup-oras@5c0b487ce3fe0ce3ab0d034e63669e426e294e4d # v1.2.2 From d9c880e5483b8f4daa382de6170d5f0c6e627701 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:01:31 +0200 Subject: [PATCH 014/117] chore(deps): update actions/setup-python digest to a26af69 (#135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- .github/workflows/test.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 9af15113..43f4da8d 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -44,7 +44,7 @@ jobs: comment-id: ${{ github.event.comment.id }} reaction-type: eyes - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' with: python-version: 3.13 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fc665629..ed9ae69b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,7 +31,7 @@ jobs: with: version: v3.10.0 - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: 3.13 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index d098d1fc..561edaa1 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -22,7 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: 3.13 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6b26e0d7..f8ab90d3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,7 +44,7 @@ jobs: with: version: v3.10.0 - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: 3.13 From eb94968765aa77d5330b39334ad5f99a9684b91c Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 24 Apr 2025 17:26:28 +0100 Subject: [PATCH 015/117] chore: enable Snyk scan for orchestrator-infra chart (#136) * chore: enable Snyk scan for orchestrator-infra chart Signed-off-by: Fortune-Ndlovu * Run SNYK IaC Scan for Developer Hub Signed-off-by: Fortune-Ndlovu * fixup: sha is preferred than versioning Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- .github/workflows/snyk.yaml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index 54b94798..57f135f2 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -25,14 +25,26 @@ jobs: helm repo add backstage https://backstage.github.io/charts helm repo update helm dependency build ./charts/backstage - helm template ./charts/backstage/ --output-dir ./output + helm dependency build ./charts/orchestrator-infra + helm template ./charts/backstage --output-dir ./output/backstage + helm template ./charts/orchestrator-infra --output-dir ./output/orchestrator-infra - - name: Run SNYK IaC Scan + - name: Run SNYK IaC Scan for Developer Hub continue-on-error: true uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} with: - args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart" - file: ./output/ + args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/backstage" + file: ./output/backstage + + - name: Run Snyk IaC Scan for Orchestrator Infra + continue-on-error: true + uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} + with: + args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/orchestrator-infra" + file: ./output/orchestrator-infra From eed795192e0a9243b6f63122a6602442493d102c Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Fri, 25 Apr 2025 14:13:39 +0100 Subject: [PATCH 016/117] chore(ci): simplify Snyk workflow using matrix for chart scans (#137) * chore(ci): simplify Snyk workflow using matrix for chart scans Signed-off-by: Fortune-Ndlovu * chore(ci): enable workflow_dispatch and PR triggers for Snyk matrix scan testing Signed-off-by: Fortune-Ndlovu * chore(ci): dynamically name Snyk IaC scan step using chart name Signed-off-by: Fortune-Ndlovu * fixup: Removed path and output from the matrix, now inferred directly from chart name. AND Kept workflow_dispatch so WE can trigger this manually from GitHub. Signed-off-by: Fortune-Ndlovu * test Signed-off-by: Fortune-Ndlovu * chore(ci): remove debug step from Snyk matrix workflow Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- .github/workflows/snyk.yaml | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index 57f135f2..f20f76a0 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -1,13 +1,21 @@ name: SNYK on: + workflow_dispatch: # allow manual runs from UI schedule: - - cron: "0 0 * * 0" + - cron: "0 0 * * 0" # weekly scan (every Sunday at 00:00 UTC) jobs: scan-iac: name: Scan Rendered Templates runs-on: ubuntu-latest + + strategy: + matrix: + chart: + - backstage + - orchestrator-infra + steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -19,32 +27,20 @@ jobs: with: version: v3.17.0 - - name: Render Templates + - name: Render Templates for ${{ matrix.chart }} run: | helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts - helm repo update - helm dependency build ./charts/backstage - helm dependency build ./charts/orchestrator-infra - helm template ./charts/backstage --output-dir ./output/backstage - helm template ./charts/orchestrator-infra --output-dir ./output/orchestrator-infra - - - name: Run SNYK IaC Scan for Developer Hub - continue-on-error: true - uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} - with: - args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/backstage" - file: ./output/backstage + helm repo update + helm dependency build ./charts/${{ matrix.chart }} + helm template ./charts/${{ matrix.chart }} --output-dir ./output/${{ matrix.chart }} - - name: Run Snyk IaC Scan for Orchestrator Infra + - name: Run Snyk IaC Scan for ${{ matrix.chart }} continue-on-error: true uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} with: - args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/orchestrator-infra" - file: ./output/orchestrator-infra + args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/${{ matrix.chart }}" + file: ./output/${{ matrix.chart }} From ea0243d936b09974ecfd97cf29f93512c3610646 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:47:04 +0300 Subject: [PATCH 017/117] Merging Orchestrator with RHDH chart (#109) * Orchestrator merged with backstage chart * Readme changes * changes to chart version and value lint issue * Changes after PR review * chart bump * updated plugins to correct versions * correct orchestrator plugins - official * network policy pod selection + servicebinding default * chart version bump * orchestrator value flattened * values json update * Exist check for sonataflowplatform * sonataflow job working for local psql * Integrates sonataflowplatform with external DB + readme instructions * making job image configurable * moved wait to init container * configurable images and comments * chart bump * chart bump to 3.5.0 * lint issues * bumped version to 4.0.0 * conditionals for templates and error messages --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 43 ++++- charts/backstage/README.md.gotmpl | 27 ++++ charts/backstage/templates/_helpers.tpl | 2 +- charts/backstage/templates/_partials.tpl | 49 ++++++ .../templates/dynamic-plugins-configmap.yaml | 32 +++- .../backstage/templates/network-policies.yaml | 74 +++++++++ charts/backstage/templates/sonataflows.yaml | 133 ++++++++++++++++ charts/backstage/values.schema.json | 140 +++++++++++++++++ charts/backstage/values.schema.tmpl.json | 148 +++++++++++++++++- charts/backstage/values.yaml | 34 ++++ charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 2 +- charts/orchestrator-infra/templates/NOTES.txt | 6 +- 14 files changed, 681 insertions(+), 13 deletions(-) create mode 100644 charts/backstage/templates/_partials.tpl create mode 100644 charts/backstage/templates/network-policies.yaml create mode 100644 charts/backstage/templates/sonataflows.yaml diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 0f639968..edafba8b 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -44,4 +44,4 @@ sources: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 3.4.0 +version: 4.0.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 900edc44..f2d47c80 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -2,7 +2,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 3.4.0](https://img.shields.io/badge/Version-3.4.0-informational?style=flat-square) +![Version: 4.0.0](https://img.shields.io/badge/Version-4.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -191,6 +191,20 @@ Kubernetes: `>= 1.25.0-0` | global.dynamic.includes[0] | List of dynamic plugins included inside the `janus-idp/backstage-showcase` container image, some of which are disabled by default. This file ONLY works with the `janus-idp/backstage-showcase` container image. | string | `"dynamic-plugins.default.yaml"` | | global.dynamic.plugins | List of dynamic plugins, possibly overriding the plugins listed in `includes` files. Every item defines the plugin `package` as a [NPM package spec](https://docs.npmjs.com/cli/v10/using-npm/package-spec), an optional `pluginConfig` with plugin-specific backstage configuration, and an optional `disabled` flag to disable/enable a plugin listed in `includes` files. It also includes an `integrity` field that is used to verify the plugin package [integrity](https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description). | list | `[]` | | global.host | Custom hostname shorthand, overrides `global.clusterRouterBase`, `upstream.ingress.host`, `route.host`, and url values in `upstream.backstage.appConfig`. | string | `""` | +| orchestrator.enabled | | bool | `false` | +| orchestrator.serverlessLogicOperator.enabled | | bool | `false` | +| orchestrator.serverlessOperator.enabled | | bool | `false` | +| orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"postgres:15"` | +| orchestrator.sonataflowPlatform.eventing.broker.name | | string | `""` | +| orchestrator.sonataflowPlatform.eventing.broker.namespace | | string | `""` | +| orchestrator.sonataflowPlatform.externalDBName | Name for the user-configured external Database | string | `""` | +| orchestrator.sonataflowPlatform.externalDBsecretRef | Secret name for the user-created secret to connect an external DB | string | `""` | +| orchestrator.sonataflowPlatform.initContainerImage | Image for the init container used by the create-db job | string | `"busybox"` | +| orchestrator.sonataflowPlatform.monitoring.enabled | | bool | `true` | +| orchestrator.sonataflowPlatform.resources.limits.cpu | | string | `"500m"` | +| orchestrator.sonataflowPlatform.resources.limits.memory | | string | `"1Gi"` | +| orchestrator.sonataflowPlatform.resources.requests.cpu | | string | `"250m"` | +| orchestrator.sonataflowPlatform.resources.requests.memory | | string | `"64Mi"` | | route | OpenShift Route parameters | object | `{"annotations":{},"enabled":true,"host":"{{ .Values.global.host }}","path":"/","tls":{"caCertificate":"","certificate":"","destinationCACertificate":"","enabled":true,"insecureEdgeTerminationPolicy":"Redirect","key":"","termination":"edge"},"wildcardPolicy":"None"}` | | route.annotations | Route specific annotations | object | `{}` | | route.enabled | Enable the creation of the route resource | bool | `true` | @@ -320,3 +334,30 @@ upstream: volumePermissions: enabled: true ``` + +## Installing RHDH with Orchestrator + +Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, on boarding developers ,and user-made workflows of Backstage actions or external systems. +Orchestrator is a flavor of RHDH, and can be installed alongside the RHDH in the same namespace and in the folloing way: + +1. Have an admin install the orchestrator-infra helm chart, which will install the pre-requisites required to install RHDH flavored Orchestrator. This proccess will include installing cluster-wide resources, so should be done with admin privileges +``` +helm install charts/orchestrator-infra +``` +2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. +3. Install backstage chart with helm, setting orchestrator to be enabled. +4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. + +To use orchestrator with an external DB, please follow the instructions in [our documentation](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md) +and populate the following values in the values.yaml: +```bash + externalDBsecretRef: + externalDBName: "" +``` +Please note that externalDBName is the name of the user-configured existing database, not the database that orchestrator and sonataflow resources will use. + +Finally, install the helm chart: +``` +helm install charts/backstage --set orchestrator.enabled=true --set orchestrator.serverlessLogicOperator.enabled=true --set orchestrator.serverlessOperator.enabled=true \ +--set externalDBsecretRef= --set externalDBName=example +``` \ No newline at end of file diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index 6b7e0a4c..fd7ba75a 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -269,3 +269,30 @@ upstream: volumePermissions: enabled: true ``` + +## Installing RHDH with Orchestrator + +Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, on boarding developers ,and user-made workflows of Backstage actions or external systems. +Orchestrator is a flavor of RHDH, and can be installed alongside the RHDH in the same namespace and in the folloing way: + +1. Have an admin install the orchestrator-infra helm chart, which will install the pre-requisites required to install RHDH flavored Orchestrator. This proccess will include installing cluster-wide resources, so should be done with admin privileges +``` +helm install charts/orchestrator-infra +``` +2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. +3. Install backstage chart with helm, setting orchestrator to be enabled. +4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. + +To use orchestrator with an external DB, please follow the instructions in [our documentation](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md) +and populate the following values in the values.yaml: +```bash + externalDBsecretRef: + externalDBName: "" +``` +Please note that externalDBName is the name of the user-configured existing database, not the database that orchestrator and sonataflow resources will use. + +Finally, install the helm chart: +``` +helm install charts/backstage --set orchestrator.enabled=true --set orchestrator.serverlessLogicOperator.enabled=true --set orchestrator.serverlessOperator.enabled=true \ +--set externalDBsecretRef= --set externalDBName=example +``` \ No newline at end of file diff --git a/charts/backstage/templates/_helpers.tpl b/charts/backstage/templates/_helpers.tpl index a6cd9cff..b1210191 100644 --- a/charts/backstage/templates/_helpers.tpl +++ b/charts/backstage/templates/_helpers.tpl @@ -47,4 +47,4 @@ Referenced from: https://github.com/bitnami/charts/blob/main/bitnami/postgresql/ {{- else -}} {{- printf "%s" (include "common.names.fullname" .) -}} {{- end -}} -{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/backstage/templates/_partials.tpl b/charts/backstage/templates/_partials.tpl new file mode 100644 index 00000000..e36be41b --- /dev/null +++ b/charts/backstage/templates/_partials.tpl @@ -0,0 +1,49 @@ +{{- define "orchestrator.plugins" }} +{{- $config := include "orchestrator.plugins.config" . | fromYaml }} +plugins: + - disabled: false + package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestratorBackend.package }}" + integrity: "{{ $config.orchestratorPlugins.orchestratorBackend.integrity }}" + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + - disabled: false + package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestrator.package }}" + integrity: "{{ $config.orchestratorPlugins.orchestrator.integrity }}" + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator + - disabled: true + package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.package }}" + integrity: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.integrity }}"{{- end }} + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + +{{- define "orchestrator.plugins.config" }} +orchestratorPlugins: + scope: "@redhat" + orchestrator: + package: "backstage-plugin-orchestrator@1.5.1" + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + orchestratorBackend: + package: "backstage-plugin-orchestrator-backend-dynamic@1.5.1" + integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== + scaffolderBackendOrchestrator: + package: "backstage-plugin-scaffolder-backend-module-orchestrator-dynamic@1.5.1" + integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== +{{- end }} \ No newline at end of file diff --git a/charts/backstage/templates/dynamic-plugins-configmap.yaml b/charts/backstage/templates/dynamic-plugins-configmap.yaml index 1239a67b..a8af0614 100644 --- a/charts/backstage/templates/dynamic-plugins-configmap.yaml +++ b/charts/backstage/templates/dynamic-plugins-configmap.yaml @@ -4,5 +4,33 @@ metadata: name: {{ printf "%s-dynamic-plugins" .Release.Name }} data: dynamic-plugins.yaml: | - {{- include "common.tplvalues.render" ( dict "value" - .Values.global.dynamic "context" $) | nindent 4 }} + {{- $dynamic := deepCopy .Values.global.dynamic }} + {{- $plugins := list }} + + {{- range .Values.global.dynamic.plugins }} + {{- $plugins = append $plugins . }} + {{- end }} + + {{- if .Values.orchestrator.enabled }} + {{- $orchestratorPlugins := include "orchestrator.plugins" . | fromYaml }} + {{- range $orchestratorPlugins.plugins }} + {{- $plugins = append $plugins . }} + {{- end }} + {{- end }} + + {{- $_ := set $dynamic "plugins" $plugins }} + + {{- include "common.tplvalues.render" (dict "value" $dynamic "context" $) | nindent 4 }} + +--- +{{- if .Values.orchestrator.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name}}-dynamic-plugins-npmrc + namespace: {{ .Release.Namespace }} +type: Opaque +stringData: + .npmrc: | + @redhat:registry=https://npm.registry.redhat.com +{{- end }} \ No newline at end of file diff --git a/charts/backstage/templates/network-policies.yaml b/charts/backstage/templates/network-policies.yaml new file mode 100644 index 00000000..1979e4f3 --- /dev/null +++ b/charts/backstage/templates/network-policies.yaml @@ -0,0 +1,74 @@ +{{- if and .Values.orchestrator.enabled .Values.orchestrator.serverlessLogicOperator.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-allow-knative-to-sonataflow-and-workflows + # Sonataflow and Workflows are using the RHDH target namespace. + namespace: {{ .Release.Namespace | quote }} +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: knative-eventing + - namespaceSelector: + matchLabels: + # Allow auxiliary knative function for workflow (such as m2k-save-transformation) + kubernetes.io/metadata.name: knative-serving +--- +# NetworkPolicy to unblock incoming traffic to the namespace +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-allow-external-communication + namespace: {{ .Release.Namespace | quote }} +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: openshift-ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-allow-intra-network + namespace: {{ .Release.Namespace | quote }} +spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + # Allow ingress from any pod within the same namespace + - podSelector: {} + + +{{- end }} +--- +{{- if and .Values.orchestrator.enabled .Values.orchestrator.sonataflowPlatform.monitoring.enabled }} +# NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-allow-monitoring-to-sonataflow-and-workflows + namespace: {{ .Release.Namespace | quote }} +spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow openshift-user-workload-monitoring pods to access the workflow. + kubernetes.io/metadata.name: openshift-user-workload-monitoring +{{- end }} \ No newline at end of file diff --git a/charts/backstage/templates/sonataflows.yaml b/charts/backstage/templates/sonataflows.yaml new file mode 100644 index 00000000..aec16d17 --- /dev/null +++ b/charts/backstage/templates/sonataflows.yaml @@ -0,0 +1,133 @@ +{{- if and (default false .Values.orchestrator.enabled) (default false .Values.orchestrator.serverlessLogicOperator.enabled) }} +{{- $sonataflowplatformExists := lookup "sonataflow.org/v1alpha08" "SonataFlowPlatform" .Release.Namespace "sonataflow-platform" }} +{{- if and .Release.IsInstall $sonataflowplatformExists }} +{{- fail "Cannot create multiple sonataflowplatform in the same namespace, one already exists." }} +{{- end }} + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform + namespace: {{ .Release.Namespace }} +spec: + monitoring: + enabled: {{ .Values.orchestrator.sonataflowPlatform.monitoring.enabled }} + build: + template: + resources: + requests: + memory: {{ .Values.orchestrator.sonataflowPlatform.resources.requests.memory }} + cpu: {{ .Values.orchestrator.sonataflowPlatform.resources.requests.cpu }} + limits: + memory: {{ .Values.orchestrator.sonataflowPlatform.resources.limits.memory }} + cpu: {{ .Values.orchestrator.sonataflowPlatform.resources.limits.cpu }} + {{- if (and (.Values.orchestrator.sonataflowPlatform.eventing.broker.name) (.Values.orchestrator.sonataflowPlatform.eventing.broker.namespace)) }} + eventing: + broker: + ref: + apiVersion: eventing.knative.dev/v1 + kind: Broker + name: {{ .Values.orchestrator.sonataflowPlatform.eventing.broker.name }} + namespace: {{ .Values.orchestrator.sonataflowPlatform.eventing.broker.namespace }} + {{- end }} + services: + dataIndex: + enabled: true + persistence: + postgresql: + {{- if .Values.upstream.postgresql.enabled }} + secretRef: + name: {{ .Release.Name }}-postgresql-svcbind-postgres + userKey: username + passwordKey: password + serviceRef: + name: {{ .Release.Name }}-postgresql + namespace: {{ .Release.Namespace }} + databaseName: sonataflow + {{- else }} + secretRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + jdbcUrl: jdbc:postgresql://{{ .Values.upstream.backstage.appConfig.backend.database.connection.host}}:{{ .Values.upstream.backstage.appConfig.backend.database.connection.port}}/sonataflow?currentSchema=data-index-service + {{- end }} + {{- if .Values.orchestrator.sonataflowPlatform.dataIndexImage }} + podTemplate: + container: + image: {{ .Values.orchestrator.sonataflowPlatform.dataIndexImage }} + {{- end }} + jobService: + enabled: true + persistence: + postgresql: + {{- if .Values.upstream.postgresql.enabled }} + secretRef: + name: {{ .Release.Name }}-postgresql-svcbind-postgres + userKey: username + passwordKey: password + serviceRef: + name: {{ .Release.Name }}-postgresql + namespace: {{ .Release.Namespace }} + databaseName: sonataflow + {{- else }} + secretRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + jdbcUrl: jdbc:postgresql://{{ .Values.upstream.backstage.appConfig.backend.database.connection.host}}:{{ .Values.upstream.backstage.appConfig.backend.database.connection.port}}/sonataflow?currentSchema=jobs-service + {{- end }} + {{- if .Values.orchestrator.sonataflowPlatform.jobServiceImage }} + podTemplate: + container: + image: {{ .Values.orchestrator.sonataflowPlatform.jobServiceImage }} + {{- end }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-create-sonataflow-database + namespace: {{ .Release.Namespace }} +spec: + template: + spec: + initContainers: + - name: wait-for-db + image: {{ .Values.orchestrator.sonataflowPlatform.initContainerImage }} + command: + - sh + - -c +{{- if .Values.upstream.postgresql.enabled }} + - "until nc -z {{ .Release.Name }}-postgresql 5432; do echo 'Waiting for DB...'; sleep 2; done" +{{- else }} + - "until nc -z {{ .Values.upstream.backstage.appConfig.backend.database.connection.host }} {{ .Values.upstream.backstage.appConfig.backend.database.connection.port }}; do echo 'Waiting for external DB...'; sleep 2; done" +{{- end }} + containers: + - name: psql + image: {{ .Values.orchestrator.sonataflowPlatform.createDBJobImage }} + env: + - name: PGPASSWORD + valueFrom: +{{- if .Values.upstream.postgresql.enabled }} + secretKeyRef: + name: {{ .Release.Name }}-postgresql-svcbind-postgres + key: password + command: [ "sh", "-c" ] + args: + - "psql -h {{ .Release.Name }}-postgresql -U postgres -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" +{{- else }} + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef}} + key: POSTGRES_PASSWORD + command: [ "sh", "-c" ] + args: + - "psql -h {{ .Values.upstream.backstage.appConfig.backend.database.connection.host }} -U {{ .Values.upstream.backstage.appConfig.backend.database.connection.user }} -d {{ .Values.orchestrator.sonataflowPlatform.externalDBName }} -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" +{{- end }} + restartPolicy: Never + backoffLimit: 2 +{{- end }} + + + + + + diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 44b14c44..21be2ae2 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -91,6 +91,146 @@ }, "type": "object" }, + "orchestrator": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "title": "enabled flag", + "type": "boolean" + }, + "serverlessLogicOperator": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "title": "enabled flag", + "type": "boolean" + } + }, + "title": "serverlessLogicOperator configuration", + "type": "object" + }, + "serverlessOperator": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "title": "enabled flag", + "type": "boolean" + } + }, + "title": "serverlessOperator configuration", + "type": "object" + }, + "sonataflowPlatform": { + "additionalProperties": false, + "properties": { + "createDBJobImage": { + "additionalProperties": false, + "title": "Image for the container used by the create-db job", + "type": "string" + }, + "eventing": { + "additionalProperties": false, + "properties": { + "broker": { + "additionalProperties": false, + "properties": { + "name": { + "default": "", + "title": "name value", + "type": "string" + }, + "namespace": { + "default": "", + "title": "namespace value", + "type": "string" + } + }, + "title": "broker configuration", + "type": "object" + } + }, + "title": "eventing configuration", + "type": "object" + }, + "externalDBName": { + "additionalProperties": false, + "title": "Name for the user-configured external Database", + "type": "string" + }, + "externalDBsecretRef": { + "additionalProperties": false, + "title": "Secret name for the user-created secret to connect an external DB", + "type": "string" + }, + "initContainerImage": { + "additionalProperties": false, + "title": "Image for the init container used by the create-db job", + "type": "string" + }, + "monitoring": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "title": "enabled flag", + "type": "boolean" + } + }, + "title": "monitoring configuration", + "type": "object" + }, + "resources": { + "additionalProperties": false, + "properties": { + "limits": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "500m", + "title": "cpu value", + "type": "string" + }, + "memory": { + "default": "1Gi", + "title": "memory value", + "type": "string" + } + }, + "title": "limits configuration", + "type": "object" + }, + "requests": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "250m", + "title": "cpu value", + "type": "string" + }, + "memory": { + "default": "64Mi", + "title": "memory value", + "type": "string" + } + }, + "title": "requests configuration", + "type": "object" + } + }, + "title": "resources configuration", + "type": "object" + } + }, + "title": "sonataflowPlatform configuration", + "type": "object" + } + }, + "title": "orchestrator configuration", + "type": "object" + }, "route": { "additionalProperties": false, "properties": { diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index 61b8efc8..73110f0c 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -59,7 +59,9 @@ "default": false } }, - "required": ["package"] + "required": [ + "package" + ] } }, "includes": { @@ -110,7 +112,7 @@ "properties": { "annotations": { "title": "Route specific annotations.", - "type": "object", + "type": "object", "default": {} }, "enabled": { @@ -193,6 +195,146 @@ } } } + }, + "orchestrator": { + "title": "orchestrator configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "enabled flag", + "type": "boolean", + "default": false + }, + "serverlessLogicOperator": { + "title": "serverlessLogicOperator configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "enabled flag", + "type": "boolean", + "default": true + } + } + }, + "serverlessOperator": { + "title": "serverlessOperator configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "enabled flag", + "type": "boolean", + "default": true + } + } + }, + "sonataflowPlatform": { + "title": "sonataflowPlatform configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "monitoring": { + "title": "monitoring configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "enabled flag", + "type": "boolean", + "default": true + } + } + }, + "eventing": { + "title": "eventing configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "broker": { + "title": "broker configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "title": "name value", + "type": "string", + "default": "" + }, + "namespace": { + "title": "namespace value", + "type": "string", + "default": "" + } + } + } + } + }, + "resources": { + "title": "resources configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "requests": { + "title": "requests configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "memory value", + "type": "string", + "default": "64Mi" + }, + "cpu": { + "title": "cpu value", + "type": "string", + "default": "250m" + } + } + }, + "limits": { + "title": "limits configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "memory value", + "type": "string", + "default": "1Gi" + }, + "cpu": { + "title": "cpu value", + "type": "string", + "default": "500m" + } + } + } + } + }, + "externalDBsecretRef": { + "title": "Secret name for the user-created secret to connect an external DB", + "type": "string", + "additionalProperties": false + }, + "externalDBName": { + "title": "Name for the user-configured external Database", + "type": "string", + "additionalProperties": false + }, + "initContainerImage": { + "title": "Image for the init container used by the create-db job", + "type": "string", + "additionalProperties": false + }, + "createDBJobImage": { + "title": "Image for the container used by the create-db job", + "type": "string", + "additionalProperties": false + } + } + } + } } } -} +} \ No newline at end of file diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 403b5a93..81080365 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -243,6 +243,8 @@ upstream: postgresql: enabled: true postgresqlDataDir: /var/lib/pgsql/data/userdata + serviceBindings: + enabled: true image: registry: quay.io repository: fedora/postgresql-15 @@ -338,3 +340,35 @@ test: # -- Test connection pod image tag. Note that the image needs to have both the `sh` and `curl` binaries in it. tag: latest + +orchestrator: + enabled: false + serverlessLogicOperator: + enabled: false + serverlessOperator: + enabled: false + sonataflowPlatform: + monitoring: + enabled: true + eventing: + broker: + name: "" + namespace: "" + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + # -- Secret name for the user-created secret to connect an external DB + externalDBsecretRef: "" + + # -- Name for the user-configured external Database + externalDBName: "" + + # -- Image for the init container used by the create-db job + initContainerImage: busybox + + # -- Image for the container used by the create-db job + createDBJobImage: "postgres:15" diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index d191a570..50631e9b 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.0.4 +version: 0.0.5 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index c6556b8e..f1d519f6 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift (Community Version) -![Version: 0.0.4](https://img.shields.io/badge/Version-0.0.4-informational?style=flat-square) +![Version: 0.0.5](https://img.shields.io/badge/Version-0.0.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. diff --git a/charts/orchestrator-infra/templates/NOTES.txt b/charts/orchestrator-infra/templates/NOTES.txt index 00676355..1897ccb6 100644 --- a/charts/orchestrator-infra/templates/NOTES.txt +++ b/charts/orchestrator-infra/templates/NOTES.txt @@ -33,7 +33,7 @@ Red Hat Serverless Logic Operator {{ $serverlessLogicOperatorInstalled }} To manually approve the openshift-serverless InstallPlan: -OS_PLAN=$(oc get installplan -n openshift-serverless) +OS_PLAN=$(oc get installplan -n openshift-serverless --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[0].metadata.name}') oc patch installplan $OS_PLAN -n openshift-serverless --type merge --patch '{"spec":{"approved":true}}' {{- end }} @@ -41,6 +41,6 @@ oc patch installplan $OS_PLAN -n openshift-serverless --type merge --patch '{"sp To manually approve the openshift-serverless-logic InstallPlan: -OSL_PLAN=$(oc get installplan -n openshift-serverless) -oc patch installplan $OSL_PLAN -n openshift-serverless --type merge --patch '{"spec":{"approved":true}}' +OSL_PLAN=$(oc get installplan -n openshift-serverless-logic --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[0].metadata.name}') +oc patch installplan $OSL_PLAN -n openshift-serverless-logic --type merge --patch '{"spec":{"approved":true}}' {{- end }} From ab9f81e6492b405e7caf9965568d2eef4e3fa03d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:31:57 +0200 Subject: [PATCH 018/117] chore(deps): update oras-project/setup-oras action to v1.2.3 (#140) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cc9c23ff..721432b3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -50,7 +50,7 @@ jobs: uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 - name: Install Oras - uses: oras-project/setup-oras@5c0b487ce3fe0ce3ab0d034e63669e426e294e4d # v1.2.2 + uses: oras-project/setup-oras@8d34698a59f5ffe24821f0b48ab62a3de8b64b20 # v1.2.3 - name: Publish and Sign OCI Charts run: | From 7f0c9da8e7d11274bd0e77bd3b7f8259833fc1ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:32:43 +0200 Subject: [PATCH 019/117] chore(deps): update mikefarah/yq action to v4.45.2 (#142) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 43f4da8d..770a761c 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -81,7 +81,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@8bf425b4d1344db7cd469a8d10a390876e0c77fd # v4.45.1 + uses: mikefarah/yq@c59fa8de59f1f5a16fdbfd1e2a6e97a1b42a64b9 # v4.45.2 with: cmd: yq ".version" charts/backstage/Chart.yaml @@ -94,7 +94,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@8bf425b4d1344db7cd469a8d10a390876e0c77fd # v4.45.1 + uses: mikefarah/yq@c59fa8de59f1f5a16fdbfd1e2a6e97a1b42a64b9 # v4.45.2 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml From 0ef375ed87e784de5ef2c7113ffc855e7b862083 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Mon, 5 May 2025 16:49:35 +0300 Subject: [PATCH 020/117] chore: add MAX_ENTRY_SIZE var to values + doc change (#143) --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 19 ++++++++++++++++++- charts/backstage/README.md.gotmpl | 17 +++++++++++++++++ charts/backstage/values.schema.json | 4 ++++ charts/backstage/values.yaml | 3 +++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index edafba8b..6d4bb148 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -44,4 +44,4 @@ sources: # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.0.0 +version: 4.0.1 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index f2d47c80..84a74e8b 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -2,7 +2,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 4.0.0](https://img.shields.io/badge/Version-4.0.0-informational?style=flat-square) +![Version: 4.0.1](https://img.shields.io/badge/Version-4.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -348,6 +348,23 @@ helm install charts/orchestrator-infra 3. Install backstage chart with helm, setting orchestrator to be enabled. 4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. +### Enablement of Notifications Plugin + +To enable the notifications and signals plugin, please edit the dynamic plugin configmap after the installation and add the following: +``` +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-notifications" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-signals" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-notifications-backend-dynamic" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-signals-backend-dynamic" +``` +Enabling these plugins will allow you to recieve notifications from workflows running with Orchestrator. + +### Using Orchestrator while configuring an ExternalDB + To use orchestrator with an external DB, please follow the instructions in [our documentation](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md) and populate the following values in the values.yaml: ```bash diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index fd7ba75a..8cd6d7c3 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -283,6 +283,23 @@ helm install charts/orchestrator-infra 3. Install backstage chart with helm, setting orchestrator to be enabled. 4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. +### Enablement of Notifications Plugin + +To enable the notifications and signals plugin, please edit the dynamic plugin configmap after the installation and add the following: +``` +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-notifications" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-signals" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-notifications-backend-dynamic" +- disabled: false + package: "./dynamic-plugins/dist/backstage-plugin-signals-backend-dynamic" +``` +Enabling these plugins will allow you to recieve notifications from workflows running with Orchestrator. + +### Using Orchestrator while configuring an ExternalDB + To use orchestrator with an external DB, please follow the instructions in [our documentation](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md) and populate the following values in the values.yaml: ```bash diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 21be2ae2..e6188b59 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -4493,6 +4493,10 @@ { "name": "NPM_CONFIG_USERCONFIG", "value": "/opt/app-root/src/.npmrc.dynamic-plugins" + }, + { + "name": "MAX_ENTRY_SIZE", + "value": "30000000" } ], "image": "{{ include \"backstage.image\" . }}", diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 81080365..cd060d57 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -208,6 +208,9 @@ upstream: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + # This following variable is required for orchestrator to startup properly. + - name: MAX_ENTRY_SIZE + value: "30000000" imagePullPolicy: Always volumeMounts: - mountPath: /dynamic-plugins-root From 6c418ed01f4b7609ceb2690d941509fdb163ca80 Mon Sep 17 00:00:00 2001 From: Omar Al Jaljuli Date: Wed, 7 May 2025 12:46:19 -0400 Subject: [PATCH 021/117] chore: merge downstream chart content into rhdh-chart and include install script+docs (#130) rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED bump version rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- .github/workflows/release.yaml | 32 ---------- .rhdh/docs/installing-ci-charts.adoc | 20 +++++++ .rhdh/scripts/install.sh | 85 +++++++++++++++++++++++++++ README.md | 14 +++-- charts/backstage/Chart.yaml | 29 +++++---- charts/backstage/README.md | 39 ++++-------- charts/backstage/README.md.gotmpl | 24 +++----- charts/backstage/artifacthub-repo.yml | 12 ---- charts/backstage/values.schema.json | 2 +- charts/backstage/values.yaml | 3 +- 10 files changed, 154 insertions(+), 106 deletions(-) create mode 100644 .rhdh/docs/installing-ci-charts.adoc create mode 100755 .rhdh/scripts/install.sh delete mode 100644 charts/backstage/artifacthub-repo.yml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 721432b3..9e4a21cf 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -38,35 +38,3 @@ jobs: config: cr.yaml env: CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Install Cosign - uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 - - - name: Install Oras - uses: oras-project/setup-oras@8d34698a59f5ffe24821f0b48ab62a3de8b64b20 # v1.2.3 - - - name: Publish and Sign OCI Charts - run: | - for chart in `find .cr-release-packages -name '*.tgz' -print`; do - echo "[INFO] Handling chart at ${chart}..." - helm push ${chart} oci://ghcr.io/${GITHUB_REPOSITORY} |& tee helm-push-output.log - file_name=${chart##*/} - chart_name=${file_name%-*} - digest=$(awk -F "[, ]+" '/Digest/{print $NF}' < helm-push-output.log) - cosign sign -y "ghcr.io/${GITHUB_REPOSITORY}/${chart_name}@${digest}" - - if test -f "./charts/${chart_name}/artifacthub-repo.yml"; then - oras push "ghcr.io/${GITHUB_REPOSITORY}/${chart_name}:artifacthub.io" \ - "./charts/${chart_name}/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml" - fi - echo "[INFO] ... Done with chart at ${chart}." - done - env: - COSIGN_EXPERIMENTAL: 1 diff --git a/.rhdh/docs/installing-ci-charts.adoc b/.rhdh/docs/installing-ci-charts.adoc new file mode 100644 index 00000000..838606e8 --- /dev/null +++ b/.rhdh/docs/installing-ci-charts.adoc @@ -0,0 +1,20 @@ +== Installing CI builds of RHDH through the chart + +=== Script install prerequisites +* Openshift CLI +* Helm CLI + +=== Scripted installation + +To link:../scripts/install.sh[install] from a Helm Chart Registry, run the following commands: + +``` +cd /tmp +# Create or select a namespace +# Install the chart repo +# Install the chart, then update the clusterRouterBase +curl -sSLO https://raw.githubusercontent.com/redhat-developer/rhdh-chart/refs/heads/main/.rhdh/scripts/install.sh && chmod +x install.sh +./install.sh 1.7-20-CI --namespace rhdh-1-7-20-ci +``` + +That's it! diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh new file mode 100755 index 00000000..34634cf8 --- /dev/null +++ b/.rhdh/scripts/install.sh @@ -0,0 +1,85 @@ +# install a helm chart with the correct global.clusterRouterBase + +# default namespace if none set +namespace="rhdh-helm" +chartrepo=0 # by default don't create a new chart repo unless the version chart version includes "CI" suffix +github=0 # by default don't use the Github repo unless the chart doesn't exist in the OCI registry + +usage () +{ + echo "Usage: $0 CHART_VERSION [-n namespace] + +Examples: + $0 1.1.1 + $0 1.7-20-CI -n rhdh-ci + +Options: + -n, --namespace Project or namespace into which to install specified chart; default: $namespace + --github-repo If set will use the deprecated github repository to install the helm chart instead of the OCI registry. + --chartrepo If set, a Helm Chart Repo will be applied to the cluster, based on the chart version. + If CHART_VERSION ends in CI and --github-repo is set, this is done by default. + --router If set, the cluster router base is manually set. + Required for non-admin users + Redundant for admin users +" + exit +} + +if [[ $# -lt 1 ]]; then usage; fi + +while [[ "$#" -gt 0 ]]; do + case $1 in + '--chartrepo') chartrepo=1;; + '-n'|'--namespace') namespace="$2"; shift 1;; + '-h') usage;; + '--github-repo') github=1;; + '--router') CLUSTER_ROUTER_BASE="$2"; shift 1;; + *) CV="$1";; + esac + shift 1 +done + +if [[ ! "$CV" ]]; then usage; fi + +CHART_URL="oci://quay.io/rhdh/chart" + +if ! helm show chart $CHART_URL --version "$CV" &> /dev/null; then github=1; fi +if [[ $github -eq 1 ]]; then + # If a Github CI chart, create a chart repo + if [[ "$CV" == *"-CI" ]]; then chartrepo=1; fi + CHART_URL="https://github.com/rhdh-bot/openshift-helm-charts/raw/redhat-developer-hub-${CV}/charts/redhat/redhat/redhat-developer-hub/${CV}/redhat-developer-hub-${CV}.tgz" +fi + +echo "Using ${CHART_URL} to install Helm chart" + +# choose namespace for the install (or create if non-existant) +oc new-project "$namespace" || oc project "$namespace" + +if [[ $chartrepo -eq 1 ]]; then + oc apply -f https://github.com/rhdh-bot/openshift-helm-charts/raw/redhat-developer-hub-"${CV}"/installation/rhdh-next-ci-repo.yaml +fi + +# 1. install (or upgrade) +helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" + +# 2. collect values +PASSWORD=$(kubectl get secret redhat-developer-hub-postgresql -o jsonpath="{.data.password}" | base64 -d) +if [[ $(oc auth can-i get route/openshift-console) == "yes" ]]; then + CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') +elif [[ -z $CLUSTER_ROUTER_BASE ]]; then + echo "Error: openshift-console routes cannot be accessed with user permissions" + echo "Rerun command installation script with --router " + echo + usage + exit 1 +fi + +# 3. change values +helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" \ + --set global.clusterRouterBase="${CLUSTER_ROUTER_BASE}" \ + --set global.postgresql.auth.password="$PASSWORD" + +echo " +Once deployed, Developer Hub $CV will be available at +https://redhat-developer-hub-${namespace}.${CLUSTER_ROUTER_BASE} +" \ No newline at end of file diff --git a/README.md b/README.md index 6fe95d95..a4eed3a9 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ -# RHDH Backstage Helm Chart for OpenShift +# UPDATE -See [charts/backstage/README.md](charts/backstage/README.md). +This repository now houses the only RHDH CI Helm chart after merging with the now deprecated repository: https://github.com/rhdh-bot/openshift-helm-charts/. + +See: https://issues.redhat.com/browse/RHIDP-1477 -## Why this chart? +# RHDH Helm Chart for OpenShift + +See [charts/backstage/README.md](charts/backstage/README.md). -This is the "upstream" chart for RHDH, formerly the janus-idp chart. +# RHDH orchestrator infra Helm chart for Openshift -To alleviate the confusion of having charts in this repo and [artifacthub](https://artifacthub.io/packages/helm/rhdh-chart/backstage), AND the official charts in the [Openshift Helm Charts](https://github.com/openshift-helm-charts/charts) repo @ https://charts.openshift.io/ we intend to merge this content into the downstream chart via this issue: https://issues.redhat.com/browse/RHIDP-1477 +See [charts/orchestrator-infra/README.md](charts/orchestrator-infra/README.md) ## Contributing and reporting issues diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 6d4bb148..379f8ba3 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -7,10 +7,11 @@ annotations: - name: Chart Source url: https://github.com/redhat-developer/rhdh-chart - name: Default Image Source - url: https://github.com/janus-idp/backstage-showcase - charts.openshift.io/name: Backstage - charts.openshift.io/provider: Red Hat Developer Hub Team - charts.openshift.io/supportURL: https://issues.redhat.com/browse/RHIDP + url: https://github.com/redhat-developer/rhdh + charts.openshift.io/name: Red Hat Developer Hub + charts.openshift.io/provider: Red Hat + charts.openshift.io/archs: x86_64 + charts.openshift.io/supportURL: https://access.redhat.com/support apiVersion: v2 description: | A Helm chart for deploying Red Hat Developer Hub. @@ -26,22 +27,24 @@ dependencies: repository: https://backstage.github.io/charts version: "2.5.1" alias: upstream -home: https://redhat-developer.github.io/rhdh-chart/ -icon: https://avatars.githubusercontent.com/u/117844786 +home: https://red.ht/rhdh +icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjE5MS44OCIKICAgaGVpZ2h0PSIxOTEuODgiCiAgIHZpZXdCb3g9IjAgMCAxOTEuODggMTkxLjg4IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgiIC8+CiAgPGcKICAgICBpZD0idXVpZC03OTAxZjg3OC1jZTAwLTQ0MWYtYWMyNi1kZGQzNjU0ZDRmNzkiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxyZWN0CiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIHdpZHRoPSIzNiIKICAgICAgIGhlaWdodD0iMzYiCiAgICAgICByeD0iOSIKICAgICAgIHJ5PSI5IgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InJlY3QyIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjgsMi4yNSBjIDQuMjczMzYsMCA3Ljc1LDMuNDc2NjQgNy43NSw3Ljc1IHYgMTggYyAwLDQuMjczMzYgLTMuNDc2NjQsNy43NSAtNy43NSw3Ljc1IEggMTAgQyA1LjcyNjY0LDM1Ljc1IDIuMjUsMzIuMjczMzYgMi4yNSwyOCBWIDEwIEMgMi4yNSw1LjcyNjY0IDUuNzI2NjQsMi4yNSAxMCwyLjI1IEggMjggTSAyOCwxIEggMTAgQyA1LjAyOTQ0LDEgMSw1LjAyOTQzIDEsMTAgdiAxOCBjIDAsNC45NzA1NyA0LjAyOTQ0LDkgOSw5IGggMTggYyA0Ljk3MDU2LDAgOSwtNC4wMjk0MyA5LC05IFYgMTAgQyAzNyw1LjAyOTQzIDMyLjk3MDU2LDEgMjgsMSBaIgogICAgICAgZmlsbD0iIzRkNGQ0ZCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNCIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0idXVpZC1jM2NhNjg5MS02ZTE4LTQyY2ItODUyYi0zZGVkZDZjMzFlNjgiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI2LjQ0MjM4LDI1LjU1ODExIC0zLjc3Mzc0LC0zLjc3Mzc0IGMgMC41OTE0MywtMC43NzcwNCAwLjk1NjM2LC0xLjczNDggMC45NTYzNiwtMi43ODQzNiAwLC0yLjU1MDI5IC0yLjA3NTIsLTQuNjI1IC00LjYyNSwtNC42MjUgLTIuNTUwMjksMCAtNC42MjUsMi4wNzQ3MSAtNC42MjUsNC42MjUgMCwyLjU1MDI5IDIuMDc0NzEsNC42MjUgNC42MjUsNC42MjUgMS4wNDk0NCwwIDIuMDA3MjYsLTAuMzY0OTMgMi43ODQzNiwtMC45NTYzNiBsIDMuNzczMjUsMy43NzMyNSBjIDAuMTIyMDcsMC4xMjIwNyAwLjI4MjIzLDAuMTgzMTEgMC40NDIzOCwwLjE4MzExIDAuMTYwMTUsMCAwLjMyMDMxLC0wLjA2MTA0IDAuNDQyMzgsLTAuMTgzMTEgMC4yNDMxNiwtMC4yNDQxNCAwLjI0MzE2LC0wLjYzOTY1IDAsLTAuODgzNzkgeiBNIDE1LjYyNSwxOSBjIDAsLTEuODYwODQgMS41MTQxNiwtMy4zNzUgMy4zNzUsLTMuMzc1IDEuODYxMzMsMCAzLjM3NSwxLjUxNDE2IDMuMzc1LDMuMzc1IDAsMS44NjA4NCAtMS41MTM2NywzLjM3NSAtMy4zNzUsMy4zNzUgLTEuODYwODQsMCAtMy4zNzUsLTEuNTE0MTYgLTMuMzc1LC0zLjM3NSB6IgogICAgICAgZmlsbD0iI2VlMDAwMCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNyIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI3LDEzLjYyNSBjIDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMS40NDcyNyAtMS4xNzc3MywtMi42MjUgLTIuNjI1LC0yLjYyNSAtMS40NDcyNywwIC0yLjYyNSwxLjE3NzczIC0yLjYyNSwyLjYyNSAwLDAuNDk2NyAwLjE0NjYxLDAuOTU2NTQgMC4zODcyNywxLjM1MzAzIGwgLTEuMjA0NjUsMS4yMDUwOCBjIC0wLjI0NDE0LDAuMjQ0MTQgLTAuMjQzMTYsMC42Mzk2NSA5LjhlLTQsMC44ODM3OSAwLjEyMTA5LDAuMTIyMDcgMC4yODEyNSwwLjE4MzExIDAuNDQxNDEsMC4xODMxMSAwLjE2MDE2LDAgMC4zMjAzMSwtMC4wNjEwNCAwLjQ0MjM4LC0wLjE4MzExIGwgMS4yMDQxLC0xLjIwNDQ3IGMgMC4zOTY2MSwwLjI0MDkxIDAuODU2NjMsMC4zODc1NyAxLjM1MzUyLDAuMzg3NTcgeiBtIDAsLTQgYyAwLjc1NzgxLDAgMS4zNzUsMC42MTY3IDEuMzc1LDEuMzc1IDAsMC43NTgzIC0wLjYxNzE5LDEuMzc1IC0xLjM3NSwxLjM3NSAtMC4zNzgxMSwwIC0wLjcyMTA3LC0wLjE1MzY5IC0wLjk2OTk3LC0wLjQwMTczIC03LjNlLTQsLTcuM2UtNCAtOS44ZS00LC0wLjAwMTggLTAuMDAxNywtMC4wMDI2IC02LjFlLTQsLTYuMWUtNCAtMC4wMDE1LC03LjllLTQgLTAuMDAyMSwtMC4wMDE0IC0wLjI0NzYyLC0wLjI0ODc4IC0wLjQwMTE4LC0wLjU5MTM3IC0wLjQwMTE4LC0wLjk2OTMgMCwtMC43NTgzIDAuNjE3MTksLTEuMzc1IDEuMzc1LC0xLjM3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5LDguMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDExIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTksMjUuMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDEzIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjcuNSwxNi44NzUgYyAtMS4xNzE4OCwwIC0yLjEyNSwwLjk1MzEyIC0yLjEyNSwyLjEyNSAwLDEuMTcxODggMC45NTMxMiwyLjEyNSAyLjEyNSwyLjEyNSAxLjE3MTg4LDAgMi4xMjUsLTAuOTUzMTIgMi4xMjUsLTIuMTI1IDAsLTEuMTcxODggLTAuOTUzMTIsLTIuMTI1IC0yLjEyNSwtMi4xMjUgeiBtIDAsMyBjIC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgMCwtMC40ODI0MiAwLjM5MjU4LC0wLjg3NSAwLjg3NSwtMC44NzUgMC40ODI0MiwwIDAuODc1LDAuMzkyNTggMC44NzUsMC44NzUgMCwwLjQ4MjQyIC0wLjM5MjU4LDAuODc1IC0wLjg3NSwwLjg3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoMTUiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMi42MjUsMTkgYyAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IC0xLjE3MTg4LDAgLTIuMTI1LDAuOTUzMTIgLTIuMTI1LDIuMTI1IDAsMS4xNzE4OCAwLjk1MzEyLDIuMTI1IDIuMTI1LDIuMTI1IDEuMTcxODgsMCAyLjEyNSwtMC45NTMxMiAyLjEyNSwtMi4xMjUgeiBtIC0zLDAgYyAwLC0wLjQ4MjQyIDAuMzkyNTgsLTAuODc1IDAuODc1LC0wLjg3NSAwLjQ4MjQyLDAgMC44NzUsMC4zOTI1OCAwLjg3NSwwLjg3NSAwLDAuNDgyNDIgLTAuMzkyNTgsMC44NzUgLTAuODc1LDAuODc1IC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDE3IiAvPgogICAgPHBhdGgKICAgICAgIGQ9Ik0gMTMuMjM3NDMsMTIuMzUzNjQgQyAxMy40NzgzNCwxMS45NTcwMyAxMy42MjUsMTEuNDk2ODkgMTMuNjI1LDExIDEzLjYyNSw5LjU1MjczIDEyLjQ0NzI3LDguMzc1IDExLDguMzc1IDkuNTUyNzMsOC4zNzUgOC4zNzUsOS41NTI3MyA4LjM3NSwxMSBjIDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDAuNDk2ODksMCAwLjk1NzAzLC0wLjE0NjY3IDEuMzUzNjQsLTAuMzg3NTcgbCAxLjIwNDQ3LDEuMjA0NDcgYyAwLjEyMjA3LDAuMTIyMDcgMC4yODE3NCwwLjE4MzExIDAuNDQxODksMC4xODMxMSAwLjE2MDE1LDAgMC4zMTk4MiwtMC4wNjEwNCAwLjQ0MTg5LC0wLjE4MzExIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IEwgMTMuMjM3NDIsMTIuMzUzNjQgWiBNIDkuNjI1LDExIGMgMCwtMC43NTgzIDAuNjE2NywtMS4zNzUgMS4zNzUsLTEuMzc1IDAuNzU4MywwIDEuMzc1LDAuNjE2NyAxLjM3NSwxLjM3NSAwLDAuMzc3OTkgLTAuMTUzNSwwLjcyMDU4IC0wLjQwMTEyLDAuOTY5MzYgLTcuOWUtNCw3LjllLTQgLTAuMDAxOSwxMGUtNCAtMC4wMDI3LDAuMDAxOCAtOGUtNCw3LjllLTQgLTAuMDAxLDAuMDAxOSAtMC4wMDE4LDAuMDAyNyBDIDExLjcyMDU4LDEyLjIyMTUgMTEuMzc3OTksMTIuMzc1IDExLDEyLjM3NSAxMC4yNDE3LDEyLjM3NSA5LjYyNSwxMS43NTgzIDkuNjI1LDExIFoiCiAgICAgICBmaWxsPSIjZmZmZmZmIgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InBhdGgxOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDEzLjU1ODExLDIzLjU1ODExIC0xLjIwNDQ3LDEuMjA0NDcgQyAxMS45NTcwMywyNC41MjE2NyAxMS40OTY4OSwyNC4zNzUwMSAxMSwyNC4zNzUwMSBjIC0xLjQ0NzI3LDAgLTIuNjI1LDEuMTc3NzMgLTIuNjI1LDIuNjI1IDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMC40OTY4OSAtMC4xNDY2NywtMC45NTcwMyAtMC4zODc1NywtMS4zNTM2NCBMIDE0LjQ0MTksMjQuNDQxOSBjIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IC0wLjI0NDE0LC0wLjI0NDE0IC0wLjYzOTY1LC0wLjI0NDE0IC0wLjg4Mzc5LDAgeiBNIDExLDI4LjM3NSBjIC0wLjc1ODMsMCAtMS4zNzUsLTAuNjE2NyAtMS4zNzUsLTEuMzc1IDAsLTAuNzU4MyAwLjYxNjcsLTEuMzc1IDEuMzc1LC0xLjM3NSAwLjM3ODg1LDAgMC43MjIyOSwwLjE1Mzk5IDAuOTcxMTksMC40MDI1OSAyLjRlLTQsMi40ZS00IDIuNGUtNCw0LjllLTQgNC45ZS00LDcuM2UtNCAyLjVlLTQsMi40ZS00IDQuOWUtNCwyLjRlLTQgNy4zZS00LDQuOWUtNCAwLjI0ODYsMC4yNDg5IDAuNDAyNTksMC41OTIzNSAwLjQwMjU5LDAuOTcxMTkgMCwwLjc1ODMgLTAuNjE2NywxLjM3NSAtMS4zNzUsMS4zNzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDIxIiAvPgogIDwvZz4KPC9zdmc+Cg== keywords: - backstage - idp + # remove reference to janus-idp when sunsetted https://issues.redhat.com/browse/RHIDP-3227 - janus-idp -kubeVersion: ">= 1.25.0-0" + - developer-hub + - redhat-developer-hub + - redhat +kubeVersion: ">= 1.19.0-0" maintainers: - - name: Red Hat Developer Hub Team - url: https://github.com/redhat-developer/rhdh-chart + - name: Red Hat + url: https://redhat.com name: backstage type: application -sources: - - https://github.com/redhat-developer/rhdh-chart - - https://github.com/redhat-developer/rhdh +sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.0.1 +version: 4.1.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 84a74e8b..3c628c5e 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,45 +1,27 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) -![Version: 4.0.1](https://img.shields.io/badge/Version-4.0.1-informational?style=flat-square) +![Version: 4.1.0](https://img.shields.io/badge/Version-4.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.5/html-single/telemetry_data_collection/index -**Homepage:** +**Homepage:** ## Productized RHDH -For the **PRODUCTIZED** version of this chart, see: +This repository now provides the productized RHDH chart. +For the **GENERALLY AVAILABLE** version of this chart, see: -* https://github.com/rhdh-bot/openshift-helm-charts - CI builds for testing purposes only * https://github.com/openshift-helm-charts/charts - official releases to https://charts.openshift.io/ ## Maintainers | Name | Email | Url | | ---- | ------ | --- | -| Red Hat Developer Hub Team | | | - -## Source Code - -* -* - ---- - -RHDH Backstage chart is an opinionated flavor of the upstream chart located at [backstage/charts](https://github.com/backstage/charts). It extends the upstream chart with additional OpenShift specific functionality and provides opinionated values. - -[Backstage](https://backstage.io) is an open platform for building developer portals. Powered by a centralized software catalog, Backstage restores order to your microservices and infrastructure and enables your product teams to ship high-quality code quickly — without compromising autonomy. - -Backstage unifies all your infrastructure tooling, services, and documentation to create a streamlined development environment from end to end. - -**This chart offers an opinionated OpenShift-specific experience.** It is based on and directly depends on an upstream canonical [Backstage Helm chart](https://github.com/backstage/charts/tree/main/charts/backstage). For less opinionated experience, please consider using the upstream chart directly. - -This chart extends all the features in the upstream chart in addition to including OpenShift only features. It is not recommended to use this chart on other platforms. +| Red Hat | | | ## TL;DR @@ -69,6 +51,10 @@ Charts are available in the following formats: - [Chart Repository](https://helm.sh/docs/topics/chart_repository/) - [OCI Artifacts](https://helm.sh/docs/topics/registries/) +### Note + +Up-to-date instructions on installing RHDH through the chart can be found in the [installation docs](https://github.com/redhat-developer/rhdh-chart/tree/main/.rhdh/docs/installation-ci-charts.adoc). + ### Installing from the Chart Repository The following command can be used to add the chart repository: @@ -108,12 +94,12 @@ helm upgrade -i redhat-developer/backstage Note: this repo replaces https://github.com/janus-idp/helm-backstage, which has been deprecated in Feb 2024. -Charts are also available in OCI format. The list of available releases can be found [here](https://github.com/orgs/redhat-developer/packages/container/package/rhdh-chart%2Fbackstage). +Charts are also available in OCI format. The list of available releases can be found [here](https://quay.io/repository/rhdh/chart?tab=tags). Install one of the available versions: ```shell -helm upgrade -i oci://ghcr.io/redhat-developer/rhdh-chart/backstage --version= +helm upgrade -i oci://quay.io/rhdh/chart --version= ``` > **Tip**: List all releases using `helm list` @@ -170,7 +156,7 @@ The command removes all the Kubernetes components associated with the chart and ## Requirements -Kubernetes: `>= 1.25.0-0` +Kubernetes: `>= 1.19.0-0` | Repository | Name | Version | |------------|------|---------| @@ -191,6 +177,7 @@ Kubernetes: `>= 1.25.0-0` | global.dynamic.includes[0] | List of dynamic plugins included inside the `janus-idp/backstage-showcase` container image, some of which are disabled by default. This file ONLY works with the `janus-idp/backstage-showcase` container image. | string | `"dynamic-plugins.default.yaml"` | | global.dynamic.plugins | List of dynamic plugins, possibly overriding the plugins listed in `includes` files. Every item defines the plugin `package` as a [NPM package spec](https://docs.npmjs.com/cli/v10/using-npm/package-spec), an optional `pluginConfig` with plugin-specific backstage configuration, and an optional `disabled` flag to disable/enable a plugin listed in `includes` files. It also includes an `integrity` field that is used to verify the plugin package [integrity](https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description). | list | `[]` | | global.host | Custom hostname shorthand, overrides `global.clusterRouterBase`, `upstream.ingress.host`, `route.host`, and url values in `upstream.backstage.appConfig`. | string | `""` | +| nameOverride | | string | `"developer-hub"` | | orchestrator.enabled | | bool | `false` | | orchestrator.serverlessLogicOperator.enabled | | bool | `false` | | orchestrator.serverlessOperator.enabled | | bool | `false` | diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index 8cd6d7c3..cf60c589 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -2,7 +2,6 @@ {{ template "chart.deprecationWarning" . }} -[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/rhdh-chart&style=flat-square)](https://artifacthub.io/packages/search?repo=rhdh-chart) {{ template "chart.versionBadge" . }} {{ template "chart.typeBadge" . }} @@ -12,26 +11,15 @@ ## Productized RHDH -For the **PRODUCTIZED** version of this chart, see: +This repository now provides the productized RHDH chart. +For the **GENERALLY AVAILABLE** version of this chart, see: -* https://github.com/rhdh-bot/openshift-helm-charts - CI builds for testing purposes only * https://github.com/openshift-helm-charts/charts - official releases to https://charts.openshift.io/ {{ template "chart.maintainersSection" . }} {{ template "chart.sourcesSection" . }} ---- - -RHDH Backstage chart is an opinionated flavor of the upstream chart located at [backstage/charts](https://github.com/backstage/charts). It extends the upstream chart with additional OpenShift specific functionality and provides opinionated values. - -[Backstage](https://backstage.io) is an open platform for building developer portals. Powered by a centralized software catalog, Backstage restores order to your microservices and infrastructure and enables your product teams to ship high-quality code quickly — without compromising autonomy. - -Backstage unifies all your infrastructure tooling, services, and documentation to create a streamlined development environment from end to end. - -**This chart offers an opinionated OpenShift-specific experience.** It is based on and directly depends on an upstream canonical [Backstage Helm chart](https://github.com/backstage/charts/tree/main/charts/backstage). For less opinionated experience, please consider using the upstream chart directly. - -This chart extends all the features in the upstream chart in addition to including OpenShift only features. It is not recommended to use this chart on other platforms. ## TL;DR @@ -61,6 +49,10 @@ Charts are available in the following formats: - [Chart Repository](https://helm.sh/docs/topics/chart_repository/) - [OCI Artifacts](https://helm.sh/docs/topics/registries/) +### Note + +Up-to-date instructions on installing RHDH through the chart can be found in the [installation docs](https://github.com/redhat-developer/rhdh-chart/tree/main/.rhdh/docs/installation-ci-charts.adoc). + ### Installing from the Chart Repository The following command can be used to add the chart repository: @@ -100,12 +92,12 @@ helm upgrade -i redhat-developer/backstage Note: this repo replaces https://github.com/janus-idp/helm-backstage, which has been deprecated in Feb 2024. -Charts are also available in OCI format. The list of available releases can be found [here](https://github.com/orgs/redhat-developer/packages/container/package/rhdh-chart%2Fbackstage). +Charts are also available in OCI format. The list of available releases can be found [here](https://quay.io/repository/rhdh/chart?tab=tags). Install one of the available versions: ```shell -helm upgrade -i oci://ghcr.io/redhat-developer/rhdh-chart/backstage --version= +helm upgrade -i oci://quay.io/rhdh/chart --version= ``` > **Tip**: List all releases using `helm list` diff --git a/charts/backstage/artifacthub-repo.yml b/charts/backstage/artifacthub-repo.yml deleted file mode 100644 index 66d36f66..00000000 --- a/charts/backstage/artifacthub-repo.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Artifact Hub repository metadata file -# -# Some settings like the verified publisher flag or the ignored packages won't -# be applied until the next time the repository is processed. Please keep in -# mind that the repository won't be processed if it has not changed since the -# last time it was processed. Depending on the repository kind, this is checked -# in a different way. For Helm http based repositories, we consider it has -# changed if the `index.yaml` file changes. For git based repositories, it does -# when the hash of the last commit in the branch you set up changes. This does -# NOT apply to ownership claim operations, which are processed immediately. -# -repositoryID: fc3b125b-2d9c-41e2-a6f6-d4ee2680dd23 diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index e6188b59..508632e3 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -6887,7 +6887,7 @@ "type": "object" }, "nameOverride": { - "default": "backstage", + "default": "developer-hub", "title": "String to partially override common.names.fullname", "type": "string" }, diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index cd060d57..20128b10 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -1,3 +1,4 @@ +nameOverride: developer-hub global: dynamic: # -- Array of YAML files listing dynamic plugins to include with those listed in the `plugins` field. @@ -32,7 +33,7 @@ global: # -- Upstream Backstage [chart configuration](https://github.com/backstage/charts/blob/main/charts/backstage/values.yaml) # @default -- Use Openshift compatible settings upstream: - nameOverride: backstage + nameOverride: developer-hub backstage: image: registry: quay.io From 4ec9ea7224de042e2946187ecdc0caa45356274a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 20:16:27 +0200 Subject: [PATCH 022/117] chore(deps): update helm release common to v2.31.1 (#141) * chore(deps): update helm release common to v2.31.1 * Bump version to 4.2.0 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 1cfc71e0..56220b87 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: https://charts.bitnami.com/bitnami - version: 2.30.0 + version: 2.31.1 - name: backstage repository: https://backstage.github.io/charts version: 2.5.1 -digest: sha256:1919b6bd7d69f0c2f34cf29c5560427893eafeb92352e1da57b7b52885242b60 -generated: "2025-04-22T13:07:58.036321179Z" +digest: sha256:235ad581243f86bfbad1ebbf282b8d3785fd593080872769737b9602c9831feb +generated: "2025-05-07T16:57:41.747354463Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 379f8ba3..92fadbad 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -22,7 +22,7 @@ dependencies: repository: https://charts.bitnami.com/bitnami tags: - bitnami-common - version: "2.30.0" + version: "2.31.1" - name: backstage repository: https://backstage.github.io/charts version: "2.5.1" @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.1.0 +version: 4.2.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 3c628c5e..c64c603b 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.1.0](https://img.shields.io/badge/Version-4.1.0-informational?style=flat-square) +![Version: 4.2.0](https://img.shields.io/badge/Version-4.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -161,7 +161,7 @@ Kubernetes: `>= 1.19.0-0` | Repository | Name | Version | |------------|------|---------| | https://backstage.github.io/charts | upstream(backstage) | 2.5.1 | -| https://charts.bitnami.com/bitnami | common | 2.30.0 | +| https://charts.bitnami.com/bitnami | common | 2.31.1 | ## Values From db5dc7228dc5ca6f7ac9515430aeccbb3f906c8b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 22:40:41 +0200 Subject: [PATCH 023/117] chore(deps): update actions/setup-go digest to d35c59a (#145) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 770a761c..36749e8c 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -49,7 +49,7 @@ jobs: with: python-version: 3.13 - - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 if: steps.command.outputs.command-name == 'bump' with: go-version: ^1 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 561edaa1..e37104c8 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -26,7 +26,7 @@ jobs: with: python-version: 3.13 - - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 with: go-version: ^1 From ea106873f2ac863f6489079a7425003229432867 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 10:02:07 +0200 Subject: [PATCH 024/117] chore(deps): update mikefarah/yq action to v4.45.3 (#147) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 36749e8c..75cc65dc 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -81,7 +81,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@c59fa8de59f1f5a16fdbfd1e2a6e97a1b42a64b9 # v4.45.2 + uses: mikefarah/yq@734e2cd25402f10f51351c4034625043f07e7b06 # v4.45.3 with: cmd: yq ".version" charts/backstage/Chart.yaml @@ -94,7 +94,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@c59fa8de59f1f5a16fdbfd1e2a6e97a1b42a64b9 # v4.45.2 + uses: mikefarah/yq@734e2cd25402f10f51351c4034625043f07e7b06 # v4.45.3 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml From c91203d88e224af7677bdd4bf101e9460fe6ee8d Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 9 May 2025 13:22:40 +0200 Subject: [PATCH 025/117] chore(pre-commit): Automatically commit the changes detected by the pre-commit hooks (#148) --- .github/workflows/pre-commit.yaml | 56 ++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index e37104c8..c70c681d 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -1,7 +1,9 @@ name: Pre-commit on: - pull_request: + # pull_request_target needed to be able to commit and push pre-commit diffs to external fork PRs. + # But we included a manual authorization safeguard to prevent PWN requests. See the 'authorize' job below. + pull_request_target: branches: - main - rhdh-1.[0-9]+ @@ -13,14 +15,34 @@ concurrency: cancel-in-progress: true jobs: + authorize: + # The 'external' environment is configured with the maintainers team as required reviewers. + # All the subsequent jobs in this workflow 'need' this job, which will require manual approval for PRs coming from external forks. + # see list of approvers in OWNERS file + environment: + ${{ (github.event.pull_request.head.repo.full_name == github.repository || + contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.actor)) && 'internal' || 'external' }} + runs-on: ubuntu-latest + steps: + - name: approved + run: echo "✓" + pre-commit: name: Pre-commit runs-on: ubuntu-latest + needs: authorize + permissions: + contents: write + pull-requests: write env: GO111MODULE: on steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + repository: ${{github.event.pull_request.head.repo.full_name}} + ref: ${{ github.event.pull_request.head.ref }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: @@ -35,5 +57,37 @@ jobs: - name: Run pre-commit uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + continue-on-error: true # Don't fail immediately; we'll handle it below with: extra_args: --verbose --all-files --show-diff-on-failure + + - name: Check for changes after pre-commit + id: diff-checker + run: | + echo "CHANGED=$(if git diff --quiet; then echo "true"; else echo "false"; fi)" >> $GITHUB_OUTPUT + + - name: Commit any changes + if: ${{ steps.diff-checker.outputs.CHANGED == 'true' }} + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git fetch --prune + git pull --rebase --autostash + git add -A . + git commit \ + -m "chore(pre-commit): Auto-fix hooks" \ + -m "Co-authored-by: $GITHUB_ACTOR <$GITHUB_ACTOR@users.noreply.github.com>" + git push + + - name: Comment on PR if manifests were updated + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + if: ${{ !cancelled() && steps.diff-checker.outputs.CHANGED == 'true' }} + continue-on-error: true + with: + script: | + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⚠️ Files changed after running the pre-commit hooks

Those changes should have been pushed automatically to your PR branch.

NOTE: If the PR checks are stuck after this additional commit, manually close the PR and immediately reopen it to trigger the checks again.' + }) From d19fb1f46cf09c2317554c2aed57634ce2a21fa4 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 9 May 2025 13:30:07 +0200 Subject: [PATCH 026/117] ci: Fix pre-commit diff check command (#150) --- .github/workflows/pre-commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index c70c681d..e85b435b 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -64,7 +64,7 @@ jobs: - name: Check for changes after pre-commit id: diff-checker run: | - echo "CHANGED=$(if git diff --quiet; then echo "true"; else echo "false"; fi)" >> $GITHUB_OUTPUT + echo "CHANGED=$(if git diff --quiet; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT - name: Commit any changes if: ${{ steps.diff-checker.outputs.CHANGED == 'true' }} From c86275b57e08b4758445546a086e8165c292feb9 Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Fri, 9 May 2025 11:22:41 -0300 Subject: [PATCH 027/117] chore: set correct minimum k8s version based on OCP 4.14 (#146) * chore: set correct minimum k8s version based on OCP 4.14 Signed-off-by: Nick Boldt * bump chart version to 4.2.1 Signed-off-by: Nick Boldt * update readme template too Signed-off-by: Nick Boldt * Update charts/backstage/README.md Co-authored-by: Armel Soro * Update charts/backstage/README.md.gotmpl Co-authored-by: Armel Soro --------- Signed-off-by: Nick Boldt Co-authored-by: Armel Soro --- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 6 +++--- charts/backstage/README.md.gotmpl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 92fadbad..25ec59b7 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -37,7 +37,7 @@ keywords: - developer-hub - redhat-developer-hub - redhat -kubeVersion: ">= 1.19.0-0" +kubeVersion: ">= 1.27.0-0" maintainers: - name: Red Hat url: https://redhat.com @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.0 +version: 4.2.1 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index c64c603b..44bb545b 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.0](https://img.shields.io/badge/Version-4.2.0-informational?style=flat-square) +![Version: 4.2.1](https://img.shields.io/badge/Version-4.2.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -39,7 +39,7 @@ This chart bootstraps a [Backstage](https://backstage.io/docs/deployment/docker) ## Prerequisites -- Kubernetes 1.25+ (OpenShift 4.12+) +- Kubernetes 1.27+ ([OpenShift 4.14+](https://docs.redhat.com/en/documentation/openshift_container_platform/4.14/html-single/release_notes/index#ocp-4-14-about-this-release)) - Helm 3.10+ or [latest release](https://github.com/helm/helm/releases) - PV provisioner support in the underlying infrastructure - [Backstage container image](https://backstage.io/docs/deployment/docker) @@ -156,7 +156,7 @@ The command removes all the Kubernetes components associated with the chart and ## Requirements -Kubernetes: `>= 1.19.0-0` +Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index cf60c589..81163c1c 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -37,7 +37,7 @@ This chart bootstraps a [Backstage](https://backstage.io/docs/deployment/docker) ## Prerequisites -- Kubernetes 1.25+ (OpenShift 4.12+) +- Kubernetes 1.27+ ([OpenShift 4.14+](https://docs.redhat.com/en/documentation/openshift_container_platform/4.14/html-single/release_notes/index#ocp-4-14-about-this-release)) - Helm 3.10+ or [latest release](https://github.com/helm/helm/releases) - PV provisioner support in the underlying infrastructure - [Backstage container image](https://backstage.io/docs/deployment/docker) From e5ddcbc50de9bff8c4fa899a985928c036b5a0b9 Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Fri, 9 May 2025 12:49:45 -0300 Subject: [PATCH 028/117] chore: for now, comment out the non-working chart repo install stuff until we have this implemented again in RHIDP-6668 (#151) Signed-off-by: RHDH Build (rhdh-bot) Co-authored-by: RHDH Build (rhdh-bot) --- .rhdh/scripts/install.sh | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index 34634cf8..7dfbd84b 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -1,63 +1,67 @@ +#!/bin/bash # install a helm chart with the correct global.clusterRouterBase # default namespace if none set namespace="rhdh-helm" -chartrepo=0 # by default don't create a new chart repo unless the version chart version includes "CI" suffix -github=0 # by default don't use the Github repo unless the chart doesn't exist in the OCI registry +# chartrepo=0 # by default don't create a new chart repo unless the version chart version includes "CI" suffix usage () { echo "Usage: $0 CHART_VERSION [-n namespace] Examples: - $0 1.1.1 + $0 1.5.1 $0 1.7-20-CI -n rhdh-ci Options: -n, --namespace Project or namespace into which to install specified chart; default: $namespace --github-repo If set will use the deprecated github repository to install the helm chart instead of the OCI registry. - --chartrepo If set, a Helm Chart Repo will be applied to the cluster, based on the chart version. - If CHART_VERSION ends in CI and --github-repo is set, this is done by default. --router If set, the cluster router base is manually set. Required for non-admin users Redundant for admin users " - exit + # --chartrepo If set, a Helm Chart Repo will be applied to the cluster, based on the chart version. + # If CHART_VERSION ends in CI, this is done by default. } -if [[ $# -lt 1 ]]; then usage; fi +if [[ $# -lt 1 ]]; then usage; exit 0; fi while [[ "$#" -gt 0 ]]; do case $1 in - '--chartrepo') chartrepo=1;; + # '--chartrepo') chartrepo=1;; '-n'|'--namespace') namespace="$2"; shift 1;; - '-h') usage;; - '--github-repo') github=1;; + '-h') usage; exit 0;; '--router') CLUSTER_ROUTER_BASE="$2"; shift 1;; *) CV="$1";; esac shift 1 done -if [[ ! "$CV" ]]; then usage; fi +if [[ ! "$CV" ]]; then usage; exit 1; fi CHART_URL="oci://quay.io/rhdh/chart" -if ! helm show chart $CHART_URL --version "$CV" &> /dev/null; then github=1; fi -if [[ $github -eq 1 ]]; then - # If a Github CI chart, create a chart repo - if [[ "$CV" == *"-CI" ]]; then chartrepo=1; fi - CHART_URL="https://github.com/rhdh-bot/openshift-helm-charts/raw/redhat-developer-hub-${CV}/charts/redhat/redhat/redhat-developer-hub/${CV}/redhat-developer-hub-${CV}.tgz" -fi +if ! helm show chart $CHART_URL --version "$CV" &> /dev/null; then + echo "Error: could not load chart $CV from $CHART_URL !" + echo + usage + exit 1 + fi echo "Using ${CHART_URL} to install Helm chart" # choose namespace for the install (or create if non-existant) oc new-project "$namespace" || oc project "$namespace" -if [[ $chartrepo -eq 1 ]]; then - oc apply -f https://github.com/rhdh-bot/openshift-helm-charts/raw/redhat-developer-hub-"${CV}"/installation/rhdh-next-ci-repo.yaml -fi +# TODO: RHIDP-6668 generate rhdh-next-ci-repo.yaml while installing so we don't have to publish a new file every time +# TODO: RHIDP-6668 publish an index.yaml with every tarball pushed to quay.io/rhdh/chart; save them in rhdh-chart repo (one per CI versioned branch) +# if [[ "$CV" == *"-CI" ]] || [[ $chartrepo -eq 1 ]]; then +# see samples at +# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/index.yaml#L19 +# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/index.yaml#L49-L50 +# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/rhdh-next-ci-repo.yaml#L8 +# oc apply -f https://github.com/redhat-developer/rhdh-chart/raw/redhat-developer-hub-"${CV}"/installation/rhdh-next-ci-repo.yaml +# fi # 1. install (or upgrade) helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" From 8a95ef646e4a874359ff288d4565d6a9b2530cc0 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Mon, 12 May 2025 10:58:02 +0300 Subject: [PATCH 029/117] fix: documentation bug for backstage chart readme, in orchestrator (#144) * fix: documentation bug for backstage chart readme, in orchestrator * readme change - notification plugin * Added changes after review * Change to doc - notification plugin steps * using correct name for helm repo / install * Apply suggestions from code review * chore(pre-commit): Auto-fix hooks Co-authored-by: rm3l --------- Co-authored-by: Armel Soro Co-authored-by: github-actions[bot] Co-authored-by: rm3l --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 48 +++++++++++++++++++++---------- charts/backstage/README.md.gotmpl | 46 ++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 30 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 25ec59b7..a79f8982 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.1 +version: 4.2.2 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 44bb545b..e7bcfb4f 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.1](https://img.shields.io/badge/Version-4.2.1-informational?style=flat-square) +![Version: 4.2.2](https://img.shields.io/badge/Version-4.2.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -322,23 +322,37 @@ upstream: enabled: true ``` -## Installing RHDH with Orchestrator +## Installing RHDH with Orchestrator on OpenShift -Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, on boarding developers ,and user-made workflows of Backstage actions or external systems. -Orchestrator is a flavor of RHDH, and can be installed alongside the RHDH in the same namespace and in the folloing way: +Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, onboarding developers, and user-made workflows of Backstage actions or external systems. +Orchestrator is a flavor of RHDH, and can be installed alongside RHDH in the same namespace and in the following way: -1. Have an admin install the orchestrator-infra helm chart, which will install the pre-requisites required to install RHDH flavored Orchestrator. This proccess will include installing cluster-wide resources, so should be done with admin privileges +1. Have an admin install the [orchestrator-infra Helm Chart](https://github.com/redhat-developer/rhdh-chart/tree/main/charts/orchestrator-infra#readme), which will install the prerequisites required to deploy the Orchestrator-flavored RHDH. This process will include installing cluster-wide resources, so should be done with admin privileges: ``` -helm install charts/orchestrator-infra +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo add backstage https://backstage.github.io/charts +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install redhat-developer/redhat-developer-hub-orchestrator-infra +``` +2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. To do so, follow the post-install notes given by the chart, or see them [here](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/orchestrator-infra/templates/NOTES.txt) +3. Install the `backstage` chart with Helm, enabling orchestrator, serverlessLogicOperator, and serverlessOperator, like so: + +``` +helm install redhat-developer/backstage \ + --set orchestrator.enabled=true \ + --set orchestrator.serverlessLogicOperator.enabled=true \ + --set orchestrator.serverlessOperator.enabled=true ``` -2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. -3. Install backstage chart with helm, setting orchestrator to be enabled. -4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. ### Enablement of Notifications Plugin -To enable the notifications and signals plugin, please edit the dynamic plugin configmap after the installation and add the following: -``` +Workflows running with Orchestrator may use the Notifications plugin. +For this, you must enable the Notifications and Signals plugins. +To do so, you would need to edit the [default Helm values.yaml](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/backstage/values.yaml) file, and add the plugins listed below to the global.dynamic.plugins list. +Do this before installing the Helm Chart, or upgrade the Helm release with the new values file. + +```yaml - disabled: false package: "./dynamic-plugins/dist/backstage-plugin-notifications" - disabled: false @@ -358,10 +372,14 @@ and populate the following values in the values.yaml: externalDBsecretRef: externalDBName: "" ``` -Please note that externalDBName is the name of the user-configured existing database, not the database that orchestrator and sonataflow resources will use. +Please note that `externalDBName` is the name of the user-configured existing database, not the database that the orchestrator and sonataflow resources will use. -Finally, install the helm chart: +Finally, install the Helm Chart (including [setting up the external DB](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md)): ``` -helm install charts/backstage --set orchestrator.enabled=true --set orchestrator.serverlessLogicOperator.enabled=true --set orchestrator.serverlessOperator.enabled=true \ ---set externalDBsecretRef= --set externalDBName=example +helm install redhat-developer/backstage \ + --set orchestrator.enabled=true \ + --set orchestrator.serverlessLogicOperator.enabled=true \ + --set orchestrator.serverlessOperator.enabled=true \ + --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ + --set orchestrator.sonataflowPlatform.externalDBName=example ``` \ No newline at end of file diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index 81163c1c..e937367e 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -262,23 +262,37 @@ upstream: enabled: true ``` -## Installing RHDH with Orchestrator +## Installing RHDH with Orchestrator on OpenShift -Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, on boarding developers ,and user-made workflows of Backstage actions or external systems. -Orchestrator is a flavor of RHDH, and can be installed alongside the RHDH in the same namespace and in the folloing way: +Orchestrator brings serverless workflows into Backstage, focusing on the journey for application migration to the cloud, onboarding developers, and user-made workflows of Backstage actions or external systems. +Orchestrator is a flavor of RHDH, and can be installed alongside RHDH in the same namespace and in the following way: -1. Have an admin install the orchestrator-infra helm chart, which will install the pre-requisites required to install RHDH flavored Orchestrator. This proccess will include installing cluster-wide resources, so should be done with admin privileges +1. Have an admin install the [orchestrator-infra Helm Chart](https://github.com/redhat-developer/rhdh-chart/tree/main/charts/orchestrator-infra#readme), which will install the prerequisites required to deploy the Orchestrator-flavored RHDH. This process will include installing cluster-wide resources, so should be done with admin privileges: ``` -helm install charts/orchestrator-infra +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo add backstage https://backstage.github.io/charts +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install redhat-developer/redhat-developer-hub-orchestrator-infra +``` +2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. To do so, follow the post-install notes given by the chart, or see them [here](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/orchestrator-infra/templates/NOTES.txt) +3. Install the `backstage` chart with Helm, enabling orchestrator, serverlessLogicOperator, and serverlessOperator, like so: + +``` +helm install redhat-developer/backstage \ + --set orchestrator.enabled=true \ + --set orchestrator.serverlessLogicOperator.enabled=true \ + --set orchestrator.serverlessOperator.enabled=true ``` -2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. -3. Install backstage chart with helm, setting orchestrator to be enabled. -4. Enable serverlessLogicOperator and serverlessOperator in the backstage values. ### Enablement of Notifications Plugin -To enable the notifications and signals plugin, please edit the dynamic plugin configmap after the installation and add the following: -``` +Workflows running with Orchestrator may use the Notifications plugin. +For this, you must enable the Notifications and Signals plugins. +To do so, you would need to edit the [default Helm values.yaml](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/backstage/values.yaml) file, and add the plugins listed below to the global.dynamic.plugins list. +Do this before installing the Helm Chart, or upgrade the Helm release with the new values file. + +```yaml - disabled: false package: "./dynamic-plugins/dist/backstage-plugin-notifications" - disabled: false @@ -298,10 +312,14 @@ and populate the following values in the values.yaml: externalDBsecretRef: externalDBName: "" ``` -Please note that externalDBName is the name of the user-configured existing database, not the database that orchestrator and sonataflow resources will use. +Please note that `externalDBName` is the name of the user-configured existing database, not the database that the orchestrator and sonataflow resources will use. -Finally, install the helm chart: +Finally, install the Helm Chart (including [setting up the external DB](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md)): ``` -helm install charts/backstage --set orchestrator.enabled=true --set orchestrator.serverlessLogicOperator.enabled=true --set orchestrator.serverlessOperator.enabled=true \ ---set externalDBsecretRef= --set externalDBName=example +helm install redhat-developer/backstage \ + --set orchestrator.enabled=true \ + --set orchestrator.serverlessLogicOperator.enabled=true \ + --set orchestrator.serverlessOperator.enabled=true \ + --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ + --set orchestrator.sonataflowPlatform.externalDBName=example ``` \ No newline at end of file From 42aaa5cc78232707f4bf0fd0518270840aa90e44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 10:04:01 +0200 Subject: [PATCH 030/117] chore(deps): update mikefarah/yq action to v4.45.4 (#154) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 75cc65dc..8c9331c8 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -81,7 +81,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@734e2cd25402f10f51351c4034625043f07e7b06 # v4.45.3 + uses: mikefarah/yq@b534aa9ee5d38001fba3cd8fe254a037e4847b37 # v4.45.4 with: cmd: yq ".version" charts/backstage/Chart.yaml @@ -94,7 +94,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@734e2cd25402f10f51351c4034625043f07e7b06 # v4.45.3 + uses: mikefarah/yq@b534aa9ee5d38001fba3cd8fe254a037e4847b37 # v4.45.4 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml From caa7c917a7284c4d5b600b24de8028c91330af59 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 08:50:45 +0000 Subject: [PATCH 031/117] chore(deps): update helm release backstage to v2.5.2 (#153) * chore(deps): update helm release backstage to v2.5.2 * chore(pre-commit): Auto-fix hooks Co-authored-by: renovate[bot] * Bump version to 4.2.3 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] Co-authored-by: Armel Soro Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 56220b87..bf046c6b 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.31.1 - name: backstage repository: https://backstage.github.io/charts - version: 2.5.1 -digest: sha256:235ad581243f86bfbad1ebbf282b8d3785fd593080872769737b9602c9831feb -generated: "2025-05-07T16:57:41.747354463Z" + version: 2.5.2 +digest: sha256:f643c3b2baeb442148e12fcd2a462c418cbe7616be71a0a171e547455c971316 +generated: "2025-05-10T14:55:30.5715581Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index a79f8982..5fc2190f 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -25,7 +25,7 @@ dependencies: version: "2.31.1" - name: backstage repository: https://backstage.github.io/charts - version: "2.5.1" + version: "2.5.2" alias: upstream home: https://red.ht/rhdh icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjE5MS44OCIKICAgaGVpZ2h0PSIxOTEuODgiCiAgIHZpZXdCb3g9IjAgMCAxOTEuODggMTkxLjg4IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgiIC8+CiAgPGcKICAgICBpZD0idXVpZC03OTAxZjg3OC1jZTAwLTQ0MWYtYWMyNi1kZGQzNjU0ZDRmNzkiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxyZWN0CiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIHdpZHRoPSIzNiIKICAgICAgIGhlaWdodD0iMzYiCiAgICAgICByeD0iOSIKICAgICAgIHJ5PSI5IgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InJlY3QyIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjgsMi4yNSBjIDQuMjczMzYsMCA3Ljc1LDMuNDc2NjQgNy43NSw3Ljc1IHYgMTggYyAwLDQuMjczMzYgLTMuNDc2NjQsNy43NSAtNy43NSw3Ljc1IEggMTAgQyA1LjcyNjY0LDM1Ljc1IDIuMjUsMzIuMjczMzYgMi4yNSwyOCBWIDEwIEMgMi4yNSw1LjcyNjY0IDUuNzI2NjQsMi4yNSAxMCwyLjI1IEggMjggTSAyOCwxIEggMTAgQyA1LjAyOTQ0LDEgMSw1LjAyOTQzIDEsMTAgdiAxOCBjIDAsNC45NzA1NyA0LjAyOTQ0LDkgOSw5IGggMTggYyA0Ljk3MDU2LDAgOSwtNC4wMjk0MyA5LC05IFYgMTAgQyAzNyw1LjAyOTQzIDMyLjk3MDU2LDEgMjgsMSBaIgogICAgICAgZmlsbD0iIzRkNGQ0ZCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNCIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0idXVpZC1jM2NhNjg5MS02ZTE4LTQyY2ItODUyYi0zZGVkZDZjMzFlNjgiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI2LjQ0MjM4LDI1LjU1ODExIC0zLjc3Mzc0LC0zLjc3Mzc0IGMgMC41OTE0MywtMC43NzcwNCAwLjk1NjM2LC0xLjczNDggMC45NTYzNiwtMi43ODQzNiAwLC0yLjU1MDI5IC0yLjA3NTIsLTQuNjI1IC00LjYyNSwtNC42MjUgLTIuNTUwMjksMCAtNC42MjUsMi4wNzQ3MSAtNC42MjUsNC42MjUgMCwyLjU1MDI5IDIuMDc0NzEsNC42MjUgNC42MjUsNC42MjUgMS4wNDk0NCwwIDIuMDA3MjYsLTAuMzY0OTMgMi43ODQzNiwtMC45NTYzNiBsIDMuNzczMjUsMy43NzMyNSBjIDAuMTIyMDcsMC4xMjIwNyAwLjI4MjIzLDAuMTgzMTEgMC40NDIzOCwwLjE4MzExIDAuMTYwMTUsMCAwLjMyMDMxLC0wLjA2MTA0IDAuNDQyMzgsLTAuMTgzMTEgMC4yNDMxNiwtMC4yNDQxNCAwLjI0MzE2LC0wLjYzOTY1IDAsLTAuODgzNzkgeiBNIDE1LjYyNSwxOSBjIDAsLTEuODYwODQgMS41MTQxNiwtMy4zNzUgMy4zNzUsLTMuMzc1IDEuODYxMzMsMCAzLjM3NSwxLjUxNDE2IDMuMzc1LDMuMzc1IDAsMS44NjA4NCAtMS41MTM2NywzLjM3NSAtMy4zNzUsMy4zNzUgLTEuODYwODQsMCAtMy4zNzUsLTEuNTE0MTYgLTMuMzc1LC0zLjM3NSB6IgogICAgICAgZmlsbD0iI2VlMDAwMCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNyIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI3LDEzLjYyNSBjIDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMS40NDcyNyAtMS4xNzc3MywtMi42MjUgLTIuNjI1LC0yLjYyNSAtMS40NDcyNywwIC0yLjYyNSwxLjE3NzczIC0yLjYyNSwyLjYyNSAwLDAuNDk2NyAwLjE0NjYxLDAuOTU2NTQgMC4zODcyNywxLjM1MzAzIGwgLTEuMjA0NjUsMS4yMDUwOCBjIC0wLjI0NDE0LDAuMjQ0MTQgLTAuMjQzMTYsMC42Mzk2NSA5LjhlLTQsMC44ODM3OSAwLjEyMTA5LDAuMTIyMDcgMC4yODEyNSwwLjE4MzExIDAuNDQxNDEsMC4xODMxMSAwLjE2MDE2LDAgMC4zMjAzMSwtMC4wNjEwNCAwLjQ0MjM4LC0wLjE4MzExIGwgMS4yMDQxLC0xLjIwNDQ3IGMgMC4zOTY2MSwwLjI0MDkxIDAuODU2NjMsMC4zODc1NyAxLjM1MzUyLDAuMzg3NTcgeiBtIDAsLTQgYyAwLjc1NzgxLDAgMS4zNzUsMC42MTY3IDEuMzc1LDEuMzc1IDAsMC43NTgzIC0wLjYxNzE5LDEuMzc1IC0xLjM3NSwxLjM3NSAtMC4zNzgxMSwwIC0wLjcyMTA3LC0wLjE1MzY5IC0wLjk2OTk3LC0wLjQwMTczIC03LjNlLTQsLTcuM2UtNCAtOS44ZS00LC0wLjAwMTggLTAuMDAxNywtMC4wMDI2IC02LjFlLTQsLTYuMWUtNCAtMC4wMDE1LC03LjllLTQgLTAuMDAyMSwtMC4wMDE0IC0wLjI0NzYyLC0wLjI0ODc4IC0wLjQwMTE4LC0wLjU5MTM3IC0wLjQwMTE4LC0wLjk2OTMgMCwtMC43NTgzIDAuNjE3MTksLTEuMzc1IDEuMzc1LC0xLjM3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5LDguMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDExIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTksMjUuMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDEzIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjcuNSwxNi44NzUgYyAtMS4xNzE4OCwwIC0yLjEyNSwwLjk1MzEyIC0yLjEyNSwyLjEyNSAwLDEuMTcxODggMC45NTMxMiwyLjEyNSAyLjEyNSwyLjEyNSAxLjE3MTg4LDAgMi4xMjUsLTAuOTUzMTIgMi4xMjUsLTIuMTI1IDAsLTEuMTcxODggLTAuOTUzMTIsLTIuMTI1IC0yLjEyNSwtMi4xMjUgeiBtIDAsMyBjIC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgMCwtMC40ODI0MiAwLjM5MjU4LC0wLjg3NSAwLjg3NSwtMC44NzUgMC40ODI0MiwwIDAuODc1LDAuMzkyNTggMC44NzUsMC44NzUgMCwwLjQ4MjQyIC0wLjM5MjU4LDAuODc1IC0wLjg3NSwwLjg3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoMTUiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMi42MjUsMTkgYyAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IC0xLjE3MTg4LDAgLTIuMTI1LDAuOTUzMTIgLTIuMTI1LDIuMTI1IDAsMS4xNzE4OCAwLjk1MzEyLDIuMTI1IDIuMTI1LDIuMTI1IDEuMTcxODgsMCAyLjEyNSwtMC45NTMxMiAyLjEyNSwtMi4xMjUgeiBtIC0zLDAgYyAwLC0wLjQ4MjQyIDAuMzkyNTgsLTAuODc1IDAuODc1LC0wLjg3NSAwLjQ4MjQyLDAgMC44NzUsMC4zOTI1OCAwLjg3NSwwLjg3NSAwLDAuNDgyNDIgLTAuMzkyNTgsMC44NzUgLTAuODc1LDAuODc1IC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDE3IiAvPgogICAgPHBhdGgKICAgICAgIGQ9Ik0gMTMuMjM3NDMsMTIuMzUzNjQgQyAxMy40NzgzNCwxMS45NTcwMyAxMy42MjUsMTEuNDk2ODkgMTMuNjI1LDExIDEzLjYyNSw5LjU1MjczIDEyLjQ0NzI3LDguMzc1IDExLDguMzc1IDkuNTUyNzMsOC4zNzUgOC4zNzUsOS41NTI3MyA4LjM3NSwxMSBjIDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDAuNDk2ODksMCAwLjk1NzAzLC0wLjE0NjY3IDEuMzUzNjQsLTAuMzg3NTcgbCAxLjIwNDQ3LDEuMjA0NDcgYyAwLjEyMjA3LDAuMTIyMDcgMC4yODE3NCwwLjE4MzExIDAuNDQxODksMC4xODMxMSAwLjE2MDE1LDAgMC4zMTk4MiwtMC4wNjEwNCAwLjQ0MTg5LC0wLjE4MzExIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IEwgMTMuMjM3NDIsMTIuMzUzNjQgWiBNIDkuNjI1LDExIGMgMCwtMC43NTgzIDAuNjE2NywtMS4zNzUgMS4zNzUsLTEuMzc1IDAuNzU4MywwIDEuMzc1LDAuNjE2NyAxLjM3NSwxLjM3NSAwLDAuMzc3OTkgLTAuMTUzNSwwLjcyMDU4IC0wLjQwMTEyLDAuOTY5MzYgLTcuOWUtNCw3LjllLTQgLTAuMDAxOSwxMGUtNCAtMC4wMDI3LDAuMDAxOCAtOGUtNCw3LjllLTQgLTAuMDAxLDAuMDAxOSAtMC4wMDE4LDAuMDAyNyBDIDExLjcyMDU4LDEyLjIyMTUgMTEuMzc3OTksMTIuMzc1IDExLDEyLjM3NSAxMC4yNDE3LDEyLjM3NSA5LjYyNSwxMS43NTgzIDkuNjI1LDExIFoiCiAgICAgICBmaWxsPSIjZmZmZmZmIgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InBhdGgxOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDEzLjU1ODExLDIzLjU1ODExIC0xLjIwNDQ3LDEuMjA0NDcgQyAxMS45NTcwMywyNC41MjE2NyAxMS40OTY4OSwyNC4zNzUwMSAxMSwyNC4zNzUwMSBjIC0xLjQ0NzI3LDAgLTIuNjI1LDEuMTc3NzMgLTIuNjI1LDIuNjI1IDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMC40OTY4OSAtMC4xNDY2NywtMC45NTcwMyAtMC4zODc1NywtMS4zNTM2NCBMIDE0LjQ0MTksMjQuNDQxOSBjIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IC0wLjI0NDE0LC0wLjI0NDE0IC0wLjYzOTY1LC0wLjI0NDE0IC0wLjg4Mzc5LDAgeiBNIDExLDI4LjM3NSBjIC0wLjc1ODMsMCAtMS4zNzUsLTAuNjE2NyAtMS4zNzUsLTEuMzc1IDAsLTAuNzU4MyAwLjYxNjcsLTEuMzc1IDEuMzc1LC0xLjM3NSAwLjM3ODg1LDAgMC43MjIyOSwwLjE1Mzk5IDAuOTcxMTksMC40MDI1OSAyLjRlLTQsMi40ZS00IDIuNGUtNCw0LjllLTQgNC45ZS00LDcuM2UtNCAyLjVlLTQsMi40ZS00IDQuOWUtNCwyLjRlLTQgNy4zZS00LDQuOWUtNCAwLjI0ODYsMC4yNDg5IDAuNDAyNTksMC41OTIzNSAwLjQwMjU5LDAuOTcxMTkgMCwwLjc1ODMgLTAuNjE2NywxLjM3NSAtMS4zNzUsMS4zNzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDIxIiAvPgogIDwvZz4KPC9zdmc+Cg== @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.2 +version: 4.2.3 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index e7bcfb4f..d34628d6 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.2](https://img.shields.io/badge/Version-4.2.2-informational?style=flat-square) +![Version: 4.2.3](https://img.shields.io/badge/Version-4.2.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -160,7 +160,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.5.1 | +| https://backstage.github.io/charts | upstream(backstage) | 2.5.2 | | https://charts.bitnami.com/bitnami | common | 2.31.1 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 508632e3..2c40fc16 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -797,7 +797,7 @@ "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", "properties": { "preferredDuringSchedulingIgnoredDuringExecution": { - "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and subtracting \"weight\" from the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", "items": { "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", "properties": { From 3a20cda56cdb1cd81ca14c628e896048637fe823 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Mon, 12 May 2025 16:27:51 +0300 Subject: [PATCH 032/117] enabling serverless+serverlessLogic by default (#155) --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 17 +++++++---------- charts/backstage/README.md.gotmpl | 11 ++++------- charts/backstage/values.schema.json | 4 ++-- charts/backstage/values.yaml | 4 ++-- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 5fc2190f..5f8bbadd 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.3 +version: 4.2.4 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index d34628d6..2832dfb2 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.3](https://img.shields.io/badge/Version-4.2.3-informational?style=flat-square) +![Version: 4.2.4](https://img.shields.io/badge/Version-4.2.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. @@ -179,8 +179,8 @@ Kubernetes: `>= 1.27.0-0` | global.host | Custom hostname shorthand, overrides `global.clusterRouterBase`, `upstream.ingress.host`, `route.host`, and url values in `upstream.backstage.appConfig`. | string | `""` | | nameOverride | | string | `"developer-hub"` | | orchestrator.enabled | | bool | `false` | -| orchestrator.serverlessLogicOperator.enabled | | bool | `false` | -| orchestrator.serverlessOperator.enabled | | bool | `false` | +| orchestrator.serverlessLogicOperator.enabled | | bool | `true` | +| orchestrator.serverlessOperator.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"postgres:15"` | | orchestrator.sonataflowPlatform.eventing.broker.name | | string | `""` | | orchestrator.sonataflowPlatform.eventing.broker.namespace | | string | `""` | @@ -336,14 +336,13 @@ helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart helm install redhat-developer/redhat-developer-hub-orchestrator-infra ``` 2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. To do so, follow the post-install notes given by the chart, or see them [here](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/orchestrator-infra/templates/NOTES.txt) -3. Install the `backstage` chart with Helm, enabling orchestrator, serverlessLogicOperator, and serverlessOperator, like so: +3. Install the `backstage` chart with Helm, enabling orchestrator, like so: ``` -helm install redhat-developer/backstage \ - --set orchestrator.enabled=true \ - --set orchestrator.serverlessLogicOperator.enabled=true \ - --set orchestrator.serverlessOperator.enabled=true +helm install redhat-developer/backstage --set orchestrator.enabled=true ``` +Note that serverlessLogicOperator, and serverlessOperator are enabled by default. They can be disabled together or seperately by passing the following flags: +`--set orchestrator.serverlessLogicOperator.enabled=false --set orchestrator.serverlessOperator.enabled=false` ### Enablement of Notifications Plugin @@ -378,8 +377,6 @@ Finally, install the Helm Chart (including [setting up the external DB](https:// ``` helm install redhat-developer/backstage \ --set orchestrator.enabled=true \ - --set orchestrator.serverlessLogicOperator.enabled=true \ - --set orchestrator.serverlessOperator.enabled=true \ --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ --set orchestrator.sonataflowPlatform.externalDBName=example ``` \ No newline at end of file diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index e937367e..16f2a827 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -276,14 +276,13 @@ helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart helm install redhat-developer/redhat-developer-hub-orchestrator-infra ``` 2. Manually approve the Install Plans created by the chart, and wait for the Openshift Serverless and Openshift Serverless Logic Operators to be deployed. To do so, follow the post-install notes given by the chart, or see them [here](https://github.com/redhat-developer/rhdh-chart/blob/main/charts/orchestrator-infra/templates/NOTES.txt) -3. Install the `backstage` chart with Helm, enabling orchestrator, serverlessLogicOperator, and serverlessOperator, like so: +3. Install the `backstage` chart with Helm, enabling orchestrator, like so: ``` -helm install redhat-developer/backstage \ - --set orchestrator.enabled=true \ - --set orchestrator.serverlessLogicOperator.enabled=true \ - --set orchestrator.serverlessOperator.enabled=true +helm install redhat-developer/backstage --set orchestrator.enabled=true ``` +Note that serverlessLogicOperator, and serverlessOperator are enabled by default. They can be disabled together or seperately by passing the following flags: +`--set orchestrator.serverlessLogicOperator.enabled=false --set orchestrator.serverlessOperator.enabled=false` ### Enablement of Notifications Plugin @@ -318,8 +317,6 @@ Finally, install the Helm Chart (including [setting up the external DB](https:// ``` helm install redhat-developer/backstage \ --set orchestrator.enabled=true \ - --set orchestrator.serverlessLogicOperator.enabled=true \ - --set orchestrator.serverlessOperator.enabled=true \ --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ --set orchestrator.sonataflowPlatform.externalDBName=example ``` \ No newline at end of file diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 2c40fc16..d9148d43 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -103,7 +103,7 @@ "additionalProperties": false, "properties": { "enabled": { - "default": false, + "default": true, "title": "enabled flag", "type": "boolean" } @@ -115,7 +115,7 @@ "additionalProperties": false, "properties": { "enabled": { - "default": false, + "default": true, "title": "enabled flag", "type": "boolean" } diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 20128b10..38d6a3a7 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -348,9 +348,9 @@ test: orchestrator: enabled: false serverlessLogicOperator: - enabled: false + enabled: true serverlessOperator: - enabled: false + enabled: true sonataflowPlatform: monitoring: enabled: true From 0462be190059efb3cefa2dc75ea2f4c9d0936df4 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Mon, 12 May 2025 17:28:43 +0300 Subject: [PATCH 033/117] Fixing network policy issue in orchestrator (#156) * Fixing network policy issue in orchestrator * rebase and version bump --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/network-policies.yaml | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 5f8bbadd..0a884a25 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.4 +version: 4.2.5 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 2832dfb2..f7c3dc96 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.4](https://img.shields.io/badge/Version-4.2.4-informational?style=flat-square) +![Version: 4.2.5](https://img.shields.io/badge/Version-4.2.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub. diff --git a/charts/backstage/templates/network-policies.yaml b/charts/backstage/templates/network-policies.yaml index 1979e4f3..4c795d41 100644 --- a/charts/backstage/templates/network-policies.yaml +++ b/charts/backstage/templates/network-policies.yaml @@ -26,12 +26,14 @@ metadata: namespace: {{ .Release.Namespace | quote }} spec: podSelector: {} + policyTypes: + - Ingress ingress: - from: - namespaceSelector: matchLabels: # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress + policy-group.network.openshift.io/ingress: "" --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy From 5d78ccae1ae2f08f7f0256461cc6cbc8e6be9261 Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Mon, 12 May 2025 16:24:16 -0300 Subject: [PATCH 034/117] chore: generate repo and index files from the chart we're installing so we can also install a chart repo (for CI builds) (RHIDP-6668, RHIDP-7251) (#152) Signed-off-by: RHDH Build (rhdh-bot) Co-authored-by: RHDH Build (rhdh-bot) --- .rhdh/scripts/install.sh | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index 7dfbd84b..7e99d3e8 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -9,26 +9,28 @@ usage () { echo "Usage: $0 CHART_VERSION [-n namespace] +Requires an existing connection to an OCP or k8s cluster +Requires helm, plus oc or kubectl, to be installed and on the path + Examples: $0 1.5.1 - $0 1.7-20-CI -n rhdh-ci + $0 1.7-zzz-CI -n rhdh-ci Options: -n, --namespace Project or namespace into which to install specified chart; default: $namespace - --github-repo If set will use the deprecated github repository to install the helm chart instead of the OCI registry. + --chartrepo If set, a Helm Chart Repo will be applied to the cluster, based on the chart version. + If CHART_VERSION ends in CI, this is done by default. --router If set, the cluster router base is manually set. Required for non-admin users Redundant for admin users " - # --chartrepo If set, a Helm Chart Repo will be applied to the cluster, based on the chart version. - # If CHART_VERSION ends in CI, this is done by default. } if [[ $# -lt 1 ]]; then usage; exit 0; fi while [[ "$#" -gt 0 ]]; do case $1 in - # '--chartrepo') chartrepo=1;; + '--chartrepo') chartrepo=1;; '-n'|'--namespace') namespace="$2"; shift 1;; '-h') usage; exit 0;; '--router') CLUSTER_ROUTER_BASE="$2"; shift 1;; @@ -53,15 +55,28 @@ echo "Using ${CHART_URL} to install Helm chart" # choose namespace for the install (or create if non-existant) oc new-project "$namespace" || oc project "$namespace" -# TODO: RHIDP-6668 generate rhdh-next-ci-repo.yaml while installing so we don't have to publish a new file every time -# TODO: RHIDP-6668 publish an index.yaml with every tarball pushed to quay.io/rhdh/chart; save them in rhdh-chart repo (one per CI versioned branch) -# if [[ "$CV" == *"-CI" ]] || [[ $chartrepo -eq 1 ]]; then -# see samples at -# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/index.yaml#L19 -# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/index.yaml#L49-L50 -# https://github.com/rhdh-bot/openshift-helm-charts/blob/rhdh-1-rhel-9/installation/rhdh-next-ci-repo.yaml#L8 -# oc apply -f https://github.com/redhat-developer/rhdh-chart/raw/redhat-developer-hub-"${CV}"/installation/rhdh-next-ci-repo.yaml -# fi +# generate repo.yaml and index.yaml so we don't have to publish a new file every time +if [[ "$CV" == *"-CI" ]] || [[ $chartrepo -eq 1 ]]; then + mkdir -p /tmp/"$CV"-unpacked && pushd /tmp/"$CV"-unpacked >/dev/null 2>&1 || exit 1 + helm pull oci://quay.io/rhdh/chart --version "$CV" -d /tmp/"$CV"-unpacked # get tarball + helm repo index /tmp/"$CV"-unpacked # create index.yaml + cat < repo.yaml +apiVersion: helm.openshift.io/v1beta1 +kind: HelmChartRepository +metadata: + name: rhdh-next-ci-repo +spec: + connectionConfig: + file: >- + ./index.yaml +EOF + + oc apply -f repo.yaml || kubctl apply -f repo.yaml + popd >/dev/null 2>&1 || exit 1 + + # clean up temp files + rm -fr /tmp/"$CV"-unpacked +fi # 1. install (or upgrade) helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" @@ -86,4 +101,4 @@ helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" \ echo " Once deployed, Developer Hub $CV will be available at https://redhat-developer-hub-${namespace}.${CLUSTER_ROUTER_BASE} -" \ No newline at end of file +" From 35cf298ab0221112851f77ed793ca837658b8328 Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Thu, 15 May 2025 04:59:10 -0300 Subject: [PATCH 035/117] chore: install.sh - ux - link to topology view while deploying helm chart to watch progress (#160) Signed-off-by: RHDH Build (rhdh-bot) Co-authored-by: RHDH Build (rhdh-bot) --- .rhdh/scripts/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index 7e99d3e8..c6184ae8 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -98,6 +98,10 @@ helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" \ --set global.clusterRouterBase="${CLUSTER_ROUTER_BASE}" \ --set global.postgresql.auth.password="$PASSWORD" +echo " +While deploying you can watch at +https://console-openshift-console.${CLUSTER_ROUTER_BASE}/topology/ns/${namespace}?view=graph +" echo " Once deployed, Developer Hub $CV will be available at https://redhat-developer-hub-${namespace}.${CLUSTER_ROUTER_BASE} From 7f6367d49ff9724d08f405014cf5df109673fe6f Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 15 May 2025 20:48:06 +0200 Subject: [PATCH 036/117] chore(CI builds): Expose an in-cluster Helm repo off of the OCI chart pulled (#161) * chore(CI builds): Expose an in-cluster Helm repo off of the OCI chart pulled Ref: https://issues.redhat.com/browse/RHIDP-7251 Ref: https://issues.redhat.com/browse/RHIDP-6668 * Update .rhdh/scripts/install.sh * Update .rhdh/scripts/install.sh --- .rhdh/scripts/install.sh | 61 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index c6184ae8..d5f4e561 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -60,18 +60,73 @@ if [[ "$CV" == *"-CI" ]] || [[ $chartrepo -eq 1 ]]; then mkdir -p /tmp/"$CV"-unpacked && pushd /tmp/"$CV"-unpacked >/dev/null 2>&1 || exit 1 helm pull oci://quay.io/rhdh/chart --version "$CV" -d /tmp/"$CV"-unpacked # get tarball helm repo index /tmp/"$CV"-unpacked # create index.yaml + + # HelmChartRepository does not support OCI artifacts. + # Host an in-cluster Helm repo serving both the index and chart files. + if oc -n "$namespace" get configmap helm-repo-files > /dev/null; then + oc -n "$namespace" delete configmap helm-repo-files + fi + oc -n "$namespace" create configmap helm-repo-files \ + --from-file=/tmp/"$CV"-unpacked/index.yaml \ + --from-file=/tmp/"$CV"-unpacked/chart-${CV}.tgz + cat < helm-repo.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: helm-repo + annotations: + rhdh.redhat.com/chart-version: "$CV" +spec: + replicas: 1 + selector: + matchLabels: + app: helm-repo + template: + metadata: + labels: + app: helm-repo + annotations: + rhdh.redhat.com/chart-version: "$CV" + spec: + containers: + - name: nginx + image: quay.io/openshifttest/nginx-alpine:1.2.4 + ports: + - containerPort: 8080 + volumeMounts: + - name: chart-vol + mountPath: /data/http/charts + volumes: + - name: chart-vol + configMap: + name: helm-repo-files +--- +apiVersion: v1 +kind: Service +metadata: + name: helm-repo +spec: + selector: + app: helm-repo + ports: + - port: 80 + targetPort: 8080 +EOF + oc apply -f helm-repo.yaml || kubectl apply -f helm-repo.yaml + cat < repo.yaml apiVersion: helm.openshift.io/v1beta1 kind: HelmChartRepository metadata: name: rhdh-next-ci-repo + annotations: + rhdh.redhat.com/chart-version: "$CV" spec: connectionConfig: - file: >- - ./index.yaml + url: http://helm-repo.${namespace}.svc.cluster.local/charts EOF - oc apply -f repo.yaml || kubctl apply -f repo.yaml + oc apply -f repo.yaml || kubectl apply -f repo.yaml popd >/dev/null 2>&1 || exit 1 # clean up temp files From 031297d99a36136b50f01183e3a7332f4336f542 Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Fri, 16 May 2025 12:48:46 -0300 Subject: [PATCH 037/117] chore: update default chart settings using downstream patches so we can remove Chart_patch.yaml (RHIDP-7529) (#162) * chore: update default chart settings using downstream patches so we can remove Chart_patch.yaml (RHIDP-7529) Signed-off-by: Nick Boldt * bump version to 4.2.6 Signed-off-by: Nick Boldt * update Readme.md too Signed-off-by: Nick Boldt * Update charts/backstage/Chart.yaml * oh no trailing space removed Signed-off-by: Nick Boldt --------- Signed-off-by: Nick Boldt --- charts/backstage/Chart.yaml | 11 +++++++---- charts/backstage/README.md | 7 +++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 0a884a25..039ab57f 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -14,7 +14,10 @@ annotations: charts.openshift.io/supportURL: https://access.redhat.com/support apiVersion: v2 description: | - A Helm chart for deploying Red Hat Developer Hub. + Red Hat Developer Hub is a Red Hat supported version of Backstage. + It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can + help streamline the process of setting up a self-managed internal + developer portal for adopters who are just starting out. The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.5/html-single/telemetry_data_collection/index dependencies: @@ -32,8 +35,6 @@ icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgi keywords: - backstage - idp - # remove reference to janus-idp when sunsetted https://issues.redhat.com/browse/RHIDP-3227 - - janus-idp - developer-hub - redhat-developer-hub - redhat @@ -47,4 +48,6 @@ sources: [] # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 4.2.5 +# Note that when this chart is published to https://github.com/openshift-helm-charts/charts +# it will follow the RHDH versioning 1.y.z +version: 4.2.6 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index f7c3dc96..92e35d48 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,10 +1,13 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.5](https://img.shields.io/badge/Version-4.2.5-informational?style=flat-square) +![Version: 4.2.6](https://img.shields.io/badge/Version-4.2.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) -A Helm chart for deploying Red Hat Developer Hub. +Red Hat Developer Hub is a Red Hat supported version of Backstage. +It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can +help streamline the process of setting up a self-managed internal +developer portal for adopters who are just starting out. The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.5/html-single/telemetry_data_collection/index From b7e18cd92ac36703e23fe9e173ccb4ed339db199 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 20 May 2025 15:45:22 +0200 Subject: [PATCH 038/117] fix: Fix the images used by the Orchestrator DB creation Job (#164) * Use the existing PostgreSQL image in the Orchestrator DB creation job By reusing the existing images, we don't need to change our airgap docs, and this would also work with the images used in the downstream chart * Bump chart version --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 6 ++--- charts/backstage/templates/sonataflows.yaml | 29 +++++++++++---------- charts/backstage/values.yaml | 4 +-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 039ab57f..15246d7b 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -50,4 +50,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.6 +version: 4.2.7 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 92e35d48..df5850f3 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.6](https://img.shields.io/badge/Version-4.2.6-informational?style=flat-square) +![Version: 4.2.7](https://img.shields.io/badge/Version-4.2.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Red Hat Developer Hub is a Red Hat supported version of Backstage. @@ -184,12 +184,12 @@ Kubernetes: `>= 1.27.0-0` | orchestrator.enabled | | bool | `false` | | orchestrator.serverlessLogicOperator.enabled | | bool | `true` | | orchestrator.serverlessOperator.enabled | | bool | `true` | -| orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"postgres:15"` | +| orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | | orchestrator.sonataflowPlatform.eventing.broker.name | | string | `""` | | orchestrator.sonataflowPlatform.eventing.broker.namespace | | string | `""` | | orchestrator.sonataflowPlatform.externalDBName | Name for the user-configured external Database | string | `""` | | orchestrator.sonataflowPlatform.externalDBsecretRef | Secret name for the user-created secret to connect an external DB | string | `""` | -| orchestrator.sonataflowPlatform.initContainerImage | Image for the init container used by the create-db job | string | `"busybox"` | +| orchestrator.sonataflowPlatform.initContainerImage | Image for the init container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | | orchestrator.sonataflowPlatform.monitoring.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.resources.limits.cpu | | string | `"500m"` | | orchestrator.sonataflowPlatform.resources.limits.memory | | string | `"1Gi"` | diff --git a/charts/backstage/templates/sonataflows.yaml b/charts/backstage/templates/sonataflows.yaml index aec16d17..ad4a9809 100644 --- a/charts/backstage/templates/sonataflows.yaml +++ b/charts/backstage/templates/sonataflows.yaml @@ -92,18 +92,25 @@ spec: spec: initContainers: - name: wait-for-db - image: {{ .Values.orchestrator.sonataflowPlatform.initContainerImage }} + image: "{{- tpl .Values.orchestrator.sonataflowPlatform.initContainerImage . -}}" command: - - sh + - bash - -c -{{- if .Values.upstream.postgresql.enabled }} - - "until nc -z {{ .Release.Name }}-postgresql 5432; do echo 'Waiting for DB...'; sleep 2; done" -{{- else }} - - "until nc -z {{ .Values.upstream.backstage.appConfig.backend.database.connection.host }} {{ .Values.upstream.backstage.appConfig.backend.database.connection.port }}; do echo 'Waiting for external DB...'; sleep 2; done" -{{- end }} + - | + {{- if .Values.upstream.postgresql.enabled }} + dbHost="{{ .Release.Name }}-postgresql" + dbPort="5432" + {{- else }} + dbHost="{{ .Values.upstream.backstage.appConfig.backend.database.connection.host }}" + dbPort="{{ .Values.upstream.backstage.appConfig.backend.database.connection.port }}" + {{- end }} + until timeout --preserve-status --kill-after=3 2 bash -c ">/dev/tcp/${dbHost}/${dbPort}"; do + echo 'Waiting for DB...' + sleep 2 + done containers: - name: psql - image: {{ .Values.orchestrator.sonataflowPlatform.createDBJobImage }} + image: "{{- tpl .Values.orchestrator.sonataflowPlatform.createDBJobImage . -}}" env: - name: PGPASSWORD valueFrom: @@ -125,9 +132,3 @@ spec: restartPolicy: Never backoffLimit: 2 {{- end }} - - - - - - diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 38d6a3a7..c6fb9c7b 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -372,7 +372,7 @@ orchestrator: externalDBName: "" # -- Image for the init container used by the create-db job - initContainerImage: busybox + initContainerImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" # -- Image for the container used by the create-db job - createDBJobImage: "postgres:15" + createDBJobImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" From 1b660112e1ece3af2382fe8fde1881060a55fd89 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 22 May 2025 13:08:48 +0100 Subject: [PATCH 039/117] chore(ci): Add Snyk scan for Backstage with Orchestrator (#165) * Add Snyk scan for Backstage with Orchestrator and consistent project naming Signed-off-by: Fortune-Ndlovu * chart version bump Signed-off-by: Fortune-Ndlovu * Inline orchestrator values in Snyk scan job instead of using a separate values file Signed-off-by: Fortune-Ndlovu * Refactor Snyk workflow to use matrix-driven Helm args for scan variants Signed-off-by: Fortune-Ndlovu * note # 0.4.0 Signed-off-by: Fortune-Ndlovu * note renamed the matrix key to chartConfig Signed-off-by: Fortune-Ndlovu * fixup Signed-off-by: Fortune-Ndlovu * fixup: add pull request to verify the Helm rendering logic runs cleanly on PRs. Signed-off-by: Fortune-Ndlovu * update: when cliArgs is undefined that is not present in the matrix entry, it just evaluates to an empty string Signed-off-by: Fortune-Ndlovu * Add single-line string in cliargs Signed-off-by: Fortune-Ndlovu * Update jobs.scan-iac.name Signed-off-by: Fortune-Ndlovu * update Scan Rendered Templates Signed-off-by: Fortune-Ndlovu * remove pull_request trigger to test PR behavior Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu Signed-off-by: Fortune-Ndlovu Co-authored-by: Armel Soro --- .github/workflows/snyk.yaml | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index f20f76a0..d458ab2c 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -7,14 +7,19 @@ on: jobs: scan-iac: - name: Scan Rendered Templates + name: Scan Rendered Templates (${{ matrix.chartConfig.name }}) runs-on: ubuntu-latest strategy: matrix: - chart: - - backstage - - orchestrator-infra + chartConfig: + - name: "backstage" + path: "backstage" + - name: "orchestrator-infra" + path: "orchestrator-infra" + - name: "backstage-orchestrator" + path: "backstage" + cliArgs: "--set orchestrator.enabled=true" steps: - name: Checkout @@ -27,20 +32,23 @@ jobs: with: version: v3.17.0 - - name: Render Templates for ${{ matrix.chart }} + - name: Render Templates for ${{ matrix.chartConfig.name }} run: | helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo update - helm dependency build ./charts/${{ matrix.chart }} - helm template ./charts/${{ matrix.chart }} --output-dir ./output/${{ matrix.chart }} - - name: Run Snyk IaC Scan for ${{ matrix.chart }} + helm dependency build ./charts/${{ matrix.chartConfig.path }} + helm template ./charts/${{ matrix.chartConfig.path }} \ + ${{ matrix.chartConfig.cliArgs || '' }} \ + --output-dir ./output/${{ matrix.chartConfig.name }} + + - name: Run Snyk IaC Scan for ${{ matrix.chartConfig.name }} continue-on-error: true uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} with: - args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/${{ matrix.chart }}" - file: ./output/${{ matrix.chart }} + args: --report --org=$SNYK_ORG_ID --target-name="redhat-developer/rhdh-chart/${{ matrix.chartConfig.name }}" + file: ./output/${{ matrix.chartConfig.name }} From 61c7c18b864a138775f25e0e3a59c86bf111a3eb Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Wed, 11 Jun 2025 10:11:57 +0200 Subject: [PATCH 040/117] chore: Review and update owners file and authorize jobs with the current Install group members (#168) --- .github/workflows/pre-commit.yaml | 2 +- OWNERS | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index e85b435b..46aa2aeb 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -21,7 +21,7 @@ jobs: # see list of approvers in OWNERS file environment: ${{ (github.event.pull_request.head.repo.full_name == github.repository || - contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.actor)) && 'internal' || 'external' }} + contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","Fortune-Ndlovu","subhashkhileri","zdrapela","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.actor)) && 'internal' || 'external' }} runs-on: ubuntu-latest steps: - name: approved diff --git a/OWNERS b/OWNERS index 7aca213c..6316ac31 100644 --- a/OWNERS +++ b/OWNERS @@ -1,8 +1,16 @@ +# if adding/removing approvers, remember to update the .github/workflows/*.yaml files +# to add/remove them from the authorize job(s), if any approvers: - - coreydaley - - davidfestal + - rm3l - gazarenkov - - kadel - nickboldt + - kim-tsao + - coreydaley + - kadel + +reviewers: - rm3l - - tumido + - gazarenkov + - Fortune-Ndlovu + - subhashkhileri + - zdrapela From 205f79a707d05cdd870af06dd3a8d7092760ac6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 10:23:50 +0200 Subject: [PATCH 041/117] chore(deps): update stefanzweifel/git-auto-commit-action action to v6 (#167) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 8c9331c8..009c8f89 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -109,7 +109,7 @@ jobs: - name: Commit pre-commit changes if: steps.command.outputs.command-name == 'bump' - uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5 + uses: stefanzweifel/git-auto-commit-action@3cc016cfc892e0844046da36fc68da4e525e081f # v6 with: commit_message: Bump version to ${{ steps.semver.outputs.new_version }} commit_options: '-s' From 61a9e3972a53f95fa656349dc34f22c7d083d222 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:24:53 +0200 Subject: [PATCH 042/117] chore(deps): update stefanzweifel/git-auto-commit-action digest to 778341a (#169) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 009c8f89..7971f021 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -109,7 +109,7 @@ jobs: - name: Commit pre-commit changes if: steps.command.outputs.command-name == 'bump' - uses: stefanzweifel/git-auto-commit-action@3cc016cfc892e0844046da36fc68da4e525e081f # v6 + uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 with: commit_message: Bump version to ${{ steps.semver.outputs.new_version }} commit_options: '-s' From 921d171e06b1cd3a8839e58c3e327b950281aa93 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:24:30 +0200 Subject: [PATCH 043/117] chore(deps): update helm release common to v2.31.3 (#171) * chore(deps): update helm release common to v2.31.3 * chore(pre-commit): Auto-fix hooks Co-authored-by: renovate[bot] * Bump version to 4.2.8 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index bf046c6b..4a822f33 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: https://charts.bitnami.com/bitnami - version: 2.31.1 + version: 2.31.3 - name: backstage repository: https://backstage.github.io/charts version: 2.5.2 -digest: sha256:f643c3b2baeb442148e12fcd2a462c418cbe7616be71a0a171e547455c971316 -generated: "2025-05-10T14:55:30.5715581Z" +digest: sha256:31e6fedeea156b69c6d7f0e35473eb12f2570cf3fbe6520fc2427c38277604d1 +generated: "2025-06-12T19:30:29.701558818Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 15246d7b..ec45fbff 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -25,7 +25,7 @@ dependencies: repository: https://charts.bitnami.com/bitnami tags: - bitnami-common - version: "2.31.1" + version: "2.31.3" - name: backstage repository: https://backstage.github.io/charts version: "2.5.2" @@ -50,4 +50,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.7 +version: 4.2.8 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index df5850f3..c8406266 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.7](https://img.shields.io/badge/Version-4.2.7-informational?style=flat-square) +![Version: 4.2.8](https://img.shields.io/badge/Version-4.2.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Red Hat Developer Hub is a Red Hat supported version of Backstage. @@ -164,7 +164,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| | https://backstage.github.io/charts | upstream(backstage) | 2.5.2 | -| https://charts.bitnami.com/bitnami | common | 2.31.1 | +| https://charts.bitnami.com/bitnami | common | 2.31.3 | ## Values From 4df0ae9d368f3704ab458433f830eea8b9298e49 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 17 Jun 2025 09:57:59 +0200 Subject: [PATCH 044/117] chore: Update PR Template checklist with reminder to check the `orchestrator-infra` CRDs versions (#172) --- .github/pull_request_template.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d92f10bf..82ff3239 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,28 +1,30 @@ +--> ## Description of the change -## Existing or Associated Issue(s) +## Which issue(s) does this PR fix or relate to -## Additional Information +- _JIRA_issue_link_ - +## How to test changes / Special notes to the reviewer + + ## Checklist -- [ ] Chart version bumped in `Chart.yaml` according to [semver](http://semver.org/). -- [ ] Variables are documented in the `values.yaml` and added to the README.md. The [pre-commit](https://pre-commit.com/) utility can be used to generate the necessary content. Use `pre-commit run -a` to apply changes. -- [ ] JSON Schema template updated and re-generated the raw schema via `pre-commit` hook. -- [ ] List tests pass for Chart using the [Chart Testing](https://github.com/helm/chart-testing) tool and the `ct lint` command. +- [ ] For each Chart updated, version bumped in the corresponding `Chart.yaml` according to [Semantic Versioning](http://semver.org/). +- [ ] For each Chart updated, variables are documented in the `values.yaml` and added to the corresponding README.md. The [pre-commit](https://pre-commit.com/) utility can be used to generate the necessary content. Use `pre-commit run -a` to apply changes. The [pre-commit Workflow](./workflows/pre-commit.yaml) will do this automatically for you if needed. +- [ ] JSON Schema template updated and re-generated the raw schema via the `pre-commit` hook. +- [ ] Tests pass using the [Chart Testing](https://github.com/helm/chart-testing) tool and the `ct lint` command. +- [ ] If you updated the [orchestrator-infra](../charts/orchestrator-infra) chart, make sure the versions of the [Knative CRDs](../charts/orchestrator-infra/crds) are aligned with the versions of the CRDs installed by the OpenShift Serverless operators declared in the [values.yaml](../charts/orchestrator-infra/values.yaml) file. See [Installing Knative Eventing and Knative Serving CRDs](../charts/orchestrator-infra/README.md#installing-knative-eventing-and-knative-serving-crds) for more details. From 229e4f94c9071bb6d1743da368594d128ee46833 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Fri, 20 Jun 2025 14:08:41 +0100 Subject: [PATCH 045/117] chore(fix): Resolve Snyk IaC Findings for RHDH Helm Chart (with and without Orchestrator) (#166) * Resolve SNYK-CC-K8S-6, SNYK-CC-K8S-9, SNYK-CC-K8S-10 Signed-off-by: Fortune-Ndlovu * Resolve Low issues Signed-off-by: Fortune-Ndlovu * fix indent Signed-off-by: Fortune-Ndlovu * fix containerSecurityContext Signed-off-by: Fortune-Ndlovu * fix imagePullPolicy in test-connection Signed-off-by: Fortune-Ndlovu * add pull_request to trigger and test PR behavior Signed-off-by: Fortune-Ndlovu * remove pull_request to trigger and test PR behavior Signed-off-by: Fortune-Ndlovu * bump chart version Signed-off-by: Fortune-Ndlovu * Remove readOnlyRootFilesystem: true from PostgreSQL only Signed-off-by: Fortune-Ndlovu * disable container security context Signed-off-by: Fortune-Ndlovu * remove runAsUser and runAsGroup Signed-off-by: Fortune-Ndlovu * Chart version bumped Signed-off-by: Fortune-Ndlovu * Dont set runAsUser as ocp usually randomly assigns a high UID to each pod and does not allow hardcoding UIDs by default. Signed-off-by: Fortune-Ndlovu * Remove all runAsNonRoot: true settings for now to get your tests passing. Signed-off-by: Fortune-Ndlovu * fixup Signed-off-by: Fortune-Ndlovu * imagePullPolicy below image Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu Co-authored-by: Armel Soro --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/sonataflows.yaml | 28 +++++++++++++++++++ .../templates/tests/test-connection.yaml | 3 +- charts/backstage/values.yaml | 4 +++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index ec45fbff..afa4c66c 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -50,4 +50,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.8 +version: 4.2.9 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index c8406266..cb3468f3 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.8](https://img.shields.io/badge/Version-4.2.8-informational?style=flat-square) +![Version: 4.2.9](https://img.shields.io/badge/Version-4.2.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Red Hat Developer Hub is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/sonataflows.yaml b/charts/backstage/templates/sonataflows.yaml index ad4a9809..d005c447 100644 --- a/charts/backstage/templates/sonataflows.yaml +++ b/charts/backstage/templates/sonataflows.yaml @@ -92,7 +92,21 @@ spec: spec: initContainers: - name: wait-for-db + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + runAsNonRoot: true + capabilities: + drop: + - ALL image: "{{- tpl .Values.orchestrator.sonataflowPlatform.initContainerImage . -}}" + resources: + limits: + cpu: "100m" + memory: "64Mi" + requests: + cpu: "50m" + memory: "32Mi" command: - bash - -c @@ -111,6 +125,20 @@ spec: containers: - name: psql image: "{{- tpl .Values.orchestrator.sonataflowPlatform.createDBJobImage . -}}" + resources: + limits: + cpu: "100m" + memory: "128Mi" + requests: + cpu: "100m" + memory: "64Mi" + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + runAsNonRoot: true + capabilities: + drop: + - ALL env: - name: PGPASSWORD valueFrom: diff --git a/charts/backstage/templates/tests/test-connection.yaml b/charts/backstage/templates/tests/test-connection.yaml index b8aee686..85205b25 100644 --- a/charts/backstage/templates/tests/test-connection.yaml +++ b/charts/backstage/templates/tests/test-connection.yaml @@ -18,7 +18,6 @@ spec: readOnlyRootFilesystem: true capabilities: drop: ["ALL"] - runAsNonRoot: false resources: requests: cpu: 10m @@ -32,7 +31,7 @@ spec: - ls - /usr/bin/curl image: "{{ .Values.test.image.registry }}/{{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}" - imagePullPolicy: IfNotPresent + imagePullPolicy: Always command: ["/bin/sh", "-c"] args: - | diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index c6fb9c7b..474f1104 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -263,6 +263,10 @@ upstream: enabled: false containerSecurityContext: enabled: false + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL resources: requests: cpu: 250m From 9ed0749fd06195f324cc8182a6045d4b1b880de1 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:30:22 +0300 Subject: [PATCH 046/117] Backstage chart: fixing RHIDP/7806 in Orchestrator's SFP config (#173) * Backstage chart: fixing RHIDP/7806 in Orchestrator's SFP config * fixes after review * rebase and chart bump * adding port and password to psql command * readme changes * secret name change Co-authored-by: Armel Soro --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 13 +++- charts/backstage/README.md.gotmpl | 9 ++- charts/backstage/templates/sonataflows.yaml | 86 ++++++++++++++------- charts/backstage/values.schema.json | 10 +++ charts/backstage/values.schema.tmpl.json | 10 +++ charts/backstage/values.yaml | 6 ++ 7 files changed, 106 insertions(+), 30 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index afa4c66c..c0a10837 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -50,4 +50,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.9 +version: 4.2.10 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index cb3468f3..ff7c5620 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.9](https://img.shields.io/badge/Version-4.2.9-informational?style=flat-square) +![Version: 4.2.10](https://img.shields.io/badge/Version-4.2.10-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Red Hat Developer Hub is a Red Hat supported version of Backstage. @@ -187,7 +187,9 @@ Kubernetes: `>= 1.27.0-0` | orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | | orchestrator.sonataflowPlatform.eventing.broker.name | | string | `""` | | orchestrator.sonataflowPlatform.eventing.broker.namespace | | string | `""` | +| orchestrator.sonataflowPlatform.externalDBHost | Host for the user-configured external Database | string | `""` | | orchestrator.sonataflowPlatform.externalDBName | Name for the user-configured external Database | string | `""` | +| orchestrator.sonataflowPlatform.externalDBPort | Port for the user-configured external Database | string | `""` | | orchestrator.sonataflowPlatform.externalDBsecretRef | Secret name for the user-created secret to connect an external DB | string | `""` | | orchestrator.sonataflowPlatform.initContainerImage | Image for the init container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | | orchestrator.sonataflowPlatform.monitoring.enabled | | bool | `true` | @@ -373,13 +375,20 @@ and populate the following values in the values.yaml: ```bash externalDBsecretRef: externalDBName: "" + externalDBHost: "" + externalDBPort: "" ``` +The values for externalDBHost and externalDBPort should match the ones configured in the cred-secret. + Please note that `externalDBName` is the name of the user-configured existing database, not the database that the orchestrator and sonataflow resources will use. +A Job will run to create the 'sonataflow' database in the external database for the workflows to use. Finally, install the Helm Chart (including [setting up the external DB](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md)): ``` helm install redhat-developer/backstage \ --set orchestrator.enabled=true \ --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ - --set orchestrator.sonataflowPlatform.externalDBName=example + --set orchestrator.sonataflowPlatform.externalDBName=example \ + --set orchestrator.sonataflowPlatform.externalDBHost=example \ + --set orchestrator.sonataflowPlatform.externalDBPort=example ``` \ No newline at end of file diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index 16f2a827..14a3e574 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -310,13 +310,20 @@ and populate the following values in the values.yaml: ```bash externalDBsecretRef: externalDBName: "" + externalDBHost: "" + externalDBPort: "" ``` +The values for externalDBHost and externalDBPort should match the ones configured in the cred-secret. + Please note that `externalDBName` is the name of the user-configured existing database, not the database that the orchestrator and sonataflow resources will use. +A Job will run to create the 'sonataflow' database in the external database for the workflows to use. Finally, install the Helm Chart (including [setting up the external DB](https://github.com/redhat-developer/rhdh-chart/blob/main/docs/external-db.md)): ``` helm install redhat-developer/backstage \ --set orchestrator.enabled=true \ --set orchestrator.sonataflowPlatform.externalDBsecretRef= \ - --set orchestrator.sonataflowPlatform.externalDBName=example + --set orchestrator.sonataflowPlatform.externalDBName=example \ + --set orchestrator.sonataflowPlatform.externalDBHost=example \ + --set orchestrator.sonataflowPlatform.externalDBPort=example ``` \ No newline at end of file diff --git a/charts/backstage/templates/sonataflows.yaml b/charts/backstage/templates/sonataflows.yaml index d005c447..d845c7cd 100644 --- a/charts/backstage/templates/sonataflows.yaml +++ b/charts/backstage/templates/sonataflows.yaml @@ -35,7 +35,7 @@ spec: enabled: true persistence: postgresql: - {{- if .Values.upstream.postgresql.enabled }} +{{- if .Values.upstream.postgresql.enabled }} secretRef: name: {{ .Release.Name }}-postgresql-svcbind-postgres userKey: username @@ -44,23 +44,23 @@ spec: name: {{ .Release.Name }}-postgresql namespace: {{ .Release.Namespace }} databaseName: sonataflow - {{- else }} +{{- else }} secretRef: name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} userKey: POSTGRES_USER passwordKey: POSTGRES_PASSWORD - jdbcUrl: jdbc:postgresql://{{ .Values.upstream.backstage.appConfig.backend.database.connection.host}}:{{ .Values.upstream.backstage.appConfig.backend.database.connection.port}}/sonataflow?currentSchema=data-index-service - {{- end }} - {{- if .Values.orchestrator.sonataflowPlatform.dataIndexImage }} + jdbcUrl: jdbc:postgresql://{{ .Values.orchestrator.sonataflowPlatform.externalDBHost }}:{{ .Values.orchestrator.sonataflowPlatform.externalDBPort }}/sonataflow?currentSchema=data-index-service +{{- end }} +{{- if .Values.orchestrator.sonataflowPlatform.dataIndexImage }} podTemplate: container: image: {{ .Values.orchestrator.sonataflowPlatform.dataIndexImage }} - {{- end }} +{{- end }} jobService: enabled: true persistence: postgresql: - {{- if .Values.upstream.postgresql.enabled }} +{{- if .Values.upstream.postgresql.enabled }} secretRef: name: {{ .Release.Name }}-postgresql-svcbind-postgres userKey: username @@ -69,18 +69,18 @@ spec: name: {{ .Release.Name }}-postgresql namespace: {{ .Release.Namespace }} databaseName: sonataflow - {{- else }} +{{- else }} secretRef: - name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef}} + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} userKey: POSTGRES_USER passwordKey: POSTGRES_PASSWORD - jdbcUrl: jdbc:postgresql://{{ .Values.upstream.backstage.appConfig.backend.database.connection.host}}:{{ .Values.upstream.backstage.appConfig.backend.database.connection.port}}/sonataflow?currentSchema=jobs-service - {{- end }} - {{- if .Values.orchestrator.sonataflowPlatform.jobServiceImage }} + jdbcUrl: jdbc:postgresql://{{ .Values.orchestrator.sonataflowPlatform.externalDBHost }}:{{ .Values.orchestrator.sonataflowPlatform.externalDBPort }}/sonataflow?currentSchema=jobs-service +{{- end }} +{{- if .Values.orchestrator.sonataflowPlatform.jobServiceImage }} podTemplate: container: image: {{ .Values.orchestrator.sonataflowPlatform.jobServiceImage }} - {{- end }} +{{- end }} --- apiVersion: batch/v1 kind: Job @@ -88,6 +88,7 @@ metadata: name: {{ .Release.Name }}-create-sonataflow-database namespace: {{ .Release.Namespace }} spec: + activeDeadlineSeconds: 120 template: spec: initContainers: @@ -111,17 +112,31 @@ spec: - bash - -c - | - {{- if .Values.upstream.postgresql.enabled }} +{{- if .Values.upstream.postgresql.enabled }} dbHost="{{ .Release.Name }}-postgresql" dbPort="5432" - {{- else }} - dbHost="{{ .Values.upstream.backstage.appConfig.backend.database.connection.host }}" - dbPort="{{ .Values.upstream.backstage.appConfig.backend.database.connection.port }}" - {{- end }} - until timeout --preserve-status --kill-after=3 2 bash -c ">/dev/tcp/${dbHost}/${dbPort}"; do +{{- else }} + dbHost=${POSTGRES_HOST} + dbPort=${POSTGRES_PORT} +{{- end }} + until timeout 2 bash -c ">/dev/tcp/$dbHost/$dbPort"; do echo 'Waiting for DB...' sleep 2 done + echo 'Connection made!' +{{- if not .Values.upstream.postgresql.enabled }} + env: + - name: POSTGRES_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_HOST + - name: POSTGRES_PORT + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_PORT +{{- end }} containers: - name: psql image: "{{- tpl .Values.orchestrator.sonataflowPlatform.createDBJobImage . -}}" @@ -140,22 +155,41 @@ spec: drop: - ALL env: +{{- if .Values.upstream.postgresql.enabled }} - name: PGPASSWORD valueFrom: -{{- if .Values.upstream.postgresql.enabled }} secretKeyRef: name: {{ .Release.Name }}-postgresql-svcbind-postgres key: password +{{- else }} + - name: POSTGRES_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_HOST + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_USER + - name: POSTGRES_PORT + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_PORT + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef }} + key: POSTGRES_PASSWORD +{{- end }} command: [ "sh", "-c" ] +{{- if .Values.upstream.postgresql.enabled }} args: - - "psql -h {{ .Release.Name }}-postgresql -U postgres -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" + - "psql -h {{ .Release.Name }}-postgresql -p 5432 -U postgres -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" {{- else }} - secretKeyRef: - name: {{ .Values.orchestrator.sonataflowPlatform.externalDBsecretRef}} - key: POSTGRES_PASSWORD - command: [ "sh", "-c" ] args: - - "psql -h {{ .Values.upstream.backstage.appConfig.backend.database.connection.host }} -U {{ .Values.upstream.backstage.appConfig.backend.database.connection.user }} -d {{ .Values.orchestrator.sonataflowPlatform.externalDBName }} -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" + - "psql -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} -d {{ .Values.orchestrator.sonataflowPlatform.externalDBName }} -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" {{- end }} restartPolicy: Never backoffLimit: 2 diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index d9148d43..aa9b6eb1 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -155,11 +155,21 @@ "title": "eventing configuration", "type": "object" }, + "externalDBHost": { + "additionalProperties": false, + "title": "Host for the user-configured external Database", + "type": "string" + }, "externalDBName": { "additionalProperties": false, "title": "Name for the user-configured external Database", "type": "string" }, + "externalDBPort": { + "additionalProperties": false, + "title": "Port for the user-configured external Database", + "type": "string" + }, "externalDBsecretRef": { "additionalProperties": false, "title": "Secret name for the user-created secret to connect an external DB", diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index 73110f0c..02e31075 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -322,6 +322,16 @@ "type": "string", "additionalProperties": false }, + "externalDBHost": { + "title": "Host for the user-configured external Database", + "type": "string", + "additionalProperties": false + }, + "externalDBPort": { + "title": "Port for the user-configured external Database", + "type": "string", + "additionalProperties": false + }, "initContainerImage": { "title": "Image for the init container used by the create-db job", "type": "string", diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 474f1104..0088345c 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -375,6 +375,12 @@ orchestrator: # -- Name for the user-configured external Database externalDBName: "" + # -- Host for the user-configured external Database + externalDBHost: "" + + # -- Port for the user-configured external Database + externalDBPort: "" + # -- Image for the init container used by the create-db job initContainerImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" From 51bef3b740673f746954255899b7a24559b0b9b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:27:41 +0000 Subject: [PATCH 047/117] chore(deps): update helm release backstage to v2.5.3 (#175) * chore(deps): update helm release backstage to v2.5.3 * chore(pre-commit): Auto-fix hooks Co-authored-by: renovate[bot] * Bump version to 4.2.11 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 4a822f33..10ce5bc1 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.31.3 - name: backstage repository: https://backstage.github.io/charts - version: 2.5.2 -digest: sha256:31e6fedeea156b69c6d7f0e35473eb12f2570cf3fbe6520fc2427c38277604d1 -generated: "2025-06-12T19:30:29.701558818Z" + version: 2.5.3 +digest: sha256:551409ba984e6b0d3fef38eea2d571b1d033bea3ff461fabfd803d06b3347341 +generated: "2025-06-24T12:40:14.794985613Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index c0a10837..413b8375 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -28,7 +28,7 @@ dependencies: version: "2.31.3" - name: backstage repository: https://backstage.github.io/charts - version: "2.5.2" + version: "2.5.3" alias: upstream home: https://red.ht/rhdh icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjE5MS44OCIKICAgaGVpZ2h0PSIxOTEuODgiCiAgIHZpZXdCb3g9IjAgMCAxOTEuODggMTkxLjg4IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgiIC8+CiAgPGcKICAgICBpZD0idXVpZC03OTAxZjg3OC1jZTAwLTQ0MWYtYWMyNi1kZGQzNjU0ZDRmNzkiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxyZWN0CiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIHdpZHRoPSIzNiIKICAgICAgIGhlaWdodD0iMzYiCiAgICAgICByeD0iOSIKICAgICAgIHJ5PSI5IgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InJlY3QyIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjgsMi4yNSBjIDQuMjczMzYsMCA3Ljc1LDMuNDc2NjQgNy43NSw3Ljc1IHYgMTggYyAwLDQuMjczMzYgLTMuNDc2NjQsNy43NSAtNy43NSw3Ljc1IEggMTAgQyA1LjcyNjY0LDM1Ljc1IDIuMjUsMzIuMjczMzYgMi4yNSwyOCBWIDEwIEMgMi4yNSw1LjcyNjY0IDUuNzI2NjQsMi4yNSAxMCwyLjI1IEggMjggTSAyOCwxIEggMTAgQyA1LjAyOTQ0LDEgMSw1LjAyOTQzIDEsMTAgdiAxOCBjIDAsNC45NzA1NyA0LjAyOTQ0LDkgOSw5IGggMTggYyA0Ljk3MDU2LDAgOSwtNC4wMjk0MyA5LC05IFYgMTAgQyAzNyw1LjAyOTQzIDMyLjk3MDU2LDEgMjgsMSBaIgogICAgICAgZmlsbD0iIzRkNGQ0ZCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNCIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0idXVpZC1jM2NhNjg5MS02ZTE4LTQyY2ItODUyYi0zZGVkZDZjMzFlNjgiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI2LjQ0MjM4LDI1LjU1ODExIC0zLjc3Mzc0LC0zLjc3Mzc0IGMgMC41OTE0MywtMC43NzcwNCAwLjk1NjM2LC0xLjczNDggMC45NTYzNiwtMi43ODQzNiAwLC0yLjU1MDI5IC0yLjA3NTIsLTQuNjI1IC00LjYyNSwtNC42MjUgLTIuNTUwMjksMCAtNC42MjUsMi4wNzQ3MSAtNC42MjUsNC42MjUgMCwyLjU1MDI5IDIuMDc0NzEsNC42MjUgNC42MjUsNC42MjUgMS4wNDk0NCwwIDIuMDA3MjYsLTAuMzY0OTMgMi43ODQzNiwtMC45NTYzNiBsIDMuNzczMjUsMy43NzMyNSBjIDAuMTIyMDcsMC4xMjIwNyAwLjI4MjIzLDAuMTgzMTEgMC40NDIzOCwwLjE4MzExIDAuMTYwMTUsMCAwLjMyMDMxLC0wLjA2MTA0IDAuNDQyMzgsLTAuMTgzMTEgMC4yNDMxNiwtMC4yNDQxNCAwLjI0MzE2LC0wLjYzOTY1IDAsLTAuODgzNzkgeiBNIDE1LjYyNSwxOSBjIDAsLTEuODYwODQgMS41MTQxNiwtMy4zNzUgMy4zNzUsLTMuMzc1IDEuODYxMzMsMCAzLjM3NSwxLjUxNDE2IDMuMzc1LDMuMzc1IDAsMS44NjA4NCAtMS41MTM2NywzLjM3NSAtMy4zNzUsMy4zNzUgLTEuODYwODQsMCAtMy4zNzUsLTEuNTE0MTYgLTMuMzc1LC0zLjM3NSB6IgogICAgICAgZmlsbD0iI2VlMDAwMCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNyIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI3LDEzLjYyNSBjIDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMS40NDcyNyAtMS4xNzc3MywtMi42MjUgLTIuNjI1LC0yLjYyNSAtMS40NDcyNywwIC0yLjYyNSwxLjE3NzczIC0yLjYyNSwyLjYyNSAwLDAuNDk2NyAwLjE0NjYxLDAuOTU2NTQgMC4zODcyNywxLjM1MzAzIGwgLTEuMjA0NjUsMS4yMDUwOCBjIC0wLjI0NDE0LDAuMjQ0MTQgLTAuMjQzMTYsMC42Mzk2NSA5LjhlLTQsMC44ODM3OSAwLjEyMTA5LDAuMTIyMDcgMC4yODEyNSwwLjE4MzExIDAuNDQxNDEsMC4xODMxMSAwLjE2MDE2LDAgMC4zMjAzMSwtMC4wNjEwNCAwLjQ0MjM4LC0wLjE4MzExIGwgMS4yMDQxLC0xLjIwNDQ3IGMgMC4zOTY2MSwwLjI0MDkxIDAuODU2NjMsMC4zODc1NyAxLjM1MzUyLDAuMzg3NTcgeiBtIDAsLTQgYyAwLjc1NzgxLDAgMS4zNzUsMC42MTY3IDEuMzc1LDEuMzc1IDAsMC43NTgzIC0wLjYxNzE5LDEuMzc1IC0xLjM3NSwxLjM3NSAtMC4zNzgxMSwwIC0wLjcyMTA3LC0wLjE1MzY5IC0wLjk2OTk3LC0wLjQwMTczIC03LjNlLTQsLTcuM2UtNCAtOS44ZS00LC0wLjAwMTggLTAuMDAxNywtMC4wMDI2IC02LjFlLTQsLTYuMWUtNCAtMC4wMDE1LC03LjllLTQgLTAuMDAyMSwtMC4wMDE0IC0wLjI0NzYyLC0wLjI0ODc4IC0wLjQwMTE4LC0wLjU5MTM3IC0wLjQwMTE4LC0wLjk2OTMgMCwtMC43NTgzIDAuNjE3MTksLTEuMzc1IDEuMzc1LC0xLjM3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5LDguMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDExIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTksMjUuMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDEzIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjcuNSwxNi44NzUgYyAtMS4xNzE4OCwwIC0yLjEyNSwwLjk1MzEyIC0yLjEyNSwyLjEyNSAwLDEuMTcxODggMC45NTMxMiwyLjEyNSAyLjEyNSwyLjEyNSAxLjE3MTg4LDAgMi4xMjUsLTAuOTUzMTIgMi4xMjUsLTIuMTI1IDAsLTEuMTcxODggLTAuOTUzMTIsLTIuMTI1IC0yLjEyNSwtMi4xMjUgeiBtIDAsMyBjIC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgMCwtMC40ODI0MiAwLjM5MjU4LC0wLjg3NSAwLjg3NSwtMC44NzUgMC40ODI0MiwwIDAuODc1LDAuMzkyNTggMC44NzUsMC44NzUgMCwwLjQ4MjQyIC0wLjM5MjU4LDAuODc1IC0wLjg3NSwwLjg3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoMTUiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMi42MjUsMTkgYyAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IC0xLjE3MTg4LDAgLTIuMTI1LDAuOTUzMTIgLTIuMTI1LDIuMTI1IDAsMS4xNzE4OCAwLjk1MzEyLDIuMTI1IDIuMTI1LDIuMTI1IDEuMTcxODgsMCAyLjEyNSwtMC45NTMxMiAyLjEyNSwtMi4xMjUgeiBtIC0zLDAgYyAwLC0wLjQ4MjQyIDAuMzkyNTgsLTAuODc1IDAuODc1LC0wLjg3NSAwLjQ4MjQyLDAgMC44NzUsMC4zOTI1OCAwLjg3NSwwLjg3NSAwLDAuNDgyNDIgLTAuMzkyNTgsMC44NzUgLTAuODc1LDAuODc1IC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDE3IiAvPgogICAgPHBhdGgKICAgICAgIGQ9Ik0gMTMuMjM3NDMsMTIuMzUzNjQgQyAxMy40NzgzNCwxMS45NTcwMyAxMy42MjUsMTEuNDk2ODkgMTMuNjI1LDExIDEzLjYyNSw5LjU1MjczIDEyLjQ0NzI3LDguMzc1IDExLDguMzc1IDkuNTUyNzMsOC4zNzUgOC4zNzUsOS41NTI3MyA4LjM3NSwxMSBjIDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDAuNDk2ODksMCAwLjk1NzAzLC0wLjE0NjY3IDEuMzUzNjQsLTAuMzg3NTcgbCAxLjIwNDQ3LDEuMjA0NDcgYyAwLjEyMjA3LDAuMTIyMDcgMC4yODE3NCwwLjE4MzExIDAuNDQxODksMC4xODMxMSAwLjE2MDE1LDAgMC4zMTk4MiwtMC4wNjEwNCAwLjQ0MTg5LC0wLjE4MzExIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IEwgMTMuMjM3NDIsMTIuMzUzNjQgWiBNIDkuNjI1LDExIGMgMCwtMC43NTgzIDAuNjE2NywtMS4zNzUgMS4zNzUsLTEuMzc1IDAuNzU4MywwIDEuMzc1LDAuNjE2NyAxLjM3NSwxLjM3NSAwLDAuMzc3OTkgLTAuMTUzNSwwLjcyMDU4IC0wLjQwMTEyLDAuOTY5MzYgLTcuOWUtNCw3LjllLTQgLTAuMDAxOSwxMGUtNCAtMC4wMDI3LDAuMDAxOCAtOGUtNCw3LjllLTQgLTAuMDAxLDAuMDAxOSAtMC4wMDE4LDAuMDAyNyBDIDExLjcyMDU4LDEyLjIyMTUgMTEuMzc3OTksMTIuMzc1IDExLDEyLjM3NSAxMC4yNDE3LDEyLjM3NSA5LjYyNSwxMS43NTgzIDkuNjI1LDExIFoiCiAgICAgICBmaWxsPSIjZmZmZmZmIgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InBhdGgxOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDEzLjU1ODExLDIzLjU1ODExIC0xLjIwNDQ3LDEuMjA0NDcgQyAxMS45NTcwMywyNC41MjE2NyAxMS40OTY4OSwyNC4zNzUwMSAxMSwyNC4zNzUwMSBjIC0xLjQ0NzI3LDAgLTIuNjI1LDEuMTc3NzMgLTIuNjI1LDIuNjI1IDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMC40OTY4OSAtMC4xNDY2NywtMC45NTcwMyAtMC4zODc1NywtMS4zNTM2NCBMIDE0LjQ0MTksMjQuNDQxOSBjIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IC0wLjI0NDE0LC0wLjI0NDE0IC0wLjYzOTY1LC0wLjI0NDE0IC0wLjg4Mzc5LDAgeiBNIDExLDI4LjM3NSBjIC0wLjc1ODMsMCAtMS4zNzUsLTAuNjE2NyAtMS4zNzUsLTEuMzc1IDAsLTAuNzU4MyAwLjYxNjcsLTEuMzc1IDEuMzc1LC0xLjM3NSAwLjM3ODg1LDAgMC43MjIyOSwwLjE1Mzk5IDAuOTcxMTksMC40MDI1OSAyLjRlLTQsMi40ZS00IDIuNGUtNCw0LjllLTQgNC45ZS00LDcuM2UtNCAyLjVlLTQsMi40ZS00IDQuOWUtNCwyLjRlLTQgNy4zZS00LDQuOWUtNCAwLjI0ODYsMC4yNDg5IDAuNDAyNTksMC41OTIzNSAwLjQwMjU5LDAuOTcxMTkgMCwwLjc1ODMgLTAuNjE2NywxLjM3NSAtMS4zNzUsMS4zNzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDIxIiAvPgogIDwvZz4KPC9zdmc+Cg== @@ -50,4 +50,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.10 +version: 4.2.11 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index ff7c5620..51264987 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift (Community Version) -![Version: 4.2.10](https://img.shields.io/badge/Version-4.2.10-informational?style=flat-square) +![Version: 4.2.11](https://img.shields.io/badge/Version-4.2.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Red Hat Developer Hub is a Red Hat supported version of Backstage. @@ -163,7 +163,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.5.2 | +| https://backstage.github.io/charts | upstream(backstage) | 2.5.3 | | https://charts.bitnami.com/bitnami | common | 2.31.3 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index aa9b6eb1..a47a6998 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -1369,7 +1369,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. Must be a C_IDENTIFIER.", + "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", "type": "string" }, "value": { @@ -1487,7 +1487,7 @@ "x-kubernetes-patch-strategy": "merge" }, "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.", + "description": "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. 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 or Secrets", "properties": { @@ -1506,7 +1506,7 @@ "type": "object" }, "prefix": { - "description": "Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER.", + "description": "Optional text to prepend to the name of each environment variable. May consist of any printable ASCII characters except '='.", "type": "string" }, "secretRef": { @@ -2603,7 +2603,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. Must be a C_IDENTIFIER.", + "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", "type": "string" }, "value": { @@ -4595,7 +4595,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. Must be a C_IDENTIFIER.", + "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", "type": "string" }, "value": { @@ -4713,7 +4713,7 @@ "x-kubernetes-patch-strategy": "merge" }, "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.", + "description": "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. 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 or Secrets", "properties": { @@ -4732,7 +4732,7 @@ "type": "object" }, "prefix": { - "description": "Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER.", + "description": "Optional text to prepend to the name of each environment variable. May consist of any printable ASCII characters except '='.", "type": "string" }, "secretRef": { From be64c8a986c3018d923ca288918662ec3a135d43 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 26 Jun 2025 11:34:00 +0100 Subject: [PATCH 048/117] fix: avoid double helm upgrade in CI install script (#174) * fix: avoid double helm upgrade in CI install script Signed-off-by: Fortune-Ndlovu * handle secret not found Signed-off-by: Fortune-Ndlovu * remove code block Signed-off-by: Fortune-Ndlovu * update Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- .rhdh/scripts/install.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index d5f4e561..0848be70 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -133,11 +133,6 @@ EOF rm -fr /tmp/"$CV"-unpacked fi -# 1. install (or upgrade) -helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" - -# 2. collect values -PASSWORD=$(kubectl get secret redhat-developer-hub-postgresql -o jsonpath="{.data.password}" | base64 -d) if [[ $(oc auth can-i get route/openshift-console) == "yes" ]]; then CLUSTER_ROUTER_BASE=$(oc get route console -n openshift-console -o=jsonpath='{.spec.host}' | sed 's/^[^.]*\.//') elif [[ -z $CLUSTER_ROUTER_BASE ]]; then @@ -148,10 +143,9 @@ elif [[ -z $CLUSTER_ROUTER_BASE ]]; then exit 1 fi -# 3. change values +# change values helm upgrade redhat-developer-hub -i "${CHART_URL}" --version "$CV" \ - --set global.clusterRouterBase="${CLUSTER_ROUTER_BASE}" \ - --set global.postgresql.auth.password="$PASSWORD" + --set global.clusterRouterBase="${CLUSTER_ROUTER_BASE}" echo " While deploying you can watch at From 7dc82fe68f2293a9264b319d57d9cfb94c82817f Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Thu, 26 Jun 2025 13:38:56 -0300 Subject: [PATCH 049/117] chore: link to 1.6 docs, which have changed path again (RHIDP-7643) [main] (#178) * chore: link to 1.6 docs, which have changed path again Signed-off-by: Nick Boldt * chore: link to 1.6 docs, which have changed path again (version bump) Signed-off-by: Nick Boldt * return to the old, simpler header text for RHDH chart; remove '(Community Version)' from rhdh and orch infra chart readmes; bump orch infra to 0.1.0 because 0.0.z is for the 1.6 branch Signed-off-by: Nick Boldt * Update charts/backstage/Chart.yaml * Update charts/backstage/README.md --------- Signed-off-by: Nick Boldt --- charts/backstage/Chart.yaml | 9 +++------ charts/backstage/README.md | 11 ++++------- charts/backstage/README.md.gotmpl | 2 +- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 4 ++-- charts/orchestrator-infra/README.md.gotmpl | 2 +- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 413b8375..0db4f6e6 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -14,12 +14,9 @@ annotations: charts.openshift.io/supportURL: https://access.redhat.com/support apiVersion: v2 description: | - Red Hat Developer Hub is a Red Hat supported version of Backstage. - It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can - help streamline the process of setting up a self-managed internal - developer portal for adopters who are just starting out. + A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. - The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.5/html-single/telemetry_data_collection/index + The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.6/html-single/telemetry_data_collection_and_analysis/index dependencies: - name: common repository: https://charts.bitnami.com/bitnami @@ -50,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.11 +version: 4.2.12 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 51264987..6e765b75 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,15 +1,12 @@ -# RHDH Backstage Helm Chart for OpenShift (Community Version) +# RHDH Backstage Helm Chart for OpenShift -![Version: 4.2.11](https://img.shields.io/badge/Version-4.2.11-informational?style=flat-square) +![Version: 4.2.12](https://img.shields.io/badge/Version-4.2.12-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) -Red Hat Developer Hub is a Red Hat supported version of Backstage. -It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can -help streamline the process of setting up a self-managed internal -developer portal for adopters who are just starting out. +A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. -The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.5/html-single/telemetry_data_collection/index +The telemetry data collection feature is enabled by default. Red Hat Developer Hub sends telemetry data to Red Hat by using the `backstage-plugin-analytics-provider-segment` plugin. To disable this and to learn what data is being collected, see https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.6/html-single/telemetry_data_collection_and_analysis/index **Homepage:** diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index 14a3e574..f9a280a7 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -1,4 +1,4 @@ -# RHDH Backstage Helm Chart for OpenShift (Community Version) +# RHDH Backstage Helm Chart for OpenShift {{ template "chart.deprecationWarning" . }} diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index 50631e9b..e14f2f43 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.0.5 +version: 0.1.0 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index f1d519f6..9bb54f26 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ -# Orchestrator Infra Chart for OpenShift (Community Version) +# Orchestrator Infra Chart for OpenShift -![Version: 0.0.5](https://img.shields.io/badge/Version-0.0.5-informational?style=flat-square) +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index e3764bf7..07244b82 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -1,4 +1,4 @@ -# Orchestrator Infra Chart for OpenShift (Community Version) +# Orchestrator Infra Chart for OpenShift {{ template "chart.deprecationWarning" . }} From 470b5100b12082c58a96527304715f52e0566243 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 30 Jun 2025 11:41:06 +0200 Subject: [PATCH 050/117] ci: Add CI values file for testing the Orchestrator flavor [RHIDP-7469] (#177) * Add CI values file for testing the Orchestrator flavor in the Backstage chart * Install the Knative and SonataFlow CRDs in CI as prerequisite The Orchestrator flavor requires installing the orchestrator-infra chart as a prerequisite, but the OpenShift Serverless and Serverless Operators installed by this chart are available only on OCP (from the Red Hat Catalog). And we cannot install the orchestrator-infra-chart directly here using the charts/orchestrator-infra/ci/upstream-olm-values.yaml file because `ct install` on the orchestrator-infra chart would then fail to install it due to mismatching resource labels. For the simple testing that we are doing here on a vanilla K8s cluster, we only need both the Knative and SonataFlow CRDs. Additional nOTES: - Serverless 1.35 corresponds to Knative 1.18 - Serverless Logic 1.35 corresponds to Sonataflow 1.43 * Bump the backstage chart version --- .github/workflows/test.yaml | 23 +++++++++++++++++-- .../ci/with-orchestrator-values.yaml | 20 ++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 charts/backstage/ci/with-orchestrator-values.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f8ab90d3..b267f81b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -54,9 +54,12 @@ jobs: - name: Run chart-testing (list-changed) id: list-changed run: | - changed=$(ct list-changed --target-branch "${{ github.event.pull_request.base.ref }}") - if [[ -n "$changed" ]]; then + listChanged=$(ct list-changed --target-branch "${{ github.event.pull_request.base.ref }}") + if [[ -n "$listChanged" ]]; then echo "changed=true" >> "$GITHUB_OUTPUT" + if grep 'charts/backstage' <<< "$listChanged"; then + echo "backstageChartChanged=true" >> "$GITHUB_OUTPUT" + fi fi - name: Add Helm Repositories @@ -130,6 +133,22 @@ jobs: chmod +x install-olm.sh ./install-olm.sh "${OLM_VERSION}" + # https://issues.redhat.com/browse/RHIDP-7469 - minimal testing of the Orchestrator flavor. + # The Orchestrator flavor requires installing the orchestrator-infra chart as a prerequisite, + # but the OpenShift Serverless and Serverless Operators installed by this chart are available only on OCP (from the Red Hat Catalog). + # For the simple testing that we are doing here on a vanilla K8s cluster, we only need both the Knative and SonataFlow CRDs. + # TODO(rm3l): Update this when/if there is an upstream counterpart installable via OLM. + # NOTES: + # - Serverless 1.35 corresponds to Knative 1.18 + # - Serverless Logic 1.35 corresponds to Sonataflow 1.43 + - name: Install Knative and SonataFlow CRDs via the orchestrator-infra-chart as minimum prerequisite for testing the Orchestrator flavor + if: steps.list-changed.outputs.backstageChartChanged == 'true' + run: | + for crdDir in charts/orchestrator-infra/crds/*; do + kubectl apply -f "${crdDir}" + done + kubectl apply -f https://github.com/apache/incubator-kie-kogito-serverless-operator/releases/download/v1.43.1/operator.yaml + - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' run: | diff --git a/charts/backstage/ci/with-orchestrator-values.yaml b/charts/backstage/ci/with-orchestrator-values.yaml new file mode 100644 index 00000000..bf438861 --- /dev/null +++ b/charts/backstage/ci/with-orchestrator-values.yaml @@ -0,0 +1,20 @@ +route: + enabled: false + +upstream: + postgresql: + primary: + persistence: + enabled: false + +global: + dynamic: + plugins: + # Enable additional plugins, which should be merged with the Orchestrator plugins + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic + disabled: false + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import + disabled: false + +orchestrator: + enabled: true From f065f3d8172f646c12d5ccd59f180196dddd8a5f Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 30 Jun 2025 16:14:54 +0200 Subject: [PATCH 051/117] chore: Bump `backstage` chart version to fix the release workflow (#181) * chore: Bump `backstage` chart version to fix release workflow Follow-up to https://github.com/redhat-developer/rhdh-chart/pull/177 * chore(pre-commit): Auto-fix hooks Co-authored-by: rm3l --------- Co-authored-by: github-actions[bot] Co-authored-by: rm3l --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 0db4f6e6..27ff8cf4 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.12 +version: 4.2.13 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 6e765b75..c5c62a9c 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.2.12](https://img.shields.io/badge/Version-4.2.12-informational?style=flat-square) +![Version: 4.2.13](https://img.shields.io/badge/Version-4.2.13-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. From ca2064d514c258857d7d255db472626396debd7a Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:06:22 +0300 Subject: [PATCH 052/117] updated plugins to 1.6 and bumped chart y stream version (#182) --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/_partials.tpl | 30 +++++++++++++++--------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 27ff8cf4..cece1e75 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.2.13 +version: 4.3.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index c5c62a9c..1a2d31a6 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.2.13](https://img.shields.io/badge/Version-4.2.13-informational?style=flat-square) +![Version: 4.3.0](https://img.shields.io/badge/Version-4.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/_partials.tpl b/charts/backstage/templates/_partials.tpl index e36be41b..dc3d91e0 100644 --- a/charts/backstage/templates/_partials.tpl +++ b/charts/backstage/templates/_partials.tpl @@ -17,33 +17,41 @@ plugins: red-hat-developer-hub.backstage-plugin-orchestrator: appIcons: - importName: OrchestratorIcon - module: OrchestratorPlugin name: orchestratorIcon dynamicRoutes: - importName: OrchestratorPage menuItem: icon: orchestratorIcon text: Orchestrator - module: OrchestratorPlugin path: /orchestrator - - disabled: true + - disabled: false package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.package }}" - integrity: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.integrity }}"{{- end }} + integrity: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.integrity }}" pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - + - disabled: false + package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestratorFormWidgets.package }}" + integrity: "{{ $config.orchestratorPlugins.orchestratorFormWidgets.integrity }}" + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} +{{- end }} {{- define "orchestrator.plugins.config" }} orchestratorPlugins: scope: "@redhat" orchestrator: - package: "backstage-plugin-orchestrator@1.5.1" - integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + package: "backstage-plugin-orchestrator@1.6.0" + integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== orchestratorBackend: - package: "backstage-plugin-orchestrator-backend-dynamic@1.5.1" - integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== + package: "backstage-plugin-orchestrator-backend-dynamic@1.6.0" + integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== scaffolderBackendOrchestrator: - package: "backstage-plugin-scaffolder-backend-module-orchestrator-dynamic@1.5.1" - integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== + package: "backstage-plugin-scaffolder-backend-module-orchestrator-dynamic@1.6.0" + integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== + orchestratorFormWidgets: + package: "backstage-plugin-orchestrator-form-widgets@1.6.0" + integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== {{- end }} \ No newline at end of file From 197a1a4f4af94c0197eb13caf13f2800b728280b Mon Sep 17 00:00:00 2001 From: Nick Boldt Date: Thu, 3 Jul 2025 08:48:40 -0300 Subject: [PATCH 053/117] Update installing-ci-charts.adoc to set generic placeholder for the chart tag and namespace (#183) --- .rhdh/docs/installing-ci-charts.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rhdh/docs/installing-ci-charts.adoc b/.rhdh/docs/installing-ci-charts.adoc index 838606e8..20c49f8c 100644 --- a/.rhdh/docs/installing-ci-charts.adoc +++ b/.rhdh/docs/installing-ci-charts.adoc @@ -13,8 +13,9 @@ cd /tmp # Create or select a namespace # Install the chart repo # Install the chart, then update the clusterRouterBase +# NOTE: Replace 1.y-zzz with an actual chart from https://quay.io/repository/rhdh/chart?tab=tags&tag=latest curl -sSLO https://raw.githubusercontent.com/redhat-developer/rhdh-chart/refs/heads/main/.rhdh/scripts/install.sh && chmod +x install.sh -./install.sh 1.7-20-CI --namespace rhdh-1-7-20-ci +./install.sh 1.y-zzz-CI --namespace rhdh-ci ``` That's it! From d41223894050419a0b09ad5bd2430838147c1415 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 07:53:04 +0000 Subject: [PATCH 054/117] chore(deps): update mikefarah/yq action to v4.46.1 (#185) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 7971f021..3b1b6b0e 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -81,7 +81,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@b534aa9ee5d38001fba3cd8fe254a037e4847b37 # v4.45.4 + uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 with: cmd: yq ".version" charts/backstage/Chart.yaml @@ -94,7 +94,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@b534aa9ee5d38001fba3cd8fe254a037e4847b37 # v4.45.4 + uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml From 91a7ed5475d61d0ebd752aba1732db0c7f003099 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:43:57 +0300 Subject: [PATCH 055/117] Adding Orchestrator Software Templates Infra Helm Chart (#170) * Openshift pipelines chart + test * pipelines chart with notes, test and cleanup * Added openshift-gitops subchart, without test or cleanup job * gitops-infra functional, without test * paramaterizing cleanup job image * fixing security issues after SonarQube analysis * linter and templating issues * Fixing RBAC and linter * Cleanup job now runs * user permissions to crd task, namespace env var in pipelines * After review - adding CI values, generifying for k8s install * fixing upstream values * Shorten names on test resources * wait for crd in pipelines test * Add job to wait for CRD * seccompProfile * fixing pipelines-test * trying to fix the pipelines test with dynamic task and taskrun CRs * adding deletion hook for test * removing deletion hook for test * fixed infra test, fixed post cleanup job * fixing upgrade bugs * trying to fixing upgrade issue - removing hooks * Fixing delete errors, delete test upon finish * trying to fix infra test * debugging steps * more debugging * tidying up test --- .../.helmignore | 23 ++ .../Chart.yaml | 16 ++ .../README.md | 147 +++++++++++ .../README.md.gotmpl | 77 ++++++ .../ci/upstream-values.yaml | 26 ++ .../docs/GitopsOperator.md | 52 ++++ .../templates/NOTES.txt | 50 ++++ .../templates/_helpers.tpl | 37 +++ .../templates/catalogsource.yaml | 20 ++ ...cd-application-controller-clusterrole.yaml | 15 ++ ...ication-controller-clusterrolebinding.yaml | 23 ++ .../templates/openshift-gitops/argocd-cr.yaml | 17 ++ .../argocd-credential-template.yaml | 19 ++ .../openshift-gitops/argocd-initial-apps.yaml | 25 ++ .../argocd-initial-repositories.yaml | 17 ++ .../argocd-server-clusterrole.yaml | 14 ++ .../argocd-server-clusterrolebinding.yaml | 21 ++ .../openshift-gitops/crd-reader.yaml | 35 +++ .../templates/openshift-gitops/namespace.yaml | 21 ++ .../openshift-gitops/post-cleanup.yaml | 229 ++++++++++++++++++ .../templates/openshift-gitops/secrets.yaml | 26 ++ .../openshift-gitops/subscription.yaml | 18 ++ .../openshift-gitops/wait-for-crd.yaml | 89 +++++++ .../openshift-pipelines/post-cleanup.yaml | 108 +++++++++ .../openshift-pipelines/subscription.yaml | 15 ++ .../templates/tests/infra-test.yaml | 91 +++++++ .../values.yaml | 163 +++++++++++++ 27 files changed, 1394 insertions(+) create mode 100644 charts/orchestrator-software-templates-infra/.helmignore create mode 100644 charts/orchestrator-software-templates-infra/Chart.yaml create mode 100644 charts/orchestrator-software-templates-infra/README.md create mode 100644 charts/orchestrator-software-templates-infra/README.md.gotmpl create mode 100644 charts/orchestrator-software-templates-infra/ci/upstream-values.yaml create mode 100644 charts/orchestrator-software-templates-infra/docs/GitopsOperator.md create mode 100644 charts/orchestrator-software-templates-infra/templates/NOTES.txt create mode 100644 charts/orchestrator-software-templates-infra/templates/_helpers.tpl create mode 100644 charts/orchestrator-software-templates-infra/templates/catalogsource.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-gitops/wait-for-crd.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-pipelines/post-cleanup.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml create mode 100644 charts/orchestrator-software-templates-infra/templates/tests/infra-test.yaml create mode 100644 charts/orchestrator-software-templates-infra/values.yaml diff --git a/charts/orchestrator-software-templates-infra/.helmignore b/charts/orchestrator-software-templates-infra/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/orchestrator-software-templates-infra/Chart.yaml b/charts/orchestrator-software-templates-infra/Chart.yaml new file mode 100644 index 00000000..d6a9c4b9 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/Chart.yaml @@ -0,0 +1,16 @@ +annotations: + charts.openshift.io/name: Orchestrator Software Templates Infrastructure for Red Hat Developer Hub + charts.openshift.io/provider: Red Hat Developer Hub Team + charts.openshift.io/supportURL: https://issues.redhat.com/browse/RHIDP +apiVersion: v2 +name: orchestrator-software-templates-infra +description: > + A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. +kubeVersion: ">= 1.25.0-0" +type: application +sources: + - https://github.com/redhat-developer/rhdh-software-templates-infrastructure +version: 0.1.0 +maintainers: + - name: Red Hat Developer Hub Team + url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates-infra/README.md b/charts/orchestrator-software-templates-infra/README.md new file mode 100644 index 00000000..0bbd53ca --- /dev/null +++ b/charts/orchestrator-software-templates-infra/README.md @@ -0,0 +1,147 @@ + +# Orchestrator Software Templates Infra Chart for OpenShift (Community Version) + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| Red Hat Developer Hub Team | | | + +## Source Code + +* + +## Requirements + +Kubernetes: `>= 1.25.0-0` + +## TL;DR + +```console +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra +``` + +> **Tip**: List all releases using `helm list` + +## Testing a Release + +Once an Helm Release has been deployed, you can test it using the [`helm test`](https://helm.sh/docs/helm/helm_test/) command: + +```sh +helm test +``` + +This will run a simple Pod in the cluster to check that the required resources have been created. + +You can control whether to disable this test pod or you can also customize the image it leverages. +See the `test.enabled` and `test.image` parameters in the [`values.yaml`](./values.yaml) file. + +> **Tip**: Disabling the test pod will not prevent the `helm test` command from passing later on. It will simply report that no test suite is available. + +Below are a few examples: + +
+ +Disabling the test pod + +```sh +helm install \ + --set test.enabled=false +``` + +
+ +
+ +Customizing the test pod image + +```sh +helm install \ + --set test.image= +``` + +
+ +## Uninstalling the Chart + +To uninstall/delete a Helm release named `my-orchestrator-software-templates-infra`: + +```console +helm uninstall my-orchestrator-software-templates-infra +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Values + +| Key | Description | Type | Default | +|-----|-------------|------|---------| +| cleanupContainerImage | A container image and tag, will be used for the post-cleanup tasks. | string | `"bitnami/kubectl:latest"` | +| openshiftGitops.argocd_cr.controller.resources.limits.cpu | | string | `"2000m"` | +| openshiftGitops.argocd_cr.controller.resources.limits.memory | | string | `"2048Mi"` | +| openshiftGitops.argocd_cr.controller.resources.requests.cpu | | string | `"250m"` | +| openshiftGitops.argocd_cr.controller.resources.requests.memory | | string | `"1024Mi"` | +| openshiftGitops.argocd_cr.ha.enabled | | bool | `false` | +| openshiftGitops.argocd_cr.ha.resources.limits.cpu | | string | `"500m"` | +| openshiftGitops.argocd_cr.ha.resources.limits.memory | | string | `"256Mi"` | +| openshiftGitops.argocd_cr.ha.resources.requests.cpu | | string | `"250m"` | +| openshiftGitops.argocd_cr.ha.resources.requests.memory | | string | `"128Mi"` | +| openshiftGitops.argocd_cr.rbac.defaultPolicy | | string | `""` | +| openshiftGitops.argocd_cr.rbac.policy | | string | `"g, system:cluster-admins, role:admin\n"` | +| openshiftGitops.argocd_cr.rbac.scopes | | string | `"[groups]"` | +| openshiftGitops.argocd_cr.redis.resources.limits.cpu | | string | `"500m"` | +| openshiftGitops.argocd_cr.redis.resources.limits.memory | | string | `"256Mi"` | +| openshiftGitops.argocd_cr.redis.resources.requests.cpu | | string | `"250m"` | +| openshiftGitops.argocd_cr.redis.resources.requests.memory | | string | `"128Mi"` | +| openshiftGitops.argocd_cr.repo.resources.limits.cpu | | string | `"1000m"` | +| openshiftGitops.argocd_cr.repo.resources.limits.memory | | string | `"1024Mi"` | +| openshiftGitops.argocd_cr.repo.resources.requests.cpu | | string | `"250m"` | +| openshiftGitops.argocd_cr.repo.resources.requests.memory | | string | `"256Mi"` | +| openshiftGitops.argocd_cr.server.resources.limits.cpu | | string | `"500m"` | +| openshiftGitops.argocd_cr.server.resources.limits.memory | | string | `"256Mi"` | +| openshiftGitops.argocd_cr.server.resources.requests.cpu | | string | `"125m"` | +| openshiftGitops.argocd_cr.server.resources.requests.memory | | string | `"128Mi"` | +| openshiftGitops.argocd_cr.server.route.enabled | | bool | `true` | +| openshiftGitops.argocd_cr.sso.dex.openShiftOAuth | | bool | `true` | +| openshiftGitops.argocd_cr.sso.dex.resources.limits.cpu | | string | `"500m"` | +| openshiftGitops.argocd_cr.sso.dex.resources.limits.memory | | string | `"256Mi"` | +| openshiftGitops.argocd_cr.sso.dex.resources.requests.cpu | | string | `"250m"` | +| openshiftGitops.argocd_cr.sso.dex.resources.requests.memory | | string | `"128Mi"` | +| openshiftGitops.argocd_cr.sso.provider | | string | `"dex"` | +| openshiftGitops.enabled | whether the operator should be deployed by the chart | bool | `true` | +| openshiftGitops.initialApps | Initial applications to deploy | list | `[]` | +| openshiftGitops.initialRepositories | Initial repositories configuration | list | `[]` | +| openshiftGitops.name | name of instances | string | `"argocd"` | +| openshiftGitops.namespaces | namespace of rhdh instance, will be used to install openshift-gitops. | list | `["rhdh"]` | +| openshiftGitops.repositoryCredentials | Repository credential templates | list | `[]` | +| openshiftGitops.secrets | Secrets for Git access or other repository credentials | list | `[]` | +| openshiftGitops.subscription | subscription config | object | `{"namespace":"openshift-operators","spec":{"channel":"latest","disableDefaultArgoCD":true,"installPlanApproval":"Automatic","name":"openshift-gitops-operator","source":"redhat-operators","sourceNamespace":"openshift-marketplace"}}` | +| openshiftGitops.subscription.spec | namespace where the operator should be deployed | object | `{"channel":"latest","disableDefaultArgoCD":true,"installPlanApproval":"Automatic","name":"openshift-gitops-operator","source":"redhat-operators","sourceNamespace":"openshift-marketplace"}` | +| openshiftGitops.subscription.spec.channel | channel of an operator package to subscribe to | string | `"latest"` | +| openshiftGitops.subscription.spec.installPlanApproval | whether the update should be installed automatically | string | `"Automatic"` | +| openshiftGitops.subscription.spec.name | name of the operator package | string | `"openshift-gitops-operator"` | +| openshiftGitops.subscription.spec.source | name of the catalog source | string | `"redhat-operators"` | +| openshiftPipelines.enabled | whether the operator should be deployed by the chart | bool | `true` | +| openshiftPipelines.subscription.name | name of the operator package | string | `"openshift-pipelines-operator-rh"` | +| openshiftPipelines.subscription.namespace | namespace where the operator should be deployed | string | `"openshift-operators"` | +| openshiftPipelines.subscription.spec.channel | channel of an operator package to subscribe to | string | `"latest"` | +| openshiftPipelines.subscription.spec.installPlanApproval | whether the update should be installed automatically | string | `"Automatic"` | +| openshiftPipelines.subscription.spec.name | name of the operator package | string | `"openshift-pipelines-operator-rh"` | +| openshiftPipelines.subscription.spec.source | name of the catalog source | string | `"redhat-operators"` | +| openshiftPipelines.subscription.spec.sourceNamespace | | string | `"openshift-marketplace"` | +| resources.limits.cpu | | string | `"500m"` | +| resources.limits.memory | | string | `"1Gi"` | +| resources.requests.cpu | | string | `"250m"` | +| resources.requests.memory | | string | `"64Mi"` | +| test.enabled | Whether to enable the pod used for testing the Release using `helm test`. | bool | `true` | +| test.image.registry | Test infra-test Tekton Task pod image registry | string | `"bitnami"` | +| test.image.repository | Test infra-test Tekton Task pod image repository. | string | `"kubectl"` | +| test.image.tag | Test infra-test Tekton Task pod image tag. | string | `"latest"` | + diff --git a/charts/orchestrator-software-templates-infra/README.md.gotmpl b/charts/orchestrator-software-templates-infra/README.md.gotmpl new file mode 100644 index 00000000..79f95dea --- /dev/null +++ b/charts/orchestrator-software-templates-infra/README.md.gotmpl @@ -0,0 +1,77 @@ +# Orchestrator Software Templates Infra Chart for OpenShift (Community Version) + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +## TL;DR + +```console +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra +``` + +> **Tip**: List all releases using `helm list` + +## Testing a Release + +Once an Helm Release has been deployed, you can test it using the [`helm test`](https://helm.sh/docs/helm/helm_test/) command: + +```sh +helm test +``` + +This will run a simple Pod in the cluster to check that the required resources have been created. + +You can control whether to disable this test pod or you can also customize the image it leverages. +See the `test.enabled` and `test.image` parameters in the [`values.yaml`](./values.yaml) file. + +> **Tip**: Disabling the test pod will not prevent the `helm test` command from passing later on. It will simply report that no test suite is available. + +Below are a few examples: + +
+ +Disabling the test pod + +```sh +helm install \ + --set test.enabled=false +``` + +
+ +
+ +Customizing the test pod image + +```sh +helm install \ + --set test.image= +``` + +
+ +## Uninstalling the Chart + +To uninstall/delete a Helm release named `my-orchestrator-software-templates-infra`: + +```console +helm uninstall my-orchestrator-software-templates-infra +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +{{ template "chart.valuesSection" . }} diff --git a/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml b/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml new file mode 100644 index 00000000..7a40d91a --- /dev/null +++ b/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml @@ -0,0 +1,26 @@ +openshiftPipelines: + subscription: + namespace: operators + name: tektoncd-operator + spec: + channel: alpha + installPlanApproval: Automatic + name: tektoncd-operator + source: operatorhubio-catalog + sourceNamespace: olm + +openshiftGitops: + name: argocd + namespaces: + - olm + subscription: + namespace: operators + name: argocd-operator + spec: + channel: alpha + installPlanApproval: Automatic + name: argocd-operator + source: operatorhubio-catalog + sourceNamespace: olm + +cleanupContainerImage: bitnami/kubectl:latest diff --git a/charts/orchestrator-software-templates-infra/docs/GitopsOperator.md b/charts/orchestrator-software-templates-infra/docs/GitopsOperator.md new file mode 100644 index 00000000..57e1f9f1 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/docs/GitopsOperator.md @@ -0,0 +1,52 @@ +> **Notice:** +>The openshift-gitops subchart is adapted from the [gitops operator Helm Chart provided in the redhat-gpte-devopsautomation/janus-idp-bootstrap project](https://github.com/redhat-gpte-devopsautomation/janus-idp-bootstrap/tree/main/charts/gitops-operator). Orchestrator-software-templates-infra will act as a wrapper Chart for that unreleased Chart, providing some of its functionality and adapting it for orchestrator needs and applying specific configuration. +>The following Document is the README of the aforementioned chart. + + +# ⚓️ GitOps Operator Helm Deploy + +The GitOps Helm Chart customizes and deploys the [RedHat GitOps Operator](https://github.com/redhat-developer/gitops-operator) written by Red Hat. + +## Installing the chart + +To install the chart from source: +```bash +# within this directory +helm upgrade --install argocd . -f values.yaml -n janus-argocd --create-namespace +``` + +## Configuration + +The [values.yml](values.yaml) file contains instructions for common chart overrides. + +You can install multiple team instances of ArgoCD into different namespaces, just add your namespace to this list. Namespaces will be created first e.g. shown above for a single namespace called `janus-gitops`. + +RBAC for each ArgoCD instance is `cluster-admin` scoped by default. You can create `namespaced` ArgoCD instances by specifying `teamInstancesAreClusterScoped: false`. This setting does not deploy any excess RBAC and uses the defaults from the gitops-operator. + +If you want fine-grained access, you may set `clusterRoleRulesController` and `clusterRoleRulesServer` with Role rules that suit your purpose. + +The default GitOps ArgoCD instance is _not_ deployed in the `openshift-gitops` operator project. You can enable it by setting `disableDefaultArgoCD: false` + +You _do not_ need to override the ArgoCD `applicationInstanceLabelKey`. It is automatically generated based on the namespace name. + +Anything configurable in the Operator is passed to the ArgoCD custom resource provided by the Operator. See `argocd_cr` in `values.yaml` for example defaults. For more detailed overview of what's included, checkout the [ArgoCD Operator Docs](https://argocd-operator.readthedocs.io/en/latest/reference/argocd/). + +If you wish to use ArgoCD to manage this chart directly (or as a helm chart dependency) you may need to make use of the `ignoreHelmHooks` flag to ignore helm lifecycle hooks. + +One example might be deploying team instances without the Operator and helm lifecycle hooks. +```bash +helm instance argocd ./charts/gitops-operator --set operator=null --set ignoreHelmHooks=true +``` + +## Removing + +To delete the chart: +```bash +helm uninstall argocd --namespace janus-gitops +oc delete project janus-argocd + +### If ignoreHelmHooks is set to 'false' you will need to remove the argocd and appproject resources manually +oc delete argocd argocd +oc delete appproject default +``` + diff --git a/charts/orchestrator-software-templates-infra/templates/NOTES.txt b/charts/orchestrator-software-templates-infra/templates/NOTES.txt new file mode 100644 index 00000000..d400519a --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/NOTES.txt @@ -0,0 +1,50 @@ +{{/* Empty line */}} + +Helm Release {{ .Release.Name }} installed in namespace {{ .Release.Namespace }}. +{{- $yes := "YES" }} +{{- $no := "NO " }} +{{- $openshiftPipelinesInstalled := $no }} +{{- $openshiftGitopsInstalled := $no }} +{{- $timeout := "--timeout=5m" }} + +{{- if .Values.openshiftPipelines.enabled }} +{{- $unmanagedSubscriptionExists := include "unmanaged-resource-exists" (list "operators.coreos.com/v1alpha1" "Subscription" "" "openshift-pipelines" .Release.Name .Capabilities.APIVersions ) }} +{{- if eq $unmanagedSubscriptionExists "false" }} +{{- $openshiftPipelinesInstalled = $yes }} +{{- end }} +{{- end }} + + +{{- if .Values.openshiftGitops.enabled }} +{{- $unmanagedSubscriptionExists := include "unmanaged-resource-exists" (list "operators.coreos.com/v1alpha1" "Subscription" "" "openshift-gitops" .Release.Name .Capabilities.APIVersions ) }} +{{- if eq $unmanagedSubscriptionExists "false" }} +{{- $openshiftGitopsInstalled = $yes }} +{{- end }} +{{- end }} + + +Components Installed Namespace +==================================================================== +Openshift Pipelines Operator {{ $openshiftPipelinesInstalled }} {{ .Values.openshiftPipelines.subscription.namespace }} +Openshift Gitops Operator {{ $openshiftGitopsInstalled }} {{ .Values.openshiftGitops.subscription.namespace }} +==================================================================== +{{/* Empty line */}} + +Please wait for the operators to be fully installed before advancing. + + +{{- if .Values.openshiftPipelines.enabled }} + +Run the following command to validate that the openshift-pipelines namespace and all pods within it are operational: + +oc wait --for=jsonpath='{.status.phase}'=Active namespace/openshift-pipelines --timeout=80s && \ +oc wait --for=condition=Ready pod --all -n openshift-pipelines --timeout=120s +{{- end }} + +{{- if .Values.openshiftGitops.enabled }} + +Run the following command to validate that the openshift-gitops namespace and all pods within it are operational: + +oc wait --for=jsonpath='{.status.phase}'=Active namespace/openshift-gitops --timeout=80s && \ +oc wait --for=condition=Ready pod --all -n {{ .Release.Namespace }} --timeout=120s +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/_helpers.tpl b/charts/orchestrator-software-templates-infra/templates/_helpers.tpl new file mode 100644 index 00000000..5db6043e --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/_helpers.tpl @@ -0,0 +1,37 @@ +{{/* Helper functions */}} + +{{- define "unmanaged-resource-exists" -}} + {{- $api := index . 0 -}} + {{- $kind := index . 1 -}} + {{- $namespace := index . 2 -}} + {{- $name := index . 3 -}} + {{- $releaseName := index . 4 -}} + {{- $apiCapabilities := index . 5 -}} + {{- $unmanagedSubscriptionExists := "true" -}} + {{- if $apiCapabilities.Has (printf "%s/%s" $api $kind) }} + {{- $existingOperator := lookup $api $kind $namespace $name -}} + {{- if empty $existingOperator -}} + {{- "false" -}} + {{- else -}} + {{- $isManagedResource := include "is-managed-resource" (list $existingOperator $releaseName) -}} + {{- if eq $isManagedResource "true" -}} + {{- "false" -}} + {{- else -}} + {{- "true" -}} + {{- end -}} + {{- end -}} + {{- else -}} + {{- "false" -}} + {{- end -}} +{{- end -}} + +{{- define "is-managed-resource" -}} + {{- $resource := index . 0 -}} + {{- $releaseName := index . 1 -}} + {{- $resourceReleaseName := dig "metadata" "annotations" (dict "meta.helm.sh/release-name" "NA") $resource -}} + {{- if eq (get $resourceReleaseName "meta.helm.sh/release-name") $releaseName -}} + {{- "true" -}} + {{- else -}} + {{- "false" -}} + {{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml b/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml new file mode 100644 index 00000000..aba0ebc3 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml @@ -0,0 +1,20 @@ +{{- $pipelinesEnabled := .Values.openshiftPipelines.enabled }} +{{- $gitopsEnabled := .Values.openshiftGitops.enabled }} +{{- $shouldCreate := or $pipelinesEnabled $gitopsEnabled }} + + +{{- if $shouldCreate }} +{{- $existing := lookup "operators.coreos.com/v1alpha1" "CatalogSource" "olm" "operatorhubio-catalog" -}} +{{- if not $existing }} +apiVersion: operators.coreos.com/v1alpha1 +kind: CatalogSource +metadata: + name: operatorhubio-catalog + namespace: olm +spec: + sourceType: grpc + image: quay.io/operator-framework/upstream-community-operators:latest + displayName: OperatorHub.io Catalog + publisher: OperatorHub.io +{{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml new file mode 100644 index 00000000..99a7e2f9 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml @@ -0,0 +1,15 @@ +{{- if .Values.openshiftGitops.namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: {{ .Values.openshiftGitops.namespaces | first }}-argocd-application-controller + app.kubernetes.io/part-of: {{ .Values.openshiftGitops.namespaces | first }} + name: {{ .Values.openshiftGitops.namespaces | first }}-argocd-application-controller +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml new file mode 100644 index 00000000..02237012 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.openshiftGitops.namespaces }} +{{- range $ns := .Values.openshiftGitops.namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: application-controller + app.kubernetes.io/name: {{ $ns }}-argocd-application-controller + app.kubernetes.io/part-of: {{ $.Values.openshiftGitops.namespaces | first }} + annotations: + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + name: {{ $ns }}-argocd-application-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $.Values.openshiftGitops.namespaces | first }}-argocd-application-controller +subjects: +- kind: ServiceAccount + name: {{ $.Values.openshiftGitops.name }}-argocd-application-controller + namespace: {{ $ns }} +{{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml new file mode 100644 index 00000000..017c6b16 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.openshiftGitops.namespaces (.Capabilities.APIVersions.Has "argoproj.io/v1beta1/ArgoCD") }} +{{- range $ns := .Values.openshiftGitops.namespaces }} +--- +apiVersion: argoproj.io/v1beta1 +kind: ArgoCD +metadata: + name: {{ $.Values.openshiftGitops.name }} + labels: + app: {{ $.Values.openshiftGitops.name }} + namespace: {{ $ns }} +spec: + applicationInstanceLabelKey: rht-gitops.com/{{ $ns }} + {{- if $.Values.openshiftGitops.argocd_cr }} + {{- $.Values.openshiftGitops.argocd_cr | toYaml | trim | nindent 2 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml new file mode 100644 index 00000000..c4857739 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml @@ -0,0 +1,19 @@ +{{- if .Values.openshiftGitops.repositoryCredentials }} +{{- range $cred := .Values.openshiftGitops.repositoryCredentials }} +{{- range $ns := $.Values.openshiftGitops.namespaces }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $cred.name }} + namespace: {{ $ns }} + labels: + argocd.argoproj.io/secret-type: repo-creds +stringData: + username: {{ $cred.username | quote }} + password: {{ $cred.password | quote }} + type: {{ $cred.type }} + url: {{ $cred.url }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml new file mode 100644 index 00000000..9cc62c65 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml @@ -0,0 +1,25 @@ +{{- if and .Values.openshiftGitops.initialApps (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1/Application") }} +{{- range $app := .Values.openshiftGitops.initialApps }} +{{- range $ns := $.Values.openshiftGitops.namespaces }} +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ $app.name }} + namespace: {{ $ns }} +spec: + destination: + namespace: {{ $ns }} + server: 'https://kubernetes.default.svc' + source: + path: {{ $app.path }} + repoURL: {{ $app.repoURL }} + targetRevision: {{ $app.targetRevision }} + project: default + syncPolicy: + automated: + prune: true + selfHeal: true +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml new file mode 100644 index 00000000..8922f926 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml @@ -0,0 +1,17 @@ +{{- if .Values.openshiftGitops.initialRepositories }} +{{- range $repo := .Values.openshiftGitops.initialRepositories }} +{{- range $ns := $.Values.openshiftGitops.namespaces }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $repo.name }} + namespace: {{ $ns }} + labels: + argocd.argoproj.io/secret-type: repository +stringData: + type: {{ $repo.type }} + url: {{ $repo.url }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml new file mode 100644 index 00000000..9ab2784a --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml @@ -0,0 +1,14 @@ +{{- if .Values.openshiftGitops.namespaces }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: argocd-server + app.kubernetes.io/name: {{ .Values.openshiftGitops.namespaces | first }}-gitops-argocd-server + app.kubernetes.io/part-of: {{ .Values.openshiftGitops.namespaces | first }} + name: {{ .Values.openshiftGitops.namespaces | first }}-gitops-argocd-server +rules: + - apiGroups: [""] + resources: ["pods", "logs"] + verbs: ["get"] +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml new file mode 100644 index 00000000..42df5c14 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml @@ -0,0 +1,21 @@ +{{- if .Values.openshiftGitops.namespaces }} +{{- range $ns := .Values.openshiftGitops.namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: argocd-server + app.kubernetes.io/name: {{ $ns }}-argocd-server + app.kubernetes.io/part-of: {{ $ns }} + name: {{ $ns }}-argocd-server +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ $ns }}-gitops-argocd-server +subjects: +- kind: ServiceAccount + name: {{ $.Values.openshiftGitops.name }}-argocd-server + namespace: {{ $ns }} +{{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml new file mode 100644 index 00000000..6d67f626 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml @@ -0,0 +1,35 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: crd-reader + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +rules: +- apiGroups: + - apiextensions.k8s.io + resources: + - 'customresourcedefinitions' + verbs: + - get + - list +{{- if .Values.openshiftGitops.namespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: crd-reader-binding + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: crd-reader +subjects: +- kind: ServiceAccount + name: default + namespace: {{ .Values.openshiftGitops.namespaces | first }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml new file mode 100644 index 00000000..9c1fd402 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml @@ -0,0 +1,21 @@ +{{- if .Values.openshiftGitops.namespaces }} +{{- range $index, $ns := .Values.openshiftGitops.namespaces }} +{{- if ne $index 0 }} +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + "openshift.io/description": "" + "openshift.io/display-name": "" + "helm.sh/hook": pre-install,pre-upgrade + labels: + kubernetes.io/metadata.name: {{ $ns }} + openshift.io/cluster-monitoring: "true" + name: {{ $ns }} +spec: + finalizers: + - kubernetes +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml new file mode 100644 index 00000000..e8c47cf4 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml @@ -0,0 +1,229 @@ +{{- if and .Values.openshiftGitops.enabled }} + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: csv-cleanup-gitops + namespace: {{ .Values.openshiftGitops.subscription.namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: csv-cleanup-role-gitops + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +rules: + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list", "delete"] + - apiGroups: ["operators.coreos.com"] + resources: ["clusterserviceversions", "subscriptions"] + verbs: ["get", "list", "delete", "watch"] + - apiGroups: ["apps", ""] + resources: ["deployments", "pods"] + verbs: ["get", "list", "delete"] + - apiGroups: [""] + resources: ["pods", "services", "namespaces"] + verbs: ["get", "list", "delete"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterroles", "clusterrolebindings"] + verbs: ["get", "list", "delete"] + - apiGroups: ["argoproj.io"] + resources: ["applications", "appprojects", "argocds", "argocd"] + verbs: ["get", "list", "watch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: csv-cleanup-rolebinding-gitops + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: csv-cleanup-role-gitops +subjects: +- kind: ServiceAccount + name: csv-cleanup-gitops + namespace: {{ .Values.openshiftGitops.subscription.namespace }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: openshift-gitops-cleanup-gitops + namespace: {{ .Values.openshiftGitops.subscription.namespace }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +spec: + template: + spec: + serviceAccountName: csv-cleanup-gitops + restartPolicy: Never + containers: + - name: cleanup + image: {{ .Values.cleanupContainerImage }} + resources: + requests: + memory: {{ .Values.resources.requests.memory }} + cpu: {{ .Values.resources.requests.cpu }} + limits: + memory: {{ .Values.resources.limits.memory }} + cpu: {{ .Values.resources.limits.cpu }} + env: + - name: NAMESPACE + value: {{ .Values.openshiftGitops.subscription.namespace }} + - name: NAME + value: {{ .Values.openshiftGitops.subscription.spec.name }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + command: + - /bin/sh + - -c + - | + set +e # Continue on error + + echo "Starting OpenShift GitOps Cleanup Job..." + + # --- Phase 1: Delete OpenShift GitOps Operator Subscription FIRST --- + echo "Phase 1: Deleting OpenShift GitOps Operator Subscription to stop operator reconciliation..." + + echo "Attempting to delete Subscription $NAME in $NAMESPACE..." + kubectl delete subscription $NAME -n $NAMESPACE --ignore-not-found + echo "Waiting for Subscription to be deleted (timeout 90s)..." + kubectl wait --for=delete subscription/$NAME -n $NAMESPACE --timeout=90s || echo "Subscription deletion timed out or resource not found, continuing..." + + # Give the operator time to stop reconciling + echo "Waiting 10 seconds for operator to stop reconciling..." + sleep 10 + + echo "Phase 1: Subscription deletion complete." + echo "" + + # --- Phase 2: Delete all Applications --- + echo "Phase 2: Deleting all Application resources across all namespaces..." + + # Get unique list of namespaces that contain Applications + namespaces_with_apps=$(kubectl get applications.argoproj.io -A -o custom-columns="NAMESPACE:.metadata.namespace" --no-headers 2>/dev/null | sort -u) + + if [ -z "$namespaces_with_apps" ]; then + echo "No Application resources found. Skipping Application deletion." + else + echo "$namespaces_with_apps" | while read -r ns; do + echo " Processing namespace: $ns for Applications" + # Get all Applications in the current namespace + applications_in_ns=$(kubectl get applications.argoproj.io -n "$ns" -o custom-columns="NAME:.metadata.name" --no-headers 2>/dev/null) + + if [ -z "$applications_in_ns" ]; then + echo " No Applications found in $ns." + else + echo "$applications_in_ns" | while read -r app; do + if [ -n "$app" ]; then + echo " Deleting Application: $app in namespace: $ns" + kubectl delete applications.argoproj.io "$app" -n "$ns" --ignore-not-found + fi + done + fi + done + fi + echo "Phase 2: Application deletion complete." + echo "" + + # --- Phase 3: Delete all AppProjects --- + echo "Phase 3: Deleting all AppProject resources across all namespaces..." + + # Get unique list of namespaces that contain AppProjects + namespaces_with_appprojects=$(kubectl get appprojects.argoproj.io -A -o custom-columns="NAMESPACE:.metadata.namespace" --no-headers 2>/dev/null | sort -u) + + if [ -z "$namespaces_with_appprojects" ]; then + echo "No AppProject resources found. Skipping AppProject deletion." + else + echo "$namespaces_with_appprojects" | while read -r ns; do + echo " Processing namespace: $ns for AppProjects" + # Get all AppProjects in the current namespace + appprojects_in_ns=$(kubectl get appprojects.argoproj.io -n "$ns" -o custom-columns="NAME:.metadata.name" --no-headers 2>/dev/null) + + if [ -z "$appprojects_in_ns" ]; then + echo " No AppProjects found in $ns." + else + echo "$appprojects_in_ns" | while read -r proj; do + if [ -n "$proj" ]; then + echo " Deleting AppProject: $proj in namespace: $ns" + kubectl delete appprojects.argoproj.io "$proj" -n "$ns" --ignore-not-found + fi + done + fi + done + fi + echo "Phase 3: AppProject deletion complete." + echo "" + + # --- Phase 4: Delete all ArgoCD instances --- + echo "Phase 4: Deleting all ArgoCD instances across all namespaces..." + + # Get a list of all ArgoCD instances (namespace and name) once + argo_instances=$(kubectl get argocd -A -o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name" --no-headers 2>/dev/null) + + if [ -z "$argo_instances" ]; then + echo "No ArgoCD instances found. Skipping ArgoCD instance deletion." + else + echo "$argo_instances" | while read -r ns argo_name; do + if [ -n "$ns" ] && [ -n "$argo_name" ]; then + echo " Attempting to delete ArgoCD instance '$argo_name' in namespace: $ns" + # First, try a regular delete. If it gets stuck (due to finalizers), + # we will attempt to patch. + kubectl delete argocd "$argo_name" -n "$ns" --ignore-not-found --timeout=30s + + # Check if the ArgoCD instance is still present (meaning it's stuck with a finalizer) + if kubectl get argocd "$argo_name" -n "$ns" &>/dev/null; then + echo " ArgoCD instance '$argo_name' in namespace '$ns' is stuck. Attempting to remove finalizers." + # Patch the resource to remove finalizers + kubectl patch argocd "$argo_name" -n "$ns" --type=json -p='[{"op": "remove", "path": "/metadata/finalizers"}]' || \ + echo " Warning: Failed to patch finalizers for $argo_name in $ns. It might remain." + echo " Re-attempting delete for ArgoCD instance '$argo_name' in namespace: $ns after finalizer removal." + kubectl delete argocd "$argo_name" -n "$ns" --ignore-not-found --timeout=30s + fi + fi + done + fi + echo "Phase 4: ArgoCD instance deletion complete." + echo "" + + # --- Phase 5: Delete ClusterServiceVersion (CSV) --- + echo "Phase 5: Deleting ClusterServiceVersion (CSV) for $NAME in $NAMESPACE..." + + # Find CSVs that match the operator name pattern + csv_names=$(kubectl get csv -n $NAMESPACE -o name | grep -E "(gitops|argocd)" 2>/dev/null || echo "") + + if [ -z "$csv_names" ]; then + echo "No GitOps/ArgoCD CSVs found in $NAMESPACE." + else + echo "$csv_names" | while read -r csv_name; do + if [ -n "$csv_name" ]; then + echo " Deleting CSV: $csv_name" + kubectl delete "$csv_name" -n $NAMESPACE --ignore-not-found + fi + done + fi + + echo "Waiting for CSV to be deleted (timeout 120s)..." + kubectl wait --for=delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --timeout=120s 2>/dev/null || echo "CSV deletion wait timed out or resource not found, continuing..." + + echo "Phase 5: CSV deletion complete." + echo "" + + echo "OpenShift GitOps cleanup job finished successfully." + +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml new file mode 100644 index 00000000..ac8369b7 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml @@ -0,0 +1,26 @@ +{{- if .Values.openshiftGitops.secrets }} +{{- range $secret := .Values.openshiftGitops.secrets }} +{{- range $ns := $.Values.openshiftGitops.namespaces }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secret.name }} + namespace: {{ $ns }} + labels: + app.kubernetes.io/name: argocd-secret + app.kubernetes.io/part-of: argocd + {{- if eq $secret.type "repo-creds" }} + argocd.argoproj.io/secret-type: repo-creds + {{- end }} +type: kubernetes.io/basic-auth +stringData: + username: {{ $secret.username | quote }} + password: {{ $secret.password | quote }} + {{- if eq $secret.type "repo-creds" }} + type: git + url: https://github.com/demo-setup-test + {{- end }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml new file mode 100644 index 00000000..d3c0bd34 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml @@ -0,0 +1,18 @@ +{{- if .Values.openshiftGitops.enabled }} +--- +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ .Values.openshiftGitops.subscription.spec.name }} + namespace: {{ .Values.openshiftGitops.subscription.namespace }} +spec: + channel: {{ .Values.openshiftGitops.subscription.spec.channel }} + installPlanApproval: {{ .Values.openshiftGitops.subscription.installPlanApproval }} + name: {{ .Values.openshiftGitops.subscription.spec.name }} + source: {{ .Values.openshiftGitops.subscription.spec.source | default "redhat-operators" | quote }} + sourceNamespace: {{ .Values.openshiftGitops.subscription.spec.sourceNamespace | default "openshift-marketplace" | quote }} + config: + env: + - name: DISABLE_DEFAULT_ARGOCD_INSTANCE + value: {{ .Values.openshiftGitops.subscription.disableDefaultArgoCD | quote }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/wait-for-crd.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/wait-for-crd.yaml new file mode 100644 index 00000000..3fe12670 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/wait-for-crd.yaml @@ -0,0 +1,89 @@ +{{- if .Values.openshiftGitops.enabled }} +{{- if .Values.openshiftGitops.namespaces }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cluster-check-sa + namespace: {{ .Values.openshiftGitops.namespaces | first }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-2" + "helm.sh/hook-delete-policy": before-hook-creation +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cluster-check-role + namespace: {{ .Values.openshiftGitops.namespaces | first }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-2" + "helm.sh/hook-delete-policy": before-hook-creation +rules: + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cluster-check-rolebinding + namespace: {{ .Values.openshiftGitops.namespaces | first }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-2" + "helm.sh/hook-delete-policy": before-hook-creation +subjects: + - kind: ServiceAccount + name: cluster-check-sa + namespace: {{ .Values.openshiftGitops.namespaces | first }} +roleRef: + kind: ClusterRole + name: cluster-check-role + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Pod +metadata: + name: cluster-check + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "-1" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + namespace: {{ .Values.openshiftGitops.namespaces | first }} +spec: + containers: + - name: crd-check + image: {{ .Values.cleanupContainerImage }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 1001 + seccompProfile: + type: RuntimeDefault + resources: + requests: + memory: {{ .Values.resources.requests.memory }} + cpu: {{ .Values.resources.requests.cpu }} + limits: + memory: {{ .Values.resources.limits.memory }} + cpu: {{ .Values.resources.limits.cpu }} + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + while true; do + kubectl get crd argocds.argoproj.io > /dev/null 2>&1 && echo "ArgoCD CRD ready" && break + echo "Waiting for argocds.argoproj.io CRD..." + sleep 5 + done + restartPolicy: Never + terminationGracePeriodSeconds: 0 + serviceAccountName: cluster-check-sa +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/post-cleanup.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/post-cleanup.yaml new file mode 100644 index 00000000..a352ea4d --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/post-cleanup.yaml @@ -0,0 +1,108 @@ +{{- if .Values.openshiftPipelines.enabled }} + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: csv-cleanup-pipelines + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: csv-cleanup-role-pipelines + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +rules: + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list", "delete"] + - apiGroups: ["operators.coreos.com"] + resources: ["clusterserviceversions", "subscriptions"] + verbs: ["get", "list", "delete", "watch"] + - apiGroups: ["apps", ""] + resources: ["deployments", "pods"] + verbs: ["get", "list", "delete"] + - apiGroups: [""] + resources: ["pods", "services", "namespaces"] + verbs: ["get", "list"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterroles", "clusterrolebindings"] + verbs: ["get", "list", "delete"] + - apiGroups: ["tekton.dev"] + resources: ["pipelines", "pipelineruns", "tasks", "taskruns", "pipelineresources"] + verbs: ["get", "list", "delete", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: csv-cleanup-rolebinding-pipelines + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-delete + "helm.sh/hook-weight": "10" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: csv-cleanup-role-pipelines +subjects: +- kind: ServiceAccount + name: csv-cleanup-pipelines + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: openshift-pipelines-cleanup + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} + annotations: + "helm.sh/hook": post-delete + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed +spec: + template: + spec: + serviceAccountName: csv-cleanup-pipelines + restartPolicy: Never + containers: + - name: cleanup-pipelines + image: {{ .Values.cleanupContainerImage }} + env: + - name: NAMESPACE + value: {{ .Values.openshiftPipelines.subscription.namespace }} + - name: NAME + value: {{ .Values.openshiftPipelines.subscription.name }} + resources: + requests: + memory: {{ .Values.resources.requests.memory }} + cpu: {{ .Values.resources.requests.cpu }} + limits: + memory: {{ .Values.resources.limits.memory }} + cpu: {{ .Values.resources.limits.cpu }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + command: + - /bin/sh + - -c + - | + set +e + + echo "Waiting for Subscription to be deleted..." + kubectl delete subscription $NAME -n $NAMESPACE --ignore-not-found + kubectl wait --for=delete subscription/$NAME -n $NAMESPACE --timeout=60s + + echo "Waiting for CSV to be deleted..." + kubectl delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --ignore-not-found + kubectl wait --for=delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --timeout=120s + + echo "Cleanup finished." + +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml new file mode 100644 index 00000000..9f72e838 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml @@ -0,0 +1,15 @@ +{{- $unmanagedSubscriptionExists := include "unmanaged-resource-exists" (list "operators.coreos.com/v1alpha1" "Subscription" .Values.openshiftPipelines.subscription.namespace .Values.openshiftPipelines.subscription.spec.name .Release.Name .Capabilities.APIVersions ) }} +{{- if and (eq $unmanagedSubscriptionExists "false") .Values.openshiftPipelines.enabled }} + +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: {{ .Values.openshiftPipelines.subscription.name }} + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} +spec: + channel: {{ .Values.openshiftPipelines.subscription.spec.channel }} + installPlanApproval: {{ .Values.openshiftPipelines.subscription.spec.installPlanApproval }} + name: {{ .Values.openshiftPipelines.subscription.spec.name }} + source: {{ .Values.openshiftPipelines.subscription.spec.source | default "redhat-operators" }} + sourceNamespace: {{ .Values.openshiftPipelines.subscription.spec.sourceNamespace | default "openshift-marketplace" }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/tests/infra-test.yaml b/charts/orchestrator-software-templates-infra/templates/tests/infra-test.yaml new file mode 100644 index 00000000..33d93239 --- /dev/null +++ b/charts/orchestrator-software-templates-infra/templates/tests/infra-test.yaml @@ -0,0 +1,91 @@ +{{- if .Values.test.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Release.Name }}-service-account + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded,hook-failed +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-role + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded,hook-failed +rules: + - apiGroups: ["operators.coreos.com"] + resources: ["subscriptions"] + verbs: ["list", "get"] + - apiGroups: ["operators.coreos.com"] + resources: ["clusterserviceversions", "csv"] + verbs: ["list", "get", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Release.Name }}-role-binding + namespace: {{ .Values.openshiftPipelines.subscription.namespace }} + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded,hook-failed +subjects: + - kind: ServiceAccount + name: {{ .Release.Name }}-service-account + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ .Release.Name }}-role + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Pod +metadata: + name: {{ .Release.Name }}-testing + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded,hook-failed +spec: + serviceAccountName: {{ .Release.Name }}-service-account + containers: + - name: oc-client + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + runAsNonRoot: false + resources: + requests: + cpu: 10m + memory: 128Mi + limits: + cpu: 100m + memory: 256Mi + image: "{{ .Values.test.image.registry }}/{{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}" + command: ["/bin/sh", "-c"] + args: + - | + set -e + + echo "Starting Test" + + {{- if .Values.openshiftPipelines.enabled }} + kubectl get subscription.operators.coreos.com {{ .Values.openshiftPipelines.subscription.name }} -n {{ .Values.openshiftPipelines.subscription.namespace }} || exit 1 + {{- end }} + + {{- if .Values.openshiftGitops.enabled }} + echo "Checking OpenShift GitOps subscription..." + kubectl get subscription.operators.coreos.com {{ .Values.openshiftGitops.subscription.spec.name }} -n {{ .Values.openshiftGitops.subscription.namespace }} || exit 1 + {{- end }} + + echo "Test passed!" + exit 0 + restartPolicy: Never +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates-infra/values.yaml b/charts/orchestrator-software-templates-infra/values.yaml new file mode 100644 index 00000000..0e9b651a --- /dev/null +++ b/charts/orchestrator-software-templates-infra/values.yaml @@ -0,0 +1,163 @@ +openshiftPipelines: + # -- whether the operator should be deployed by the chart + enabled: true + subscription: + # -- namespace where the operator should be deployed + namespace: openshift-operators + # -- name of the operator package + name: openshift-pipelines-operator-rh + spec: + # -- channel of an operator package to subscribe to + channel: latest + # -- whether the update should be installed automatically + installPlanApproval: Automatic + # -- name of the operator package + name: openshift-pipelines-operator-rh + # -- name of the catalog source + source: redhat-operators + sourceNamespace: openshift-marketplace + +openshiftGitops: + # -- whether the operator should be deployed by the chart + enabled: true + + # -- name of instances + name: argocd + + # -- namespace of rhdh instance, will be used to install openshift-gitops. + namespaces: + - rhdh + + # -- subscription config + subscription: + namespace: openshift-operators + # -- namespace where the operator should be deployed + spec: + # -- channel of an operator package to subscribe to + channel: latest + # -- whether the update should be installed automatically + installPlanApproval: Automatic + # -- name of the operator package + name: openshift-gitops-operator + # -- name of the catalog source + source: redhat-operators + sourceNamespace: openshift-marketplace + disableDefaultArgoCD: true + argocd_cr: + controller: + resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 250m + memory: 1024Mi + ha: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + enabled: false + rbac: + defaultPolicy: "" + policy: | + g, system:cluster-admins, role:admin + scopes: "[groups]" + redis: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + repo: + resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 250m + memory: 256Mi + server: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 125m + memory: 128Mi + route: + enabled: true + sso: + dex: + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 250m + memory: 128Mi + openShiftOAuth: true + provider: dex + # -- Initial repositories configuration + initialRepositories: + [] + # initialRepositories: + # - name: redhat-cop + # type: helm + # url: https://redhat-cop.github.io/helm-charts + + # -- Repository credential templates + repositoryCredentials: [] + # repositoryCredentials: + # - name: private-repo-creds + # url: '' + # type: git + # password: password + # username: username + + # -- Initial applications to deploy + initialApps: [] + # initialApps: + # - name: init-app + # path: argocd/ + # repoURL: '' + # targetRevision: HEAD + + # -- Secrets for Git access or other repository credentials + secrets: [] + # EXAMPLE ... + # secrets: + # - name: git-auth + # username: 'user' + # password: 'pass1234' + # sshPrivateKey: '' + + +test: + # -- Whether to enable the pod used for testing the Release using `helm test`. + enabled: true + + image: + # -- Test infra-test Tekton Task pod image registry + registry: bitnami + + # -- Test infra-test Tekton Task pod image repository. + repository: kubectl + + # -- Test infra-test Tekton Task pod image tag. + tag: latest + +# -- A container image and tag, will be used for the post-cleanup tasks. +cleanupContainerImage: bitnami/kubectl:latest +resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" From 690c5db69fba1b33c2cb1773a0ff5650526b7351 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:46:42 +0000 Subject: [PATCH 056/117] chore(deps): update helm release backstage to v2.6.0 (#184) * chore(deps): update helm release backstage to v2.6.0 * chore(pre-commit): Auto-fix hooks Co-authored-by: renovate[bot] * Bump version to 4.4.0 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 ++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 34 ++++++++++++++++++++++++++++- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index 10ce5bc1..c484002d 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.31.3 - name: backstage repository: https://backstage.github.io/charts - version: 2.5.3 -digest: sha256:551409ba984e6b0d3fef38eea2d571b1d033bea3ff461fabfd803d06b3347341 -generated: "2025-06-24T12:40:14.794985613Z" + version: 2.6.0 +digest: sha256:a37bf409d78606c8fcb1c6eceea39d317a83f0f70f01544d0108fe612704266c +generated: "2025-07-10T00:11:21.86409305Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index cece1e75..41449657 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -25,7 +25,7 @@ dependencies: version: "2.31.3" - name: backstage repository: https://backstage.github.io/charts - version: "2.5.3" + version: "2.6.0" alias: upstream home: https://red.ht/rhdh icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjE5MS44OCIKICAgaGVpZ2h0PSIxOTEuODgiCiAgIHZpZXdCb3g9IjAgMCAxOTEuODggMTkxLjg4IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgiIC8+CiAgPGcKICAgICBpZD0idXVpZC03OTAxZjg3OC1jZTAwLTQ0MWYtYWMyNi1kZGQzNjU0ZDRmNzkiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxyZWN0CiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIHdpZHRoPSIzNiIKICAgICAgIGhlaWdodD0iMzYiCiAgICAgICByeD0iOSIKICAgICAgIHJ5PSI5IgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InJlY3QyIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjgsMi4yNSBjIDQuMjczMzYsMCA3Ljc1LDMuNDc2NjQgNy43NSw3Ljc1IHYgMTggYyAwLDQuMjczMzYgLTMuNDc2NjQsNy43NSAtNy43NSw3Ljc1IEggMTAgQyA1LjcyNjY0LDM1Ljc1IDIuMjUsMzIuMjczMzYgMi4yNSwyOCBWIDEwIEMgMi4yNSw1LjcyNjY0IDUuNzI2NjQsMi4yNSAxMCwyLjI1IEggMjggTSAyOCwxIEggMTAgQyA1LjAyOTQ0LDEgMSw1LjAyOTQzIDEsMTAgdiAxOCBjIDAsNC45NzA1NyA0LjAyOTQ0LDkgOSw5IGggMTggYyA0Ljk3MDU2LDAgOSwtNC4wMjk0MyA5LC05IFYgMTAgQyAzNyw1LjAyOTQzIDMyLjk3MDU2LDEgMjgsMSBaIgogICAgICAgZmlsbD0iIzRkNGQ0ZCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNCIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0idXVpZC1jM2NhNjg5MS02ZTE4LTQyY2ItODUyYi0zZGVkZDZjMzFlNjgiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI2LjQ0MjM4LDI1LjU1ODExIC0zLjc3Mzc0LC0zLjc3Mzc0IGMgMC41OTE0MywtMC43NzcwNCAwLjk1NjM2LC0xLjczNDggMC45NTYzNiwtMi43ODQzNiAwLC0yLjU1MDI5IC0yLjA3NTIsLTQuNjI1IC00LjYyNSwtNC42MjUgLTIuNTUwMjksMCAtNC42MjUsMi4wNzQ3MSAtNC42MjUsNC42MjUgMCwyLjU1MDI5IDIuMDc0NzEsNC42MjUgNC42MjUsNC42MjUgMS4wNDk0NCwwIDIuMDA3MjYsLTAuMzY0OTMgMi43ODQzNiwtMC45NTYzNiBsIDMuNzczMjUsMy43NzMyNSBjIDAuMTIyMDcsMC4xMjIwNyAwLjI4MjIzLDAuMTgzMTEgMC40NDIzOCwwLjE4MzExIDAuMTYwMTUsMCAwLjMyMDMxLC0wLjA2MTA0IDAuNDQyMzgsLTAuMTgzMTEgMC4yNDMxNiwtMC4yNDQxNCAwLjI0MzE2LC0wLjYzOTY1IDAsLTAuODgzNzkgeiBNIDE1LjYyNSwxOSBjIDAsLTEuODYwODQgMS41MTQxNiwtMy4zNzUgMy4zNzUsLTMuMzc1IDEuODYxMzMsMCAzLjM3NSwxLjUxNDE2IDMuMzc1LDMuMzc1IDAsMS44NjA4NCAtMS41MTM2NywzLjM3NSAtMy4zNzUsMy4zNzUgLTEuODYwODQsMCAtMy4zNzUsLTEuNTE0MTYgLTMuMzc1LC0zLjM3NSB6IgogICAgICAgZmlsbD0iI2VlMDAwMCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNyIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI3LDEzLjYyNSBjIDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMS40NDcyNyAtMS4xNzc3MywtMi42MjUgLTIuNjI1LC0yLjYyNSAtMS40NDcyNywwIC0yLjYyNSwxLjE3NzczIC0yLjYyNSwyLjYyNSAwLDAuNDk2NyAwLjE0NjYxLDAuOTU2NTQgMC4zODcyNywxLjM1MzAzIGwgLTEuMjA0NjUsMS4yMDUwOCBjIC0wLjI0NDE0LDAuMjQ0MTQgLTAuMjQzMTYsMC42Mzk2NSA5LjhlLTQsMC44ODM3OSAwLjEyMTA5LDAuMTIyMDcgMC4yODEyNSwwLjE4MzExIDAuNDQxNDEsMC4xODMxMSAwLjE2MDE2LDAgMC4zMjAzMSwtMC4wNjEwNCAwLjQ0MjM4LC0wLjE4MzExIGwgMS4yMDQxLC0xLjIwNDQ3IGMgMC4zOTY2MSwwLjI0MDkxIDAuODU2NjMsMC4zODc1NyAxLjM1MzUyLDAuMzg3NTcgeiBtIDAsLTQgYyAwLjc1NzgxLDAgMS4zNzUsMC42MTY3IDEuMzc1LDEuMzc1IDAsMC43NTgzIC0wLjYxNzE5LDEuMzc1IC0xLjM3NSwxLjM3NSAtMC4zNzgxMSwwIC0wLjcyMTA3LC0wLjE1MzY5IC0wLjk2OTk3LC0wLjQwMTczIC03LjNlLTQsLTcuM2UtNCAtOS44ZS00LC0wLjAwMTggLTAuMDAxNywtMC4wMDI2IC02LjFlLTQsLTYuMWUtNCAtMC4wMDE1LC03LjllLTQgLTAuMDAyMSwtMC4wMDE0IC0wLjI0NzYyLC0wLjI0ODc4IC0wLjQwMTE4LC0wLjU5MTM3IC0wLjQwMTE4LC0wLjk2OTMgMCwtMC43NTgzIDAuNjE3MTksLTEuMzc1IDEuMzc1LC0xLjM3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5LDguMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDExIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTksMjUuMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDEzIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjcuNSwxNi44NzUgYyAtMS4xNzE4OCwwIC0yLjEyNSwwLjk1MzEyIC0yLjEyNSwyLjEyNSAwLDEuMTcxODggMC45NTMxMiwyLjEyNSAyLjEyNSwyLjEyNSAxLjE3MTg4LDAgMi4xMjUsLTAuOTUzMTIgMi4xMjUsLTIuMTI1IDAsLTEuMTcxODggLTAuOTUzMTIsLTIuMTI1IC0yLjEyNSwtMi4xMjUgeiBtIDAsMyBjIC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgMCwtMC40ODI0MiAwLjM5MjU4LC0wLjg3NSAwLjg3NSwtMC44NzUgMC40ODI0MiwwIDAuODc1LDAuMzkyNTggMC44NzUsMC44NzUgMCwwLjQ4MjQyIC0wLjM5MjU4LDAuODc1IC0wLjg3NSwwLjg3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoMTUiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMi42MjUsMTkgYyAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IC0xLjE3MTg4LDAgLTIuMTI1LDAuOTUzMTIgLTIuMTI1LDIuMTI1IDAsMS4xNzE4OCAwLjk1MzEyLDIuMTI1IDIuMTI1LDIuMTI1IDEuMTcxODgsMCAyLjEyNSwtMC45NTMxMiAyLjEyNSwtMi4xMjUgeiBtIC0zLDAgYyAwLC0wLjQ4MjQyIDAuMzkyNTgsLTAuODc1IDAuODc1LC0wLjg3NSAwLjQ4MjQyLDAgMC44NzUsMC4zOTI1OCAwLjg3NSwwLjg3NSAwLDAuNDgyNDIgLTAuMzkyNTgsMC44NzUgLTAuODc1LDAuODc1IC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDE3IiAvPgogICAgPHBhdGgKICAgICAgIGQ9Ik0gMTMuMjM3NDMsMTIuMzUzNjQgQyAxMy40NzgzNCwxMS45NTcwMyAxMy42MjUsMTEuNDk2ODkgMTMuNjI1LDExIDEzLjYyNSw5LjU1MjczIDEyLjQ0NzI3LDguMzc1IDExLDguMzc1IDkuNTUyNzMsOC4zNzUgOC4zNzUsOS41NTI3MyA4LjM3NSwxMSBjIDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDAuNDk2ODksMCAwLjk1NzAzLC0wLjE0NjY3IDEuMzUzNjQsLTAuMzg3NTcgbCAxLjIwNDQ3LDEuMjA0NDcgYyAwLjEyMjA3LDAuMTIyMDcgMC4yODE3NCwwLjE4MzExIDAuNDQxODksMC4xODMxMSAwLjE2MDE1LDAgMC4zMTk4MiwtMC4wNjEwNCAwLjQ0MTg5LC0wLjE4MzExIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IEwgMTMuMjM3NDIsMTIuMzUzNjQgWiBNIDkuNjI1LDExIGMgMCwtMC43NTgzIDAuNjE2NywtMS4zNzUgMS4zNzUsLTEuMzc1IDAuNzU4MywwIDEuMzc1LDAuNjE2NyAxLjM3NSwxLjM3NSAwLDAuMzc3OTkgLTAuMTUzNSwwLjcyMDU4IC0wLjQwMTEyLDAuOTY5MzYgLTcuOWUtNCw3LjllLTQgLTAuMDAxOSwxMGUtNCAtMC4wMDI3LDAuMDAxOCAtOGUtNCw3LjllLTQgLTAuMDAxLDAuMDAxOSAtMC4wMDE4LDAuMDAyNyBDIDExLjcyMDU4LDEyLjIyMTUgMTEuMzc3OTksMTIuMzc1IDExLDEyLjM3NSAxMC4yNDE3LDEyLjM3NSA5LjYyNSwxMS43NTgzIDkuNjI1LDExIFoiCiAgICAgICBmaWxsPSIjZmZmZmZmIgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InBhdGgxOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDEzLjU1ODExLDIzLjU1ODExIC0xLjIwNDQ3LDEuMjA0NDcgQyAxMS45NTcwMywyNC41MjE2NyAxMS40OTY4OSwyNC4zNzUwMSAxMSwyNC4zNzUwMSBjIC0xLjQ0NzI3LDAgLTIuNjI1LDEuMTc3NzMgLTIuNjI1LDIuNjI1IDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMC40OTY4OSAtMC4xNDY2NywtMC45NTcwMyAtMC4zODc1NywtMS4zNTM2NCBMIDE0LjQ0MTksMjQuNDQxOSBjIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IC0wLjI0NDE0LC0wLjI0NDE0IC0wLjYzOTY1LC0wLjI0NDE0IC0wLjg4Mzc5LDAgeiBNIDExLDI4LjM3NSBjIC0wLjc1ODMsMCAtMS4zNzUsLTAuNjE2NyAtMS4zNzUsLTEuMzc1IDAsLTAuNzU4MyAwLjYxNjcsLTEuMzc1IDEuMzc1LC0xLjM3NSAwLjM3ODg1LDAgMC43MjIyOSwwLjE1Mzk5IDAuOTcxMTksMC40MDI1OSAyLjRlLTQsMi40ZS00IDIuNGUtNCw0LjllLTQgNC45ZS00LDcuM2UtNCAyLjVlLTQsMi40ZS00IDQuOWUtNCwyLjRlLTQgNy4zZS00LDQuOWUtNCAwLjI0ODYsMC4yNDg5IDAuNDAyNTksMC41OTIzNSAwLjQwMjU5LDAuOTcxMTkgMCwwLjc1ODMgLTAuNjE2NywxLjM3NSAtMS4zNzUsMS4zNzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDIxIiAvPgogIDwvZz4KPC9zdmc+Cg== @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.3.0 +version: 4.4.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 1a2d31a6..c8c47096 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.3.0](https://img.shields.io/badge/Version-4.3.0-informational?style=flat-square) +![Version: 4.4.0](https://img.shields.io/badge/Version-4.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -160,7 +160,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.5.3 | +| https://backstage.github.io/charts | upstream(backstage) | 2.6.0 | | https://charts.bitnami.com/bitnami | common | 2.31.3 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index a47a6998..1118d889 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -1155,6 +1155,38 @@ "title": "Backstage container command arguments", "type": "array" }, + "autoscaling": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "description": "Enable autoscaling", + "title": "Backstage Autoscaling", + "type": "boolean" + }, + "maxReplicas": { + "default": 100, + "title": "Maximum number of Backstage pod replicas that the autoscaler is allowed to scale up to", + "type": "integer" + }, + "minReplicas": { + "default": 1, + "title": "Minimum number of Backstage pod replicas that the autoscaler is allowed to scale down to", + "type": "integer" + }, + "targetCPUUtilizationPercentage": { + "default": 80, + "title": "Percentage of CPU that each Backstage pod should be using on average before the autoscaler decides to scale", + "type": "integer" + }, + "targetMemoryUtilizationPercentage": { + "title": "Percentage of memory that each Backstage pod should be using on average before the autoscaler decides to scale", + "type": "integer" + } + }, + "title": "Autoscaling parameters", + "type": "object" + }, "command": { "default": [], "items": { @@ -3688,7 +3720,7 @@ "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", "properties": { "endpoints": { - "description": "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + "description": "endpoints is the endpoint name that details Glusterfs topology.", "type": "string" }, "path": { From d4056d0a08535e1c3efdbe99b2a975b93834bbde Mon Sep 17 00:00:00 2001 From: Leanne Ahern <127859485+OpinionatedHeron@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:56:27 +0100 Subject: [PATCH 057/117] Adding scripts-checks file in workflows (#187) --- .github/workflows/scripts-checks.yaml | 43 +++++++++++++++++++++++++++ .rhdh/scripts/install.sh | 4 +-- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/scripts-checks.yaml diff --git a/.github/workflows/scripts-checks.yaml b/.github/workflows/scripts-checks.yaml new file mode 100644 index 00000000..8d0a5b05 --- /dev/null +++ b/.github/workflows/scripts-checks.yaml @@ -0,0 +1,43 @@ +name: Differential ShellCheck + +on: + push: + paths: + - '**.sh' + - '.github/workflows/scripts-checks.yaml' + branches: [ main ] + pull_request: + paths: + - '**.sh' + - '.github/workflows/scripts-checks.yaml' + branches: [ 'main' ] + +permissions: + contents: read + +jobs: + shellcheck-lint: + runs-on: ubuntu-latest + + permissions: + security-events: write + + steps: + - name: Repository checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + # Differential ShellCheck requires full git history + fetch-depth: 0 + + - id: ShellCheck + name: Differential ShellCheck + uses: redhat-plumbers-in-action/differential-shellcheck@dd551ce780d8af741f8cd8bab6982667b906b457 # v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - if: always() + name: Upload artifact with ShellCheck defects in SARIF format + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: Differential ShellCheck SARIF + path: ${{ steps.ShellCheck.outputs.sarif }} \ No newline at end of file diff --git a/.rhdh/scripts/install.sh b/.rhdh/scripts/install.sh index 0848be70..84665f5d 100755 --- a/.rhdh/scripts/install.sh +++ b/.rhdh/scripts/install.sh @@ -52,7 +52,7 @@ if ! helm show chart $CHART_URL --version "$CV" &> /dev/null; then echo "Using ${CHART_URL} to install Helm chart" -# choose namespace for the install (or create if non-existant) +# choose namespace for the install (or create if non-existent) oc new-project "$namespace" || oc project "$namespace" # generate repo.yaml and index.yaml so we don't have to publish a new file every time @@ -68,7 +68,7 @@ if [[ "$CV" == *"-CI" ]] || [[ $chartrepo -eq 1 ]]; then fi oc -n "$namespace" create configmap helm-repo-files \ --from-file=/tmp/"$CV"-unpacked/index.yaml \ - --from-file=/tmp/"$CV"-unpacked/chart-${CV}.tgz + --from-file="/tmp/$CV-unpacked/chart-${CV}.tgz" cat < helm-repo.yaml apiVersion: apps/v1 kind: Deployment From 970d1f2457735b4bae6e01554da7a35ff3bf70cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 09:23:07 +0200 Subject: [PATCH 058/117] chore(deps): update redhat-plumbers-in-action/differential-shellcheck digest to 0d9e5b2 (#189) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scripts-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts-checks.yaml b/.github/workflows/scripts-checks.yaml index 8d0a5b05..6b0c546d 100644 --- a/.github/workflows/scripts-checks.yaml +++ b/.github/workflows/scripts-checks.yaml @@ -31,7 +31,7 @@ jobs: - id: ShellCheck name: Differential ShellCheck - uses: redhat-plumbers-in-action/differential-shellcheck@dd551ce780d8af741f8cd8bab6982667b906b457 # v5 + uses: redhat-plumbers-in-action/differential-shellcheck@0d9e5b29625f871e6a4215380486d6f1a7cb6cdd # v5 with: token: ${{ secrets.GITHUB_TOKEN }} From faf8c34c2f9a78d5469cb75a4d4a65ce6ad6b4b4 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 17 Jul 2025 14:55:36 +0100 Subject: [PATCH 059/117] Fix: Avoid Helm Conflict When User Creates Custom .npmrc Secret (#186) * Fix: Avoid Helm Conflict When User Creates Custom .npmrc Secret Signed-off-by: Fortune-Ndlovu * bump chart Signed-off-by: Fortune-Ndlovu * Updated to always render the .npmrc Secret when orchestrator.enabled=true Signed-off-by: Fortune-Ndlovu * Updated extra Signed-off-by: Fortune-Ndlovu * Updated extra Signed-off-by: Fortune-Ndlovu * remove helm secret block Signed-off-by: Fortune-Ndlovu * revert Signed-off-by: Fortune-Ndlovu * Telling Helms configmap and initcontainer to fetch the plugin from the direct http tarball url instead of npm registry. Signed-off-by: Fortune-Ndlovu * fix schema Signed-off-by: Fortune-Ndlovu * update _partials.tpl which is a template for orchestrator.plugins that injects the orchestrator plugiins dynamicallly wheneerv orchestrator.enabled=true Signed-off-by: Fortune-Ndlovu * update we no longer need a .npmrc to map the @redhat scope to Red Hat registry. Signed-off-by: Fortune-Ndlovu * fix invalid package name Signed-off-by: Fortune-Ndlovu * update all Signed-off-by: Fortune-Ndlovu * update package name Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/_partials.tpl | 16 ++++++++-------- .../templates/dynamic-plugins-configmap.yaml | 13 ------------- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 41449657..04f6eaaf 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.4.0 +version: 4.4.1 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index c8c47096..63ac12bf 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.4.0](https://img.shields.io/badge/Version-4.4.0-informational?style=flat-square) +![Version: 4.4.1](https://img.shields.io/badge/Version-4.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/_partials.tpl b/charts/backstage/templates/_partials.tpl index dc3d91e0..c03526e2 100644 --- a/charts/backstage/templates/_partials.tpl +++ b/charts/backstage/templates/_partials.tpl @@ -2,14 +2,14 @@ {{- $config := include "orchestrator.plugins.config" . | fromYaml }} plugins: - disabled: false - package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestratorBackend.package }}" + package: "{{ $config.orchestratorPlugins.orchestratorBackend.package }}" integrity: "{{ $config.orchestratorPlugins.orchestratorBackend.integrity }}" pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - disabled: false - package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestrator.package }}" + package: "{{ $config.orchestratorPlugins.orchestrator.package }}" integrity: "{{ $config.orchestratorPlugins.orchestrator.integrity }}" pluginConfig: dynamicPlugins: @@ -25,14 +25,14 @@ plugins: text: Orchestrator path: /orchestrator - disabled: false - package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.package }}" + package: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.package }}" integrity: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.integrity }}" pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - disabled: false - package: "{{ $config.orchestratorPlugins.scope }}/{{ $config.orchestratorPlugins.orchestratorFormWidgets.package }}" + package: "{{ $config.orchestratorPlugins.orchestratorFormWidgets.package }}" integrity: "{{ $config.orchestratorPlugins.orchestratorFormWidgets.integrity }}" pluginConfig: dynamicPlugins: @@ -43,15 +43,15 @@ plugins: orchestratorPlugins: scope: "@redhat" orchestrator: - package: "backstage-plugin-orchestrator@1.6.0" + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz" integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== orchestratorBackend: - package: "backstage-plugin-orchestrator-backend-dynamic@1.6.0" + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz" integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== scaffolderBackendOrchestrator: - package: "backstage-plugin-scaffolder-backend-module-orchestrator-dynamic@1.6.0" + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz" integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== orchestratorFormWidgets: - package: "backstage-plugin-orchestrator-form-widgets@1.6.0" + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz" integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== {{- end }} \ No newline at end of file diff --git a/charts/backstage/templates/dynamic-plugins-configmap.yaml b/charts/backstage/templates/dynamic-plugins-configmap.yaml index a8af0614..27547a95 100644 --- a/charts/backstage/templates/dynamic-plugins-configmap.yaml +++ b/charts/backstage/templates/dynamic-plugins-configmap.yaml @@ -21,16 +21,3 @@ data: {{- $_ := set $dynamic "plugins" $plugins }} {{- include "common.tplvalues.render" (dict "value" $dynamic "context" $) | nindent 4 }} - ---- -{{- if .Values.orchestrator.enabled }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Release.Name}}-dynamic-plugins-npmrc - namespace: {{ .Release.Namespace }} -type: Opaque -stringData: - .npmrc: | - @redhat:registry=https://npm.registry.redhat.com -{{- end }} \ No newline at end of file From 2f8b7d36a2dfc3a63d6139064bfa784656373655 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 17 Jul 2025 17:04:18 +0200 Subject: [PATCH 060/117] chore: Bump the charts minor versions to leave room for patch updates in the release-1.7 branch (#191) --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 2 +- charts/orchestrator-software-templates-infra/Chart.yaml | 2 +- charts/orchestrator-software-templates-infra/README.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 04f6eaaf..4bbcd6b6 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.4.1 +version: 4.5.0 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 63ac12bf..5cbf51fa 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.4.1](https://img.shields.io/badge/Version-4.4.1-informational?style=flat-square) +![Version: 4.5.0](https://img.shields.io/badge/Version-4.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index e14f2f43..8c33bd58 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.1.0 +version: 0.2.0 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 9bb54f26..ca4ff68a 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. diff --git a/charts/orchestrator-software-templates-infra/Chart.yaml b/charts/orchestrator-software-templates-infra/Chart.yaml index d6a9c4b9..ae59fc60 100644 --- a/charts/orchestrator-software-templates-infra/Chart.yaml +++ b/charts/orchestrator-software-templates-infra/Chart.yaml @@ -10,7 +10,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-software-templates-infrastructure -version: 0.1.0 +version: 0.2.0 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates-infra/README.md b/charts/orchestrator-software-templates-infra/README.md index 0bbd53ca..c6928623 100644 --- a/charts/orchestrator-software-templates-infra/README.md +++ b/charts/orchestrator-software-templates-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Infra Chart for OpenShift (Community Version) -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. From 29203c152007159d304594cecc01cfe1383efbdf Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 17 Jul 2025 18:40:20 +0200 Subject: [PATCH 061/117] chore: Trigger the chart releaser workflow on both main and release branches too (#192) --- .github/workflows/release.yaml | 2 ++ cr.yaml | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9e4a21cf..e44886ad 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - release-1.[0-9]+ paths: - "charts/**" @@ -36,5 +37,6 @@ jobs: uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0 with: config: cr.yaml + mark_as_latest: ${{ github.ref == 'refs/heads/main' }} env: CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/cr.yaml b/cr.yaml index 5d960909..ce737403 100644 --- a/cr.yaml +++ b/cr.yaml @@ -1,2 +1 @@ generate-release-notes: true - From b2370c6533b53168c46a2455fdca96c60669638a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 21 Jul 2025 12:44:48 +0200 Subject: [PATCH 062/117] fix: Make the orchestrator plugins list configurable in the default values file [RHDHBUGS-1889] (#194) Co-authored-by: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 3 +- charts/backstage/templates/_partials.tpl | 57 ------------------- .../templates/dynamic-plugins-configmap.yaml | 3 +- charts/backstage/values.schema.json | 33 ++++++++++- charts/backstage/values.schema.tmpl.json | 33 ++++++++++- charts/backstage/values.yaml | 43 ++++++++++++++ 7 files changed, 109 insertions(+), 65 deletions(-) delete mode 100644 charts/backstage/templates/_partials.tpl diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 4bbcd6b6..65db5449 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.0 +version: 4.5.1 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 5cbf51fa..b4f42c62 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.0](https://img.shields.io/badge/Version-4.5.0-informational?style=flat-square) +![Version: 4.5.1](https://img.shields.io/badge/Version-4.5.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -179,6 +179,7 @@ Kubernetes: `>= 1.27.0-0` | global.host | Custom hostname shorthand, overrides `global.clusterRouterBase`, `upstream.ingress.host`, `route.host`, and url values in `upstream.backstage.appConfig`. | string | `""` | | nameOverride | | string | `"developer-hub"` | | orchestrator.enabled | | bool | `false` | +| orchestrator.plugins | Orchestrator plugins and their configuration | list | `[{"disabled":false,"integrity":"sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator":{"appIcons":[{"importName":"OrchestratorIcon","name":"orchestratorIcon"}],"dynamicRoutes":[{"importName":"OrchestratorPage","menuItem":{"icon":"orchestratorIcon","text":"Orchestrator"},"path":"/orchestrator"}]}}}}},{"disabled":false,"integrity":"sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets":{}}}}}]` | | orchestrator.serverlessLogicOperator.enabled | | bool | `true` | | orchestrator.serverlessOperator.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | diff --git a/charts/backstage/templates/_partials.tpl b/charts/backstage/templates/_partials.tpl deleted file mode 100644 index c03526e2..00000000 --- a/charts/backstage/templates/_partials.tpl +++ /dev/null @@ -1,57 +0,0 @@ -{{- define "orchestrator.plugins" }} -{{- $config := include "orchestrator.plugins.config" . | fromYaml }} -plugins: - - disabled: false - package: "{{ $config.orchestratorPlugins.orchestratorBackend.package }}" - integrity: "{{ $config.orchestratorPlugins.orchestratorBackend.integrity }}" - pluginConfig: - orchestrator: - dataIndexService: - url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - - disabled: false - package: "{{ $config.orchestratorPlugins.orchestrator.package }}" - integrity: "{{ $config.orchestratorPlugins.orchestrator.integrity }}" - pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator: - appIcons: - - importName: OrchestratorIcon - name: orchestratorIcon - dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - path: /orchestrator - - disabled: false - package: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.package }}" - integrity: "{{ $config.orchestratorPlugins.scaffolderBackendOrchestrator.integrity }}" - pluginConfig: - orchestrator: - dataIndexService: - url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - - disabled: false - package: "{{ $config.orchestratorPlugins.orchestratorFormWidgets.package }}" - integrity: "{{ $config.orchestratorPlugins.orchestratorFormWidgets.integrity }}" - pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} -{{- end }} -{{- define "orchestrator.plugins.config" }} -orchestratorPlugins: - scope: "@redhat" - orchestrator: - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz" - integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== - orchestratorBackend: - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz" - integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== - scaffolderBackendOrchestrator: - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz" - integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== - orchestratorFormWidgets: - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz" - integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== -{{- end }} \ No newline at end of file diff --git a/charts/backstage/templates/dynamic-plugins-configmap.yaml b/charts/backstage/templates/dynamic-plugins-configmap.yaml index 27547a95..321fd177 100644 --- a/charts/backstage/templates/dynamic-plugins-configmap.yaml +++ b/charts/backstage/templates/dynamic-plugins-configmap.yaml @@ -12,8 +12,7 @@ data: {{- end }} {{- if .Values.orchestrator.enabled }} - {{- $orchestratorPlugins := include "orchestrator.plugins" . | fromYaml }} - {{- range $orchestratorPlugins.plugins }} + {{- range .Values.orchestrator.plugins }} {{- $plugins = append $plugins . }} {{- end }} {{- end }} diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 1118d889..a68fa588 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -59,11 +59,11 @@ "type": "boolean" }, "integrity": { - "title": "Integrity checksum of the package. Optional for local packages. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", + "title": "Integrity checksum of the package. Optional for local packages and OCI packages. For OCI packages, you can specify the image digest in place of the tag in the 'package' field. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", "type": "string" }, "package": { - "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command.", + "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command (for NPM packages) or the `skopeo copy` command (for OCI packages).", "type": "string" }, "pluginConfig": { @@ -99,6 +99,35 @@ "title": "enabled flag", "type": "boolean" }, + "plugins": { + "items": { + "properties": { + "disabled": { + "default": false, + "title": "Disable the plugin.", + "type": "boolean" + }, + "integrity": { + "title": "Integrity checksum of the package. Optional for local packages and OCI packages. For OCI packages, you can specify the image digest in place of the tag in the 'package' field. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", + "type": "string" + }, + "package": { + "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command (for NPM packages) or the `skopeo copy` command (for OCI packages).", + "type": "string" + }, + "pluginConfig": { + "title": "Optional plugin-specific app-config YAML fragment.", + "type": "object" + } + }, + "required": [ + "package" + ], + "type": "object" + }, + "title": "List of orchestrator plugins and their configuration", + "type": "array" + }, "serverlessLogicOperator": { "additionalProperties": false, "properties": { diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index 02e31075..58cd5bd8 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -42,11 +42,11 @@ "type": "object", "properties": { "package": { - "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command.", + "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command (for NPM packages) or the `skopeo copy` command (for OCI packages).", "type": "string" }, "integrity": { - "title": "Integrity checksum of the package. Optional for local packages. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", + "title": "Integrity checksum of the package. Optional for local packages and OCI packages. For OCI packages, you can specify the image digest in place of the tag in the 'package' field. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", "type": "string" }, "pluginConfig": { @@ -206,6 +206,35 @@ "type": "boolean", "default": false }, + "plugins": { + "title": "List of orchestrator plugins and their configuration", + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "title": "Package specification of the dynamic plugin to install. It should be usable by the `npm pack` command (for NPM packages) or the `skopeo copy` command (for OCI packages).", + "type": "string" + }, + "integrity": { + "title": "Integrity checksum of the package. Optional for local packages and OCI packages. For OCI packages, you can specify the image digest in place of the tag in the 'package' field. Supported algorithms include: `sha512`, `sha384` and `sha256`. Refer to https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description for more information", + "type": "string" + }, + "pluginConfig": { + "title": "Optional plugin-specific app-config YAML fragment.", + "type": "object" + }, + "disabled": { + "title": "Disable the plugin.", + "type": "boolean", + "default": false + } + }, + "required": [ + "package" + ] + } + }, "serverlessLogicOperator": { "title": "serverlessLogicOperator configuration", "type": "object", diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 0088345c..4a96d23d 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -386,3 +386,46 @@ orchestrator: # -- Image for the container used by the create-db job createDBJobImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" + + # -- Orchestrator plugins and their configuration + plugins: + # RHDHBUGS-1464: Note that the plugins here fetch the packages from their direct HTTP download URLs from the (official) Red Hat NPM Registry. + # Previously, we were using the "@redhat/plugin@version" form along with injecting a .npmrc Secret to resolve the "@redhat" scope, + # but this caused conflicting issues with user-provided .npmrc secrets. + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz" + integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz" + integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + path: /orchestrator + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz" + integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz" + integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} From e42438be551e8ef034a477b5ff54432361a2945b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 21 Jul 2025 16:35:45 +0200 Subject: [PATCH 063/117] fix: Add test case deploying an Orchestrator-flavored instance with a user provided npmrc secret [RHDHBUGS-1893] (#195) Co-authored-by: rm3l Co-authored-by: github-actions[bot] --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 5 ++- ...ator-and-dynamic-plugins-npmrc-values.yaml | 23 +++++++++++ .../templates/tests/test-secret.yaml | 16 ++++++++ charts/backstage/values.schema.json | 39 +++++++++++++++++++ charts/backstage/values.schema.tmpl.json | 39 +++++++++++++++++++ charts/backstage/values.yaml | 6 +++ 7 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 charts/backstage/ci/with-orchestrator-and-dynamic-plugins-npmrc-values.yaml create mode 100644 charts/backstage/templates/tests/test-secret.yaml diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 65db5449..157e6dfc 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.1 +version: 4.5.2 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index b4f42c62..5a58f368 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.1](https://img.shields.io/badge/Version-4.5.1-informational?style=flat-square) +![Version: 4.5.2](https://img.shields.io/badge/Version-4.5.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -209,11 +209,12 @@ Kubernetes: `>= 1.27.0-0` | route.tls.key | Key file contents | string | `""` | | route.tls.termination | Specify TLS termination. | string | `"edge"` | | route.wildcardPolicy | Wildcard policy if any for the route. Currently only 'Subdomain' or 'None' is allowed. | string | `"None"` | -| test | Test pod parameters | object | `{"enabled":true,"image":{"registry":"quay.io","repository":"curl/curl","tag":"latest"}}` | +| test | Test pod parameters | object | `{"enabled":true,"image":{"registry":"quay.io","repository":"curl/curl","tag":"latest"},"injectTestNpmrcSecret":false}` | | test.enabled | Whether to enable the test-connection pod used for testing the Release using `helm test`. | bool | `true` | | test.image.registry | Test connection pod image registry | string | `"quay.io"` | | test.image.repository | Test connection pod image repository. Note that the image needs to have both the `sh` and `curl` binaries in it. | string | `"curl/curl"` | | test.image.tag | Test connection pod image tag. Note that the image needs to have both the `sh` and `curl` binaries in it. | string | `"latest"` | +| test.injectTestNpmrcSecret | Whether to inject a fake dynamic plugins npmrc secret.
See RHDHBUGS-1893 and RHDHBUGS-1464 for the motivation behind this.
This is only used for testing purposes and should not be used in production.
Only relevant when `test.enabled` field is set to `true`. | bool | `false` | | upstream | Upstream Backstage [chart configuration](https://github.com/backstage/charts/blob/main/charts/backstage/values.yaml) | object | Use Openshift compatible settings | | upstream.backstage.extraVolumes[0] | Ephemeral volume that will contain the dynamic plugins installed by the initContainer below at start. | object | `{"ephemeral":{"volumeClaimTemplate":{"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"5Gi"}}}}},"name":"dynamic-plugins-root"}` | | upstream.backstage.extraVolumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests.storage | Size of the volume that will contain the dynamic plugins. It should be large enough to contain all the plugins. | string | `"5Gi"` | diff --git a/charts/backstage/ci/with-orchestrator-and-dynamic-plugins-npmrc-values.yaml b/charts/backstage/ci/with-orchestrator-and-dynamic-plugins-npmrc-values.yaml new file mode 100644 index 00000000..d5db2ac1 --- /dev/null +++ b/charts/backstage/ci/with-orchestrator-and-dynamic-plugins-npmrc-values.yaml @@ -0,0 +1,23 @@ +route: + enabled: false + +upstream: + postgresql: + primary: + persistence: + enabled: false + +global: + dynamic: + plugins: + # Enable additional plugins, which should be merged with the Orchestrator plugins + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic + disabled: false + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import + disabled: false + +orchestrator: + enabled: true + +test: + injectTestNpmrcSecret: true diff --git a/charts/backstage/templates/tests/test-secret.yaml b/charts/backstage/templates/tests/test-secret.yaml new file mode 100644 index 00000000..1dc407d5 --- /dev/null +++ b/charts/backstage/templates/tests/test-secret.yaml @@ -0,0 +1,16 @@ +# RHDHBUGS-1893: test-only option to inject a user-provided dynamic plugins npmrc secret. +# Doing it this way because the secret name is dynamic and depends on the release name. +{{- if and .Values.test.enabled .Values.test.injectTestNpmrcSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: '{{ .Release.Name }}-dynamic-plugins-npmrc' + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-5" +immutable: true +stringData: + .npmrc: | + @myscope:registry=https://my-registry.example.com + //my-registry.example.com:_authToken=foo +{{- end }} diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index a68fa588..de4c3bc9 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -361,6 +361,45 @@ "title": "OpenShift Route parameters.", "type": "object" }, + "test": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "title": "Enable test configuration. If enabled, test resources will be created to verify the Helm Release has been successfully deployed using the `helm test` command.", + "type": "boolean" + }, + "image": { + "additionalProperties": false, + "properties": { + "registry": { + "default": "quay.io", + "title": "Registry to use for the test pod image.", + "type": "string" + }, + "repository": { + "default": "curl/curl", + "title": "Repository to use for the test pod image.", + "type": "string" + }, + "tag": { + "default": "latest", + "title": "Tag to use for the test pod image.", + "type": "string" + } + }, + "title": "Image to use for the test pod. Note that the image needs to have both the `sh` and `curl` binaries in it.", + "type": "object" + }, + "injectTestNpmrcSecret": { + "default": false, + "title": "Whether to inject a fake dynamic plugins npmrc secret. This is only used for testing purposes and should not be used in production. It is only relevant when `test.enabled` field is set to `true`.", + "type": "boolean" + } + }, + "title": "Test configuration for the Backstage chart.", + "type": "object" + }, "upstream": { "properties": { "backstage": { diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index 58cd5bd8..746d3459 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -196,6 +196,45 @@ } } }, + "test": { + "title": "Test configuration for the Backstage chart.", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "Enable test configuration. If enabled, test resources will be created to verify the Helm Release has been successfully deployed using the `helm test` command.", + "type": "boolean", + "default": true + }, + "image": { + "title": "Image to use for the test pod. Note that the image needs to have both the `sh` and `curl` binaries in it.", + "type": "object", + "additionalProperties": false, + "properties": { + "registry": { + "title": "Registry to use for the test pod image.", + "type": "string", + "default": "quay.io" + }, + "repository": { + "title": "Repository to use for the test pod image.", + "type": "string", + "default": "curl/curl" + }, + "tag": { + "title": "Tag to use for the test pod image.", + "type": "string", + "default": "latest" + } + } + }, + "injectTestNpmrcSecret": { + "title": "Whether to inject a fake dynamic plugins npmrc secret. This is only used for testing purposes and should not be used in production. It is only relevant when `test.enabled` field is set to `true`.", + "type": "boolean", + "default": false + } + } + }, "orchestrator": { "title": "orchestrator configuration", "type": "object", diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 4a96d23d..c4014fe0 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -349,6 +349,12 @@ test: # -- Test connection pod image tag. Note that the image needs to have both the `sh` and `curl` binaries in it. tag: latest + # -- Whether to inject a fake dynamic plugins npmrc secret. + #
See RHDHBUGS-1893 and RHDHBUGS-1464 for the motivation behind this. + #
This is only used for testing purposes and should not be used in production. + #
Only relevant when `test.enabled` field is set to `true`. + injectTestNpmrcSecret: false + orchestrator: enabled: false serverlessLogicOperator: From a9b884c68d412d7c933665c636bb8d947e743006 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:51:26 +0200 Subject: [PATCH 064/117] chore(deps): update mikefarah/yq action to v4.47.1 (#198) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 3b1b6b0e..7814ef5f 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -81,7 +81,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 + uses: mikefarah/yq@f03c9dc599c37bfcaf533427211d05e51e6fee64 # v4.47.1 with: cmd: yq ".version" charts/backstage/Chart.yaml @@ -94,7 +94,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 + uses: mikefarah/yq@f03c9dc599c37bfcaf533427211d05e51e6fee64 # v4.47.1 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml From 20e665f56c1905ed3e2485e8d1ced41b575cb408 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 24 Jul 2025 14:58:44 +0100 Subject: [PATCH 065/117] Changed argument logic to update versions for all available charts Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 3b1b6b0e..401829df 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -24,16 +24,32 @@ jobs: const commentBody = context.payload.comment.body; const commandPrefix = "/bump "; let commandName = ""; - let commandArgs = ""; + let chartName = ""; + let versionLevel = ""; if (commentBody.startsWith(commandPrefix)) { commandName = "bump"; const args = commentBody.slice(commandPrefix.length).trim().split(" "); - commandArgs = args[0] || "patch"; + const supportedCharts = ["backstage", "orchestrator-infra", "orchestrator-software-templates-infra"]; + + if (args.length >= 1) { + const potentialChart = args[0]; + if (supportedCharts.includes(potentialChart)) { + chartName = potentialChart; + versionLevel = args[1] || "patch"; + } else { + chartName = "backstage"; + versionLevel = args[0] || "patch"; + } + } else { + chartName = "backstage"; + versionLevel = "patch"; + } } core.setOutput("command-name", commandName); - core.setOutput("command-arguments", commandArgs); + core.setOutput("chart-name", chartName); + core.setOutput("version-level", versionLevel); - name: Add eyes reaction if: steps.command.outputs.command-name == 'bump' @@ -42,7 +58,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} comment-id: ${{ github.event.comment.id }} - reaction-type: eyes + reactions: eyes - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' @@ -83,20 +99,20 @@ jobs: id: get_version uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 with: - cmd: yq ".version" charts/backstage/Chart.yaml + cmd: yq ".version" charts/${{ steps.command.outputs.chart-name }}/Chart.yaml - uses: actions-ecosystem/action-bump-semver@34e334551143a5301f38c830e44a22273c6ff5c5 # v1 if: steps.command.outputs.command-name == 'bump' id: semver with: current_version: ${{ steps.get_version.outputs.result }} - level: ${{ steps.command.outputs.command-arguments }} + level: ${{ steps.command.outputs.version-level }} - name: Bump the version if: steps.command.outputs.command-name == 'bump' uses: mikefarah/yq@1187c954ec44c3a0e62c13ca7dc9dadc1ca80ae7 # v4.46.1 with: - cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/backstage/Chart.yaml + cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/${{ steps.command.outputs.chart-name }}/Chart.yaml - name: Run pre-commit if: steps.command.outputs.command-name == 'bump' @@ -111,7 +127,7 @@ jobs: if: steps.command.outputs.command-name == 'bump' uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 with: - commit_message: Bump version to ${{ steps.semver.outputs.new_version }} + commit_message: Bump ${{ steps.command.outputs.chart-name }} version to ${{ steps.semver.outputs.new_version }} commit_options: '-s' commit_user_name: RHDH Bot commit_user_email: 146280956+rhdh-bot@users.noreply.github.com From 5ddfde5b15d28608df7fb287f3268b3be40c6054 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 24 Jul 2025 15:21:17 +0100 Subject: [PATCH 066/117] Reverting reaction change based on sourcery-ai Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 401829df..acb55726 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -58,7 +58,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} comment-id: ${{ github.event.comment.id }} - reactions: eyes + reaction-type: eyes - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' From df60478a8029269dd0d7c122fcc01c09c7d37bb7 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 24 Jul 2025 15:29:14 +0100 Subject: [PATCH 067/117] Adding error for invalid charts, rather than defaulting to backstage. Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index acb55726..7bf53451 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -38,8 +38,8 @@ jobs: chartName = potentialChart; versionLevel = args[1] || "patch"; } else { - chartName = "backstage"; - versionLevel = args[0] || "patch"; + core.setFailed(`Error: Invalid chart name: ${potentialChart}. Valid charts: ${supportedCharts.join(", ")}`); + return; } } else { chartName = "backstage"; From 3ca947fd3287dff565030a6ec87a016ccc76c995 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Wed, 30 Jul 2025 11:11:18 +0100 Subject: [PATCH 068/117] Updating to check version and chart used in command Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 44 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 7bf53451..deb8baa8 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -27,25 +27,31 @@ jobs: let chartName = ""; let versionLevel = ""; + const supportedCharts = ["backstage", "orchestrator-infra", "orchestrator-software-templates-infra"]; + const validVersions = ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"]; + if (commentBody.startsWith(commandPrefix)) { commandName = "bump"; - const args = commentBody.slice(commandPrefix.length).trim().split(" "); - const supportedCharts = ["backstage", "orchestrator-infra", "orchestrator-software-templates-infra"]; - - if (args.length >= 1) { - const potentialChart = args[0]; - if (supportedCharts.includes(potentialChart)) { - chartName = potentialChart; - versionLevel = args[1] || "patch"; - } else { - core.setFailed(`Error: Invalid chart name: ${potentialChart}. Valid charts: ${supportedCharts.join(", ")}`); + const args = commentBody.slice(commandPrefix.length).trim().split(" "); + + if (args.length >= 1 && supportedCharts.includes(args[0])) { + chartName = args[0]; + versionLevel = args[1] || "patch"; + if (!validVersions.includes(versionLevel)) { + core.setFailed(`Error: Invalid version: ${versionLevel}. Valid versions: ${validVersions.join(", ")}.`); return; - } - } else { - chartName = "backstage"; - versionLevel = "patch"; + } + } else if (args.length === 1 && validVersions.includes(args[0])) { + chartName = "backstage"; // Default chart if only version level is provided + versionLevel = args[0]; + } else if (args.length === 0) { + chartName = "backstage"; + versionLevel = "patch"; // Default to patch if no arguments are provided + } else { + core.setFailed(`Error: Invalid chart name: ${args[0]. Valid charts: ${supportedCharts.join(", ")}.`); + return; + } } - } core.setOutput("command-name", commandName); core.setOutput("chart-name", chartName); @@ -58,7 +64,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} comment-id: ${{ github.event.comment.id }} - reaction-type: eyes + reactions: eyes - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' @@ -101,7 +107,7 @@ jobs: with: cmd: yq ".version" charts/${{ steps.command.outputs.chart-name }}/Chart.yaml - - uses: actions-ecosystem/action-bump-semver@34e334551143a5301f38c830e44a22273c6ff5c5 # v1 + - uses: actions-ecosystem/action-bump-semver@34e334551143a5301f38c830e44a22273c6ff5c5 # v1 if: steps.command.outputs.command-name == 'bump' id: semver with: @@ -128,7 +134,7 @@ jobs: uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 with: commit_message: Bump ${{ steps.command.outputs.chart-name }} version to ${{ steps.semver.outputs.new_version }} - commit_options: '-s' + commit_options: "-s" commit_user_name: RHDH Bot - commit_user_email: 146280956+rhdh-bot@users.noreply.github.com + commit_user_email: 146280956+rhdh-bot@users.noreply.github.com commit_author: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com > From 25f3d22f3aae92c5f17fd4e4d8908b1777846aa4 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Wed, 30 Jul 2025 11:25:34 +0100 Subject: [PATCH 069/117] Updating syntax and failure reasons Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index deb8baa8..0b965f38 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -33,25 +33,28 @@ jobs: if (commentBody.startsWith(commandPrefix)) { commandName = "bump"; const args = commentBody.slice(commandPrefix.length).trim().split(" "); - + if (args.length >= 1 && supportedCharts.includes(args[0])) { chartName = args[0]; versionLevel = args[1] || "patch"; if (!validVersions.includes(versionLevel)) { core.setFailed(`Error: Invalid version: ${versionLevel}. Valid versions: ${validVersions.join(", ")}.`); return; - } - } else if (args.length === 1 && validVersions.includes(args[0])) { - chartName = "backstage"; // Default chart if only version level is provided - versionLevel = args[0]; - } else if (args.length === 0) { - chartName = "backstage"; - versionLevel = "patch"; // Default to patch if no arguments are provided - } else { - core.setFailed(`Error: Invalid chart name: ${args[0]. Valid charts: ${supportedCharts.join(", ")}.`); - return; - } + } + } else if (args.length === 1 && validVersions.includes(args[0])) { + chartName = "backstage"; // Default chart if only version level is provided + versionLevel = args[0]; + } else if (args.length === 0) { + chartName = "backstage"; + versionLevel = "patch"; // Default to patch if no arguments are provided + } else if (args.length >= 1 && !supportedCharts.includes(args[0])) { + core.setFailed(`Error: Invalid chart name: ${args[0]}. Valid charts: ${supportedCharts.join(", ")}.`); + return; + } else { + core.setFailed(`Error: Invalid arguments. Usage: /bump [chart] [${validVersions.join("|")}]`); + return; } + } core.setOutput("command-name", commandName); core.setOutput("chart-name", chartName); From eb3aca2c2e3fff0ba39426a19449a7b23a4197f4 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 11:47:08 +0100 Subject: [PATCH 070/117] Trying a change to recognise chart names Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 38 +++++++---------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 0b965f38..cd4a84bf 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -25,41 +25,21 @@ jobs: const commandPrefix = "/bump "; let commandName = ""; let chartName = ""; - let versionLevel = ""; - - const supportedCharts = ["backstage", "orchestrator-infra", "orchestrator-software-templates-infra"]; - const validVersions = ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"]; + let bumpLevel = ""; if (commentBody.startsWith(commandPrefix)) { commandName = "bump"; - const args = commentBody.slice(commandPrefix.length).trim().split(" "); - - if (args.length >= 1 && supportedCharts.includes(args[0])) { + const args = commentBody.slice(commandPrefix.length).trim().split(" "); + + if (args.length > 0 && args[0]) { chartName = args[0]; - versionLevel = args[1] || "patch"; - if (!validVersions.includes(versionLevel)) { - core.setFailed(`Error: Invalid version: ${versionLevel}. Valid versions: ${validVersions.join(", ")}.`); - return; - } - } else if (args.length === 1 && validVersions.includes(args[0])) { - chartName = "backstage"; // Default chart if only version level is provided - versionLevel = args[0]; - } else if (args.length === 0) { - chartName = "backstage"; - versionLevel = "patch"; // Default to patch if no arguments are provided - } else if (args.length >= 1 && !supportedCharts.includes(args[0])) { - core.setFailed(`Error: Invalid chart name: ${args[0]}. Valid charts: ${supportedCharts.join(", ")}.`); - return; + bumpLevel = args[1] || "patch"; } else { - core.setFailed(`Error: Invalid arguments. Usage: /bump [chart] [${validVersions.join("|")}]`); - return; - } + commandName = ""; + core.warning("No chart name provided. Format: /bump [level]"); + } } - core.setOutput("command-name", commandName); - core.setOutput("chart-name", chartName); - core.setOutput("version-level", versionLevel); - - name: Add eyes reaction if: steps.command.outputs.command-name == 'bump' uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 @@ -115,7 +95,7 @@ jobs: id: semver with: current_version: ${{ steps.get_version.outputs.result }} - level: ${{ steps.command.outputs.version-level }} + level: ${{ steps.command.outputs.bump-level }} - name: Bump the version if: steps.command.outputs.command-name == 'bump' From ca51b193a2a7253076fc6136d3243216bd53a815 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 11:55:39 +0100 Subject: [PATCH 071/117] Setting outputs for actions to use Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index cd4a84bf..18bfe9e5 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -34,6 +34,11 @@ jobs: if (args.length > 0 && args[0]) { chartName = args[0]; bumpLevel = args[1] || "patch"; + + core.setOutput("chart-name", chartName); + core.setOutput("bump-level", bumpLevel); + core.setOutput("command-name", commandName); + } else { commandName = ""; core.warning("No chart name provided. Format: /bump [level]"); From 6aabf77e7e2cf8db5404cd173dfa7e81870de570 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 12:08:13 +0100 Subject: [PATCH 072/117] Adding debug commands Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) mode change 100644 => 100755 .github/workflows/bump-version.yaml diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml old mode 100644 new mode 100755 index 18bfe9e5..b52ba918 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -35,10 +35,13 @@ jobs: chartName = args[0]; bumpLevel = args[1] || "patch"; + console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); + console.log(`Args array: ${JSON.stringify(args)}`); + core.setOutput("chart-name", chartName); core.setOutput("bump-level", bumpLevel); core.setOutput("command-name", commandName); - + } else { commandName = ""; core.warning("No chart name provided. Format: /bump [level]"); From 50e961cbb7cdb0f3368ef0ef49db2d8e79364d59 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 14:51:07 +0100 Subject: [PATCH 073/117] Changing code to test Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 111 +++++++++++++++++----------- event.json | 12 +++ 2 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 event.json diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index b52ba918..39c23b46 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -1,9 +1,24 @@ name: Bump on: + # Trigger for production use issue_comment: types: [created] + # Trigger for testing purposes + workflow_dispatch: + inputs: + pr_number: + description: 'Pull Request Number to test against' + required: true + chart_name: + description: 'Chart Name to bump' + required: true + bump_level: + description: 'Bump Level (major, minor, patch)' + required: true + default: 'patch' + jobs: chart-version: name: Chart Version @@ -15,47 +30,47 @@ jobs: issues: write steps: - - name: Check for command - id: command - continue-on-error: true - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - const commentBody = context.payload.comment.body; - const commandPrefix = "/bump "; - let commandName = ""; - let chartName = ""; - let bumpLevel = ""; - - if (commentBody.startsWith(commandPrefix)) { - commandName = "bump"; - const args = commentBody.slice(commandPrefix.length).trim().split(" "); + # - name: Check for command + # id: command + # continue-on-error: true + # uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + # with: + # script: | + # const commentBody = context.payload.comment.body; + # const commandPrefix = "/bump "; + # let commandName = ""; + # let chartName = ""; + # let bumpLevel = ""; + + # if (commentBody.startsWith(commandPrefix)) { + # commandName = "bump"; + # const args = commentBody.slice(commandPrefix.length).trim().split(" "); - if (args.length > 0 && args[0]) { - chartName = args[0]; - bumpLevel = args[1] || "patch"; - - console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); - console.log(`Args array: ${JSON.stringify(args)}`); - - core.setOutput("chart-name", chartName); - core.setOutput("bump-level", bumpLevel); - core.setOutput("command-name", commandName); - - } else { - commandName = ""; - core.warning("No chart name provided. Format: /bump [level]"); - } - } - - - name: Add eyes reaction - if: steps.command.outputs.command-name == 'bump' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.repository }} - comment-id: ${{ github.event.comment.id }} - reactions: eyes + # if (args.length > 0 && args[0]) { + # chartName = args[0]; + # bumpLevel = args[1] || "patch"; + + # console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); + # console.log(`Args array: ${JSON.stringify(args)}`); + + # core.setOutput("chart-name", chartName); + # core.setOutput("bump-level", bumpLevel); + # core.setOutput("command-name", commandName); + + # } else { + # commandName = ""; + # core.warning("No chart name provided. Format: /bump [level]"); + # } + # } + + # - name: Add eyes reaction + # if: steps.command.outputs.command-name == 'bump' + # uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} + # repository: ${{ github.repository }} + # comment-id: ${{ github.event.comment.id }} + # reactions: eyes - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' @@ -85,9 +100,16 @@ jobs: with: token: ${{ steps.generate_token.outputs.token }} + # Modifying for testing + # - name: Checkout Pull Request + # if: steps.command.outputs.command-name == 'bump' + # run: gh pr checkout ${{ github.event.issue.number }} + # env: + # GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + - name: Checkout Pull Request if: steps.command.outputs.command-name == 'bump' - run: gh pr checkout ${{ github.event.issue.number }} + run: gh pr checkout ${{ github.event.inputs.pr_number }} env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} @@ -103,7 +125,10 @@ jobs: id: semver with: current_version: ${{ steps.get_version.outputs.result }} - level: ${{ steps.command.outputs.bump-level }} + level: ${{ github.event.inputs.bump_level }} + # modified for testing + # current_version: ${{ steps.get_version.outputs.result }} + # level: ${{ steps.command.outputs.bump-level }} - name: Bump the version if: steps.command.outputs.command-name == 'bump' @@ -124,7 +149,7 @@ jobs: if: steps.command.outputs.command-name == 'bump' uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 with: - commit_message: Bump ${{ steps.command.outputs.chart-name }} version to ${{ steps.semver.outputs.new_version }} + commit_message: Bump ${{ github.event.inputs.chart_name }} version to ${{ steps.semver.outputs.new_version }} commit_options: "-s" commit_user_name: RHDH Bot commit_user_email: 146280956+rhdh-bot@users.noreply.github.com diff --git a/event.json b/event.json new file mode 100644 index 00000000..2f8561f8 --- /dev/null +++ b/event.json @@ -0,0 +1,12 @@ +{ + "comment": { + "body": "/bump my-chart minor", + "id": 12345 + }, + "issue": { + "number": 42 + }, + "repository": { + "full_name": "your-username/your-repo" + } +} \ No newline at end of file From ea1707b8fbbc72618220507c7ad4d1e34b73e49b Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 14:59:18 +0100 Subject: [PATCH 074/117] Reverting back as test did not run as expected Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 96 +++++++++++++---------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 39c23b46..117fd19f 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -30,47 +30,47 @@ jobs: issues: write steps: - # - name: Check for command - # id: command - # continue-on-error: true - # uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - # with: - # script: | - # const commentBody = context.payload.comment.body; - # const commandPrefix = "/bump "; - # let commandName = ""; - # let chartName = ""; - # let bumpLevel = ""; - - # if (commentBody.startsWith(commandPrefix)) { - # commandName = "bump"; - # const args = commentBody.slice(commandPrefix.length).trim().split(" "); + - name: Check for command + id: command + continue-on-error: true + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const commentBody = context.payload.comment.body; + const commandPrefix = "/bump "; + let commandName = ""; + let chartName = ""; + let bumpLevel = ""; + + if (commentBody.startsWith(commandPrefix)) { + commandName = "bump"; + const args = commentBody.slice(commandPrefix.length).trim().split(" "); - # if (args.length > 0 && args[0]) { - # chartName = args[0]; - # bumpLevel = args[1] || "patch"; - - # console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); - # console.log(`Args array: ${JSON.stringify(args)}`); - - # core.setOutput("chart-name", chartName); - # core.setOutput("bump-level", bumpLevel); - # core.setOutput("command-name", commandName); - - # } else { - # commandName = ""; - # core.warning("No chart name provided. Format: /bump [level]"); - # } - # } - - # - name: Add eyes reaction - # if: steps.command.outputs.command-name == 'bump' - # uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - # with: - # token: ${{ secrets.GITHUB_TOKEN }} - # repository: ${{ github.repository }} - # comment-id: ${{ github.event.comment.id }} - # reactions: eyes + if (args.length > 0 && args[0]) { + chartName = args[0]; + bumpLevel = args[1] || "patch"; + + console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); + console.log(`Args array: ${JSON.stringify(args)}`); + + core.setOutput("chart-name", chartName); + core.setOutput("bump-level", bumpLevel); + core.setOutput("command-name", commandName); + + } else { + commandName = ""; + core.warning("No chart name provided. Format: /bump [level]"); + } + } + + - name: Add eyes reaction + if: steps.command.outputs.command-name == 'bump' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + comment-id: ${{ github.event.comment.id }} + reactions: eyes - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 if: steps.command.outputs.command-name == 'bump' @@ -100,16 +100,9 @@ jobs: with: token: ${{ steps.generate_token.outputs.token }} - # Modifying for testing - # - name: Checkout Pull Request - # if: steps.command.outputs.command-name == 'bump' - # run: gh pr checkout ${{ github.event.issue.number }} - # env: - # GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - name: Checkout Pull Request if: steps.command.outputs.command-name == 'bump' - run: gh pr checkout ${{ github.event.inputs.pr_number }} + run: gh pr checkout ${{ github.event.issue.number }} env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} @@ -125,10 +118,7 @@ jobs: id: semver with: current_version: ${{ steps.get_version.outputs.result }} - level: ${{ github.event.inputs.bump_level }} - # modified for testing - # current_version: ${{ steps.get_version.outputs.result }} - # level: ${{ steps.command.outputs.bump-level }} + level: ${{ steps.command.outputs.bump-level }} - name: Bump the version if: steps.command.outputs.command-name == 'bump' @@ -149,7 +139,7 @@ jobs: if: steps.command.outputs.command-name == 'bump' uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 with: - commit_message: Bump ${{ github.event.inputs.chart_name }} version to ${{ steps.semver.outputs.new_version }} + commit_message: Bump ${{ steps.command.outputs.chart-name }} version to ${{ steps.semver.outputs.new_version }} commit_options: "-s" commit_user_name: RHDH Bot commit_user_email: 146280956+rhdh-bot@users.noreply.github.com From ba34551b7a7b36cd6655ad290ee10ced0551a485 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 15:04:33 +0100 Subject: [PATCH 075/117] Removing all unnecessary test info and files Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 15 --------------- event.json | 12 ------------ 2 files changed, 27 deletions(-) delete mode 100644 event.json diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 117fd19f..b52ba918 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -1,24 +1,9 @@ name: Bump on: - # Trigger for production use issue_comment: types: [created] - # Trigger for testing purposes - workflow_dispatch: - inputs: - pr_number: - description: 'Pull Request Number to test against' - required: true - chart_name: - description: 'Chart Name to bump' - required: true - bump_level: - description: 'Bump Level (major, minor, patch)' - required: true - default: 'patch' - jobs: chart-version: name: Chart Version diff --git a/event.json b/event.json deleted file mode 100644 index 2f8561f8..00000000 --- a/event.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "comment": { - "body": "/bump my-chart minor", - "id": 12345 - }, - "issue": { - "number": 42 - }, - "repository": { - "full_name": "your-username/your-repo" - } -} \ No newline at end of file From 2cde927b2af8c1cdad48745718757ca39e206bac Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 15:57:15 +0100 Subject: [PATCH 076/117] Testing Signed-off-by: Leanne Ahern --- charts/backstage/Chart.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 157e6dfc..1930e359 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,9 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z +<<<<<<< HEAD version: 4.5.2 +======= +# making change for testing purposes +version: 4.4.0 +>>>>>>> efc7ec0 (Testing workflow using charts) From 6d1bbb79363bded2a6fd345aedc8d7ac2c96bf9f Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 16:04:10 +0100 Subject: [PATCH 077/117] Tests Signed-off-by: Leanne Ahern --- charts/backstage/Chart.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 1930e359..b42dc368 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,9 +47,5 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -<<<<<<< HEAD -version: 4.5.2 -======= # making change for testing purposes version: 4.4.0 ->>>>>>> efc7ec0 (Testing workflow using charts) From 77464e32df65d69257f5b57acf046e210f46f3f5 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 16:20:48 +0100 Subject: [PATCH 078/117] Tests Signed-off-by: Leanne Ahern --- charts/backstage/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index b42dc368..7ac05f7e 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -48,4 +48,4 @@ sources: [] # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z # making change for testing purposes -version: 4.4.0 +version: 4.5.2 From d7805e3e6dd89ca09d1876edea02c28754a8579a Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Thu, 31 Jul 2025 17:01:15 +0100 Subject: [PATCH 079/117] test: add test change for bump testing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a4eed3a9..5b1ca74c 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,4 @@ See [charts/orchestrator-infra/README.md](charts/orchestrator-infra/README.md) To report issues against this chart, please use JIRA (not GH issues): https://issues.redhat.com/browse/RHIDP +# test change From 388357c5d9c5b8c373ea70ec0aad4ae336c97adf Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Tue, 5 Aug 2025 14:06:40 +0100 Subject: [PATCH 080/117] Updating set.Output as it is going to be disabled Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index ecdc412b..cf994d08 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -38,9 +38,10 @@ jobs: console.log(`Chart Name: ${chartName}, Bump Level: ${bumpLevel}`); console.log(`Args array: ${JSON.stringify(args)}`); - core.setOutput("chart-name", chartName); - core.setOutput("bump-level", bumpLevel); - core.setOutput("command-name", commandName); + const fs = require('fs'); + fs.appendFileSync(process.env.GITHUB_OUTPUT, `chart-name=${chartName}\n`); + fs.appendFileSync(process.env.GITHUB_OUTPUT, `bump-level=${bumpLevel}\n`); + fs.appendFileSync(process.env.GITHUB_OUTPUT, `command-name=${commandName}\n`); } else { commandName = ""; From 043934f240cd52eb3647eb9a174d4767f7ba296e Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Wed, 6 Aug 2025 09:56:52 +0100 Subject: [PATCH 081/117] Removing test comment in backstage Signed-off-by: Leanne Ahern --- charts/backstage/Chart.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 7ac05f7e..157e6dfc 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,5 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -# making change for testing purposes version: 4.5.2 From 328845fb98e3bd52ba574b3002d43e624a63fb80 Mon Sep 17 00:00:00 2001 From: Leanne Ahern Date: Wed, 6 Aug 2025 12:40:38 +0100 Subject: [PATCH 082/117] Removing test message in ReadMe Signed-off-by: Leanne Ahern --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 5b1ca74c..31b830b4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,4 @@ See [charts/orchestrator-infra/README.md](charts/orchestrator-infra/README.md) ## Contributing and reporting issues -To report issues against this chart, please use JIRA (not GH issues): https://issues.redhat.com/browse/RHIDP - -# test change +To report issues against this chart, please use JIRA (not GH issues): https://issues.redhat.com/browse/RHIDP \ No newline at end of file From aa57eac75f84ce81153a48d63ad847465468b91e Mon Sep 17 00:00:00 2001 From: Leanne Ahern <127859485+OpinionatedHeron@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:36:29 +0100 Subject: [PATCH 083/117] Updating workflow permissions (#209) Signed-off-by: Leanne Ahern --- .github/workflows/bump-version.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index cf994d08..15b5d5fa 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -13,6 +13,7 @@ jobs: contents: write id-token: write issues: write + pull-requests: write steps: - name: Check for command From efa04db114742e65aa33d257acda547af15a62d6 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:24:49 +0300 Subject: [PATCH 084/117] chore: bumping osl default version to 1.36 in orchestrator-infra (#201) * bumping osl default version to 1.36 in orchestrator-infra * fixing typo --- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 10 +++++----- charts/orchestrator-infra/README.md.gotmpl | 6 +++--- charts/orchestrator-infra/values.yaml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index 8c33bd58..f6ad685d 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.2.0 +version: 0.2.1 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index ca4ff68a..70bc9635 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift -![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) +![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. @@ -90,7 +90,7 @@ The command removes all the Kubernetes components associated with the chart and | serverlessLogicOperator.subscription.spec.name | name of the operator package | string | `"logic-operator-rhel8"` | | serverlessLogicOperator.subscription.spec.source | name of the catalog source | string | `"redhat-operators"` | | serverlessLogicOperator.subscription.spec.sourceNamespace | | string | `"openshift-marketplace"` | -| serverlessLogicOperator.subscription.spec.startingCSV | The initial version of the operator, must match CRDs installed by the chart | string | `"logic-operator-rhel8.v1.35.0"` | +| serverlessLogicOperator.subscription.spec.startingCSV | The initial version of the operator, must match CRDs installed by the chart | string | `"logic-operator-rhel8.v1.36.0"` | | serverlessOperator.enabled | whether the operator should be deployed by the chart | bool | `true` | | serverlessOperator.subscription.namespace | namespace where the operator should be deployed | string | `"openshift-serverless"` | | serverlessOperator.subscription.spec.channel | channel of an operator package to subscribe to | string | `"stable"` | @@ -108,10 +108,10 @@ The orchestrator-infra chart requires several CRDs for Knative Eventing and Knat The KnativeEventing and KnativeServing CRDs are required for this chart to run. These CRDs need to be present under the crds/ directory before running `helm install`. After installing the openshift-serverless subscription, more Knative CRDs will be installed on the cluster. -The versions of the CRDs present in the chart and the ones in the subscrtiprion must match. In order to verify the correct CRD, use this following command to extract the CRD: +The versions of the CRDs present in the chart and the ones in the subscription must match. In order to verify the correct CRD, use this following command to extract the CRD: ```bash -docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.36.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml -docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.36.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml ``` \ No newline at end of file diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index 07244b82..ee8fc96f 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -83,10 +83,10 @@ The orchestrator-infra chart requires several CRDs for Knative Eventing and Knat The KnativeEventing and KnativeServing CRDs are required for this chart to run. These CRDs need to be present under the crds/ directory before running `helm install`. After installing the openshift-serverless subscription, more Knative CRDs will be installed on the cluster. -The versions of the CRDs present in the chart and the ones in the subscrtiprion must match. In order to verify the correct CRD, use this following command to extract the CRD: +The versions of the CRDs present in the chart and the ones in the subscription must match. In order to verify the correct CRD, use this following command to extract the CRD: ```bash -docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.36.0 /manifests/operator_v1beta1_knativeeventing_crd.yaml > knative-eventing-crd.yaml -docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.35.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml +docker run --rm --entrypoint cat registry.redhat.io/openshift-serverless-1/serverless-operator-bundle:1.36.0 /manifests/operator_v1beta1_knativeserving_crd.yaml > knative-serving-crd.yaml ``` \ No newline at end of file diff --git a/charts/orchestrator-infra/values.yaml b/charts/orchestrator-infra/values.yaml index 158becaf..88df4343 100644 --- a/charts/orchestrator-infra/values.yaml +++ b/charts/orchestrator-infra/values.yaml @@ -15,7 +15,7 @@ serverlessLogicOperator: source: redhat-operators sourceNamespace: openshift-marketplace # -- The initial version of the operator, must match CRDs installed by the chart - startingCSV: logic-operator-rhel8.v1.35.0 + startingCSV: logic-operator-rhel8.v1.36.0 serverlessOperator: # -- whether the operator should be deployed by the chart From 922cb194533dbc74476d60a565aa25df8ac53746 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:03:37 +0300 Subject: [PATCH 085/117] Adding network policy; fixing indentation error (#207) Co-authored-by: Gennady Azarenkov --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/network-policies.yaml | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 157e6dfc..d7a1eb57 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.2 +version: 4.5.3 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 5a58f368..63941411 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.2](https://img.shields.io/badge/Version-4.5.2-informational?style=flat-square) +![Version: 4.5.3](https://img.shields.io/badge/Version-4.5.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/network-policies.yaml b/charts/backstage/templates/network-policies.yaml index 4c795d41..25722d09 100644 --- a/charts/backstage/templates/network-policies.yaml +++ b/charts/backstage/templates/network-policies.yaml @@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: - name: {{ .Release.Name }}-allow-knative-to-sonataflow-and-workflows + name: {{ .Release.Name }}-allow-infra-ns-to-workflow-ns # Sonataflow and Workflows are using the RHDH target namespace. namespace: {{ .Release.Namespace | quote }} spec: @@ -17,6 +17,11 @@ spec: matchLabels: # Allow auxiliary knative function for workflow (such as m2k-save-transformation) kubernetes.io/metadata.name: knative-serving + - namespaceSelector: + matchLabels: + # Allow communication between the serverless logic operator and the workflow namespace. + kubernetes.io/metadata.name: openshift-serverless-logic + --- # NetworkPolicy to unblock incoming traffic to the namespace apiVersion: networking.k8s.io/v1 From e40f6dd9df8b92e5aef7deabce71453d090fd2ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:07:53 +0200 Subject: [PATCH 086/117] chore(deps): update actions/checkout action to v5 (#216) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- .github/workflows/release.yaml | 2 +- .github/workflows/renovate-checks.yaml | 2 +- .github/workflows/scripts-checks.yaml | 2 +- .github/workflows/snyk.yaml | 2 +- .github/workflows/test.yaml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 15b5d5fa..cd3857da 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -83,7 +83,7 @@ jobs: - name: Checkout Repository if: steps.command.outputs.command-name == 'bump' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index ed9ae69b..00b3fee6 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 46aa2aeb..47b4a725 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -38,7 +38,7 @@ jobs: GO111MODULE: on steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 repository: ${{github.event.pull_request.head.repo.full_name}} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e44886ad..b8d11ba2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 diff --git a/.github/workflows/renovate-checks.yaml b/.github/workflows/renovate-checks.yaml index 3c8a8438..3704a16d 100644 --- a/.github/workflows/renovate-checks.yaml +++ b/.github/workflows/renovate-checks.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest name: Renovate Config Validator steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Validate config # See https://docs.renovatebot.com/config-validation/ run: | diff --git a/.github/workflows/scripts-checks.yaml b/.github/workflows/scripts-checks.yaml index 6b0c546d..9ed9ee3f 100644 --- a/.github/workflows/scripts-checks.yaml +++ b/.github/workflows/scripts-checks.yaml @@ -24,7 +24,7 @@ jobs: steps: - name: Repository checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: # Differential ShellCheck requires full git history fetch-depth: 0 diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index d458ab2c..5f78c14b 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b267f81b..9704984b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: fetch-depth: 0 From 083121adf779d2cafafbc68891521a282a4c228c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 17:18:43 +0200 Subject: [PATCH 087/117] chore(deps): update helm release common to v2.31.4 (#218) Co-authored-by: renovate[bot] Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +++--- charts/backstage/Chart.yaml | 4 ++-- charts/backstage/README.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index c484002d..f415e3c6 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: common repository: https://charts.bitnami.com/bitnami - version: 2.31.3 + version: 2.31.4 - name: backstage repository: https://backstage.github.io/charts version: 2.6.0 -digest: sha256:a37bf409d78606c8fcb1c6eceea39d317a83f0f70f01544d0108fe612704266c -generated: "2025-07-10T00:11:21.86409305Z" +digest: sha256:caad0f40e6eb3c67b9dd08ae7033f4d104140eabbd77d216308d3e0009e0af78 +generated: "2025-08-12T14:49:41.920263351Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index d7a1eb57..d6e07a96 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -22,7 +22,7 @@ dependencies: repository: https://charts.bitnami.com/bitnami tags: - bitnami-common - version: "2.31.3" + version: "2.31.4" - name: backstage repository: https://backstage.github.io/charts version: "2.6.0" @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.3 +version: 4.5.4 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 63941411..1575f8a3 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.3](https://img.shields.io/badge/Version-4.5.3-informational?style=flat-square) +![Version: 4.5.4](https://img.shields.io/badge/Version-4.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -161,7 +161,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| | https://backstage.github.io/charts | upstream(backstage) | 2.6.0 | -| https://charts.bitnami.com/bitnami | common | 2.31.3 | +| https://charts.bitnami.com/bitnami | common | 2.31.4 | ## Values From 7df857ca1a11c8e5d56fec41eb13675715f6129d Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Tue, 19 Aug 2025 12:41:24 +0100 Subject: [PATCH 088/117] chore: add monitoring docs (#220) * add monitoring docs Signed-off-by: Fortune-Ndlovu * clean up links Signed-off-by: Fortune-Ndlovu --------- Signed-off-by: Fortune-Ndlovu --- docs/images/openshift-metrics.png | Bin 0 -> 217899 bytes docs/monitoring.md | 175 ++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 docs/images/openshift-metrics.png create mode 100644 docs/monitoring.md diff --git a/docs/images/openshift-metrics.png b/docs/images/openshift-metrics.png new file mode 100644 index 0000000000000000000000000000000000000000..cd54b89e1b7535adc21e9746c52d8ae9597ef9d9 GIT binary patch literal 217899 zcmeFZbySt@)-|r8QX(J%(o)hOC9w%bBn6a|R*?{-W7CL$Y?P30P(n&TKsu$_AV_z2 zm(uZDob$f#`QG#RoWFnH7~dE^$g|yh-*K&LtvTnK>-Kx3D2<0hj&ttZIXqbzN#%3r zFn^vqhcSV50bZH4wEA%F+|6^clJ`^{&##PPKDpjMgt>t%`k7^yB1?*kgQI>s0>%IC zX>Oj`^A#M26+GM>68W&Pdky3PrJu)5{QWeN`_nsbxcD(I?NiTHwin-Hxb^PM{`N{8 zm635r-2RUh=k3ITby|M+o9E7B-92}K;PttSfB4n$*?H$llAnM4w|{-Y^x|3x)*n9o zuXk|WJLh>Qe^-*|Z;tcNcf!|P{(oQd=jHhS+r9CaB>$P-8zXL!lcv6$)$6R3eLAha zhx>Pd1PeUodj8^ChfCn^Uej>OFBF-*TKk`J)#Xk3Tkx~Bek>;z3%$bp53y(&aqhzI zyl^6`kXrHMEQQ%ii?Tg>_fv5+GEB2nFK;;$OO=b0GTnLex3Ri$8Rw?e>2_#laNVzG zA;0n%4CT9?hNIQ89aOS><+?QK?%4%0KMuK^p~|T=<%k!uD~@a+`TKYsV}{G|uo3<} zd!;g7?ly3v2q|hau$cz?F4-j$+{`6udkow*1m8OjhU*PaplUDFWK4VuU^^l zA{8f{PFcg{c7MZ<#mF^zyJ8jxO{ZtUm*0=xV59xploy6P3sDg-$`ar7-rZB@p?06q z^=sL|Df6^*GS1B(XjMKxj85O?eOBw;63P^GgFDG#b;z_Z2E`Xst(hd1+)3~__KrCZ z4^Ml7Xt~R-1>F=uBI=Eo7n4$gi1k!Gw?U(zkkDq}9%DpA1if@%9N){XnU>IT=VcB$ z=k}5Dp&?;rW@b9N+J=VN2hJyLyxvTIpAN$qJ?CY;^wX=$dS&#i_I(aEN4WYjd!0=S ztB!6K3Tsi1oeb+|Fx6p<-@H&Id16J4D;(*UVD;+MaE{bG>SDzjrLK5k)3w3@JZ`t+{k z7zW>Gh@As<_R+#fdjw-#=_eAgDN;%91? zL$nlF)d)J>oEm$owUp<`JMohA{ah20Gp__$iMt<_5F~y4c$dkNOUL!sLT8;Xz7GMc z%+#kTt?UTj#O2Jb<13?HvjjTG*nxq8=NP;AVtaS7Ha8~gmiu!tF)^1fuUS}FkaFqG zjCjnoMN7UgF=?(_wbzbd@Iy9H;bLJ;P>q&YC&TJXri6IdmHQh0IcuN2Y8i5O@$y=T zoZ*WXFOF)(Z{4~jVo~;Mf!~zB|3jW?mZ)l`7HjX_aMni&`gT~5L$W-q$9l|c9(n|% zM^ctV>bS;Z>=H34TFJOA^<*f(RSf1Ec7IlIa&i)|98;}bHu@36V$c>JAHUp_K}1GI zMoGCcGn1m875;3_v`|Z5KheONmzS4`iOI#qg)WBQOg+!@5H8f|I9t1TsV`ez^77@& z2L}g|Giz(^`&)+Y4m&G@l0N$YjYTW{(A60ILa#u$z$0}Mdx`fo2=txOP$ASsoNSFBf_%t*$ z#EUpV&?ZV(i92pQoo|nK<-N(nvo)XSak5%G?kJ5o;!X!OeBderHf1+lurp^tBt%p{4%)4ZMrc) zC)>w29rNNvW`i~+#W)$s!NEZ#4Hr@ge`?Vf%rv6Mtq~mWIdD3@y9JZ4p|N&{i!F;S z$H%^Z|2{lC+}+*X(voq{>#|0U&g3uG_BbI)yM>>$IpTF}G_B$iC<+0KlJauoa-V`` z2QJ!@Q(a{!)f2soxK`==*G>Gf4a@5BI8S$cbvNNGfuP&e%l#9@`>99dYd3aD1|PE+ zB=2_-GmqA|P1M|bxk$E&?T76r%e5yXrHPBInzK#A&Uw~e(NdE(Lm{hb3A;!W zf#Bdc*JVOk$$;h0R`?7V*9ezV5x$k?mJ58w?j&OXq>qCG`!k+#(xiLq#T)#>#w&mO!jn9Q`aCi5+EA<1u6lM-W&9SNcpTpk`CdbWo<<_}{Ievvhi|0VI( zh)cgNxNwrK`cqBaCMWwy)8T@Jfq?-H4GkL`Te~@WbbsoANKa31b*R|m^!ORCaXY!F z^TVbR-D~qCWE5Myv4wfl9E#2(7=*9iBBaArA&{)+~?zD#WM3w9QRvMzl1uDx&PtWs&mmqO_ zx>P#yMX~~;j=uhWkAU4ff}6#K z1+l^$Mu&m%Z~~U&dAtawVk_&8vAz0~oykeptmH7d?;1|R3AXwnN851}q=PH=ZibV& zV}Vmc_beSA>X&(zYi0LkxBRASO+Dxf=`mF)Jm4+0IJ8ufLtAaRtDS69cd{fYhSW2T zGSZ3ef9mubeXq+G+#eXiaL-Pb`hvJh$PHZqTdr#*mP6nC*6&M6N?yNyed8uFhghZ5 zW)>2#ZA7KbEEe^4j@WlVX8INo)3R>xxn%c{m4)vLDE|NvVBI96V4^|qlM+6mF?3*kHR_OL-YS8eT z+G#mAN^2Jz8W}1jicQ;JT8k^n>RqB1b%vEq`)t^p8q%%O-Ibh_#L>o)K&)iT{KDK^ zH%mR+`S-GhosDyV6CLJYys-UHm4UI1T&645%g3EWTHb{>4#o~){bXBM;6KEcL%e+g{&!)T;;}Q7ee2&XS-e11NwS8G>2~CSAe7Bv- zP-n~mk*s%o;E~(hlyxd`jxahZ%Dd3y<@&@%9O*SeAt52P4CU0Y8?7=0sT3vKVWiGn z(`l;HS^+vwMO9PFB<;2{WFO_C%`BQ3M^zYE{L^bgt#m`A%gy}uzI%=HZzUdVsYt4Z zFQhY!Mr4(pO4<3Qm+EMLo~eoFQlT3vFvd3?gkqh=sYDSQ9Q@r(F+pT7Ri0E_T%3j` z5ekpAq@>6qDd4uvo4?+?d9$!!qM@N7E!_lF_vBZ%($SFdrL|&<(W&!`P%=f-N9*dQ zw`N<9GK1-*cNRLmB8jeD3)@w8brs1~y?}&T(>@Dwn^YiWMsBXgDn0a znLQf15SNwnBrhZ@X61CR=Jk1u<=*UFAWTN{Gw_F6(D5szPrzv7<(3`}ifbo9_-%3g z%lwzuB!4+nzB7A3K0I$p zQE__mm5^R~Q}k_h4_UaJp|P>Cfx$NV%N~RJmnYV}nZ09TVjaAF5nSzn8*_CEhwkPy zKAy?`6?wIk-BJg(_S#Xc9LZ5Jsod|eo5pMLjEgaGrTQn+g2>eNHa8)Mae7&{*%{qb zUeEkBYwJQJhU^lftp2j9Z#8Sj*8{R(sq8)us2C1Md)9&3z%y$iSqOWxwKcG;Pfy%v zCFSIBG|XU=zfJUF?X%0XQOAG;lVtx00A7gVWad+3JPj40<0h8d=`ergx zVx5tZ0R`D(W8%C(R{bwv0z?ub&c7C*Bw${p9H{oFPEMX4-)m-WU8nhl_g87dGvpE~ z9rn$r^RJbzLha%;(UH+n&?|%;mU>dniaB-5A4p1SMa3TO9qcR&X4yvOjF#(JVhrzDf4v>Yt9f>{z^4l9BIdZbP@XjE?2#BIq)85h&cl7f?+Y zZka>=zGWmFiFOK~h!YH;6ct7wU6gImjS8{mvnhgK)w7FZ1?DHaAbAd#afmVUbMT8M zOZ|v>Fc3b?QH=}iI+vs!?WkD){r=l4hZ7ijDhya^C+u4i+3Kz5S2hUgOR@0v6Lp?R zWsE1ebV784z@Q|G`zX3C0+H5J+ktYnk5 zlvUPEx-7}T_%fY=QKqZd#?(WDdf5F~_Kkn^Ts{|!$s!Pmc#^)0bc^c%$V7#*m!h8C~=)^Qp{pR&|(IF z8Xh^{1fu0=bAEEtdby8dZ6JhE9)()&vbxx@9RA|;=coI?uw8cyo^n!Q5K7lm4( zqJ%mW8W&RL*3^EL3g^A>f>Bj!PqD@zyFSI|&!2%mP+qwfe60k;#L+|gbT zZM_&8$);_i0yv*F96dJXz8G3FT2pg3RrzJ17O$->h+)(*Uo4+KwXkR>qffjSg2Plf z)91(wuWvJPv4kAke91+iqazvV3gwWu3%bgA@*ZhFG1sp@CHYVGN(&xV`IOZ;tLE1PVl;xXnznKN;_yazTDR zIj>QhCkAHK;aWLJZ-9Zqb#UGq4phs9v&zcKp6+*eoc$A^8)0E#=cB#Nao6o18v1U9 z&l_+Qd-o~W0OK636m(pr5)O-u)JT;hc30V~<_ioAgd%@>&|iI(Moh%y=S@1E+qV}0 z=vr8;K>^066#NO)`sDb~V-wEM?RPKK*Fr6hY7YikQvkk3MsrYWt3ZLQudl};#(BRP z)>wK!_7A8j?B4lylD;pRvGJj?Au&Y;#(Lvb$K-FlU0pYbuIBHq4eJAn0s(5?Vmuf^ zh1arwnU$qZ7w_s-MOg)v#v(RcY(GDt(&2K4RjpU~-+K)NuT44_btH^dI6cX@tf`b} zF;*B5@T0555CEBWaRcV1OWSb~Bj)YKrE}j$oVE*WZNx`PGHeX1HH6&bc-i#F(aU|7 z6D|~hfytsZ0S7~+rkC=!LL!-x4)DmWMC1Iwtl+;5n#k)?qOoz(sY`1a9W|)+_RD)u z!u6!YY5mb+gZ^p zn7?xP)Un|mu^XF40^iH6xA+2$4IHr|FXhU2R`+XN(sauQZRgtcZy>Cv7^uA!C8?>Y z4I2Hk^>a_!Gz+BtDN|jm?;)=X5nj67M=maAy7VoobbCICoPTIiHr#dwJC|^-(wp{tx@{emF%N?6%tM zx%(_+woktk$vn=l_}q+}OQg%h%2ON9g)=(Lk2c(@jujW~$WUdiYHA42eyE_ZACP(I z*MZF1Gw6Xlfkw;iN1#tFF3N`F;}&XVyHuw)XEThFvRX=5c$?)f+htn?>$k5f8}bJJ ze*f*u@Okz>v}iWwu&rL?xi+A`{qo=Qt_M5}>|DU)r`2vkB?Qj)*Uik{On@B1^t@(p zZGi5+MJ?v0zC|DlArS*(l55XlXJ8=dBRZ2D(Dj)Q6iF}zM`VVPW6=U0o_TrJ!N0yX z%j+E^Rv>Y4-^0qkFfz1!uVm7nQyCf>8W|bs>xU89%H{Oc0;VTS_%&n(-&YXr(T73^--soh-=@9P(@RCmby}mNGZS-hn=0#^4)=o z{badt9Lr*>V=_8Z$Sn}@&!2w=+T}&V&E0L>9>>pNRF)PQnLnE)9>lnwXNLjI!vz@k zXX6)D6ZG`FY@+nt!{uMkiF>$*yQ*^Sv6DWD>{()E?Uj=)<0RFaZ&A$^L3nUo`$Bp+ zPwcdDSj`}-lzno4f@AlIl9H1CNhe2E{dnQzZ@{Bv`b?dsg`hc6zFA&g7UxKImGvHV z+|v3LBo_AHUH}WZVk!t>XrStfj0SFnb#agJOP}hjRt^~PlLu5)iS<9N_0QmP)SR0A zOFH$mVX7hA7_wisEnYvvf5V04`qd}2pFSm09`FH413Am!D{tU2s{iOiT#Q0KtQI7a%C?gG;yuPCu>lnBHh(;oxZHJtxK-k^7*$vXlBv zIaL{t>OgO8hYG|4kV&*EjaDIdIjju$p~&yNDslcX#BbK0tzG>L_KLbgyMJ_ne}Mlx zjznF#QG-;pyZf?$Ycu@_QD_KIQUTYJPU!@ff_rrz?3V>zydX*N0qqEOAgu4=;$kKD zw4k3!PkN_8Q-I2Ii$bj*Nb<}0WVN0yCML-~xCY@hqk|2GgIi~?l03kncZpvuOI__& zl$sP;F^*4&C!fA}ac1V*^I2AE{tgO%a?!cUXLYFNjt)zB>kpC_gi`@YP>lTr4zN6_%Hmr;8yW>h`EPH)}?8_3BkNjqIIY>}^#{3JD-zO={d_ zkmb2^N3ZJngYM5iN~mCS!|@^ND{tS&$4?Y9xRo2ET~q-@tl~?rX(YSmkXFK|hLTc{ zxG?12lg>VKgu|R6Bm={!feKswJYCBtTu`I&|=l{TH1f*W0B<@oHZ+YvB zMES2QEX~bLOq?Cp?Qr8N6&-L-11R@^9GaRHSBHIpo7=GNzN@>axJyYz)t{ps8IiDQ zH_@Kw0tpH{Js33+%FeZQ! z7q2(JN1@pJDn=@$_dsRj*Zpes?D)XPmYMvduxj;o;#GYMHnFB2$Qk z^L2e~-5P<3Nm_**^7ZYA5_)gDpK5LWJF$&1qthgb=^JcV!I+S>yyHl!$oe65V}`6v}d`dvx;rJiGxMFq}Vo*45qL z-+%Dnfvp;iJ1g|(Hhz!RJW|}0*POUGSYQ95b1D_~)Xeh(k(eTriLrDmZ-ml&ImvdL ziM5%TdZEJ5(C%SNf%X2})0o0#Is{3eX}@Vtbgp+{c)1tfhP-uq-XJEA#A#DA(qrm84%HRu0AMUI2qUD4DdRYHxHq# zI?U|FP|d_=TomlYNOIX?&j5y^mc-`aYNfVowsq%{B$MBmTE?&0i~8FuZotJjH)_Wd z9-XLi78+N7vrC+OClKeq&9nPxFpEbwGyik*1seHaj7d$dFrpR@@#2L5hX_l38XPhM;DE4(z6ja-gpfes0is&y8|8F(iQ{e7vqQ;UE z!*Yl9AE0J{u67ElTTh1VL=6*AF8FgOF7~LG8aiw$8seiCHd7Pa|K9(}~s zv;A8^=3yH&VR_kN-)lf!-}36W@`VCv@;hbeQsPZ9DGKf3eO!_|=6$Lezds=c$FfAi zM`%b%`@K_U{M*bi_t!&MkJgO)}gJi%-mb@ox~H=LxY3aT9vn; zxI(Ej)jBA%KV`c#R$$DvwgW61su=QcM@>R|R0_S$Ls+?c@-=+{TaLUDQ$`X46Yt>Z zo)sMq4vt>ALnY)jOBK*n0r-X0=$1A@=bob6F;6P%afYhOXT|BsNf9wOD05K2!#Tn) z>FbX_cu%UWr3K*HJ9r$I->grk;zdTD>?FfQ$rom3#f~k?2n2%eU0}lP7tI8tAv~=S zxfA4TokX`B7P}r`j~G1vyp4sds)^tXz`e>DS6i%mxWnCzJOA`-K8ZrIc;?z_v7be2tH8|uV-MPcD3d5THCgxl#6a_T zZDH4YY@yQIiLT{Sy_A;0l_rH+qkd|o;z#a}6!EInMFR6g<^wJ!6-e$xHRem1@;NO{B5 ztsA?|?gxb%b$AkX;+_OsK4iEhKuPYvg6f@ssS~sbY)w0GO11 zFP#7dQ@f=wYeS?IbcIc{vi3+$nT(90KU<^P>B*CB=mAw8J3M(G5fIDgw4B{&S=lSd z{^dbI@wmm#4&)8Xu@YGo5NKqLBks}VfBU9W=={UmFH}TS)C?`|=~-A&S^55`*kO@M zA~+&?w7c7OCr)UK!yDQ;BhY}cu_-owVKi$op7(q<6FL}`jW=Um7S`7jfkHO=HzjK95)aXlPr6{3V zwwC>mvftoLpwn^H1F}%^YX4o4`8f9-?!4k+8a_uWQ|GsZh5Uq0E-oI-q*ptYFRo<*I)!sc)iGEWKl+fhV7Joq@_X%*tJwWH`ZPo78x*vZ zzKTF&vP7h+Wb*5=3$o*NpN%@mvL2G&hoEhc`X+aKSA!J&=JSrMInw!Bl|1;Wv{ti~ z`BgH?GF9Ce7jTT(zR}F2ta5Sb9Oc$ z5vb;&A?Kd-$7l|MvG;PR!0)Yr@BqvZaz#2gz_x;Ztm^5>F$JH=#OkW~b7euVo#lh- z>m3=MKmOgRHqnc<;c?i+p`Q>Q4?HE543g&eyukYV*kht6~)r3DlwXx{U9e6K~JWOB;%AiIM@FeW-$ z6gMI_4%(F+ar|a*ZP07hF0>GJ7;$W0@Jk0Z>LqtKDVHWWvFN9l-A44%0S?yoj7e3; z2fjXqkH#uMc<+thcnT-1!?Bdz7~p$_3_P1rdDEsn>7OpHxgGwVzPI@Ry8WMOtyM~! zr_S6+L9y?M*{VRogZg>!8?2i&@HPrTlb?vyp-sszhLgG}!P4c+E!9GI=Y`khaB|CQ z?hgrNa0!{uD&Jcs5(*A*8Y&ji#LSNQw;&$Y+Hu}0sMa#4%9?v<}su`x3{?rl5+8vW!+ zl&7a>{&HkV&2<2NrC`UjvB}@U-nxc-ET^!U3Oqucw%+4pn+9~_)Hq5)=;*3r5fTv8 zNY^uE!39a6pz|*S7SmK^Yg};c`GVy7k3PMpdKMKJCP4YJfSWvMoJb{P!vMBMQ074f z_d}s>b93LD(~~nM`XTYZ4Ykbr7eW{=UAk0k+A9n>f4R@RqM`y?Kp?dflk_DSwyFb( z;O6Ge&>O7=n&V@pRk*eJ+f5kHk>9t?-mdgTPF~)oe>5+0ZdI##@;;n6v_||*^Dfy7 z%{m_JW-aa%?9@656RM>@`s8eX+87Xf@p4~&LBaG8My`PQyqIm*b3?=4>eJURyPrpJ z=;v9~`EJksNEEbIhPLFsLqqrAazS7*W%fMnu+7!vj$m9G_VJZT*XYRb!@F^hY%lJ< zxLz95N@8u-nATfU^E!6QQ96Xd@FxQ%zgz*M?WTKazKH(7kxlC5w-%aL-Q^Z(rAx{@ zY>I`Z=KCDi8!WcJ1QH+ix%4&WBHdnIT64|*wKB+N*}pcjiqPB01KDZ4lLW4^nr6T2&U zi<^bL*@Lx5Q&m5IZ(=KI;m*s}iq6?LZ(DY1E>Mp05#PIT;R2_A6}$J7D%Ufk*8ThU z>+0%~laoR8;qcy@i?u9C=~){s_aOia*DUpxi&TcBk|dZmpx}u*|C-nVRcpRo0tL$W zMx#!EH>%8Tks{jfJJU;q_=-L@2=+nX|6>T^zj5bP6agJ4XZw#IKfw4pGExB;q3$gn!gkJ|An9dy zZIb;1i-?6CoYsHWk2X38e<~#HS6A8hFG#(6aG|7MhJkQ!U|_F5eR5?gNj*bu4g0jX zVok)jVUCwkKv}K>O;4&OJmEtu#K#zf&5@dOz5mNKQ3^6LAp`=NPfxV9ZJ+48b=K7B z(6^#$5|f*%&tAMNUE#T<9W9$TK*~Bv?P32~yL(jmyQ)oE$f5pBVOgr<5(0a)(ZrcMd z0(FGi=gjTIcb)LY&6G1#bwuKaXQJ$fzP*${0cG1tJR;+e4Jj=A4JI6DzjT0bKS=Mr zz~Euo0DV9-8a+^A<#36ydd`DC+?igoryOZXEBX9+l8sqlkT4yc1Qs`V6UUw)zwi-8 z$HwB)_T}p5jx?!!fj&T8ZSCIWa~^#QJDQ%#ibP01dZVh-Muo+-V1c>c=u?h~6zyn9 z)%o%4+6O005&BP3FiZ=}WGbmrdF8Bfhsnp^sft;YP`G}-eU;|T8fKz}7g*KTrP`x; z=be)DK zR=Ootv`kFCTokkV(scRx`KH4~+0a2*j-mYAJUOlO@BK)})eEp250u;m6I`}5CQofH zq<>+VthUmh_1y3f-1hjycB*#5irC2CX~^?e*N##%DRh>T+s+^I*mM93o;2f^ANy)8 zUbyC06#%e_I`0gfZhC^Hta8EtI3907 zzNm<(5P>Vb5;kGBezhLf=4_hvJ1_uX!`b4X7{jtq6MO`5)kKIuu0#(Tyih)%*LLH^4ZEeDm*_AS5&r{l{(;~02&~o+4M6%3VC(btzDQf2 zmgr7SOw8|b;!5rM(K?tO=gK{faEgoTG4UPOP|G;xy+0m=0S5mCX5mJo2c z*Yx$N7ssGK-t(%ht78Lz4H!yE{P@fbb^iQ$+qpJtE2~Fz_KL-z;5P=*&`?r-=`?}z z+?j7EZ{eG~j}B)Gq7dNZaQ3mFFf`H-RdUQBhUC8q7~mX9kPNECT02&I>V3 z=v#mK^a;LHv*^_zz!=AGRa^1rfG%hj7!3{$6`A#~)!~bu85|FOFPD-(4DQ=g3hd$F z;0U4=3WR)?ETqX-`-in%E!is>dx1po|&ZZNAhc-^+Z z=Ibhes(x0p?YOYKynX64H5wkjiPaGEl=0tL+}#PMi($bP9R=J)PWR6D&23;lTf==m zHJhvP@!JJtJ2Mp}6^K$007I1%$slF^{3+$Ewc0%$sLwUtI)sSqE`!`kkOcN#w3TJ5 zTRZo-`=L4KYWp^}m1a0B>X|L%yB8(^hMBG<$kRHPz4Z+Z3_?GAc=YJeP49_PSswx@ zyu9EwW{}m__kiCeP;f0{<;ZmaOBGg=Z_efyCPtCtvodbrd5}PHRqZ7KBt$dY=ai>h zm#0mFJ{=CN`~GYsBpr|A-~H7`Te!OpfY?VAq2K9E0NS{bk&(#LQvmxFcwFo{B^zG& zVgwRkg4wn@fe%5d`J#FM%>D1G3l=VTeSNq%XnuZ%@Zf>PVSFg_9Taf(qyI=nlTs6lB%efti;)&7GgW$-AWhA|Ry)98(-><>Rd#~09 z7o4B-v$KZJo>@=U`V^Y=Z^7{{=jG*{d7{C}+TGRVzBh?UE2*xo4$-uq&h#(}%DIr zNFjI*^aM=r;Of=Mf6)SwZ3jl7s#girmuiTfZ)8bGNbGe9JFGm>(u(1^mWU$Z($jhL zsD{^w;IX1&d-ch0E;_k~4}n)ow1so%bdH^V3;6_VMxY@~VsCHh@>S^kH}dhj4#yFHYEA4h!5R+0d?2 ziWe?~(2ZN%zJfhc2{;Fv)*aH}Erl4>^hc2EpMh~$0tGNfEmtodHB@BYdg%pZLg?y2 zO@j5Q;UA%*q=fFVDQtB^!`a2f7Q$0ld0pKz8zTB}$~&g?GCnoCcZuWX6rAW~Jn|m+ z3p8g|AH$M6xP%S$^{_+IJx))+f(LGmu`w~QW{ZnM7c!RX!Q8ud@1SKem;06Qv7X)- zOmH~OsU_e-F`WW-E7CVSGz6)qTRwt)l7xi)$xZJkkd(v2!{t(WczJQLvG=FmbM2X3 zgm&mPiMA;2y83!apuDxUZ>pPqraz{i)y8tQd|iFJ&~m)0z_xB>sLV_8Zgj>%M`UX478-2L+g4ww}K?Vw80W zrcq#|km}wZdtlLE{Ws%xA1``#;f=hH=Xrzs`lxsO!G(ouS2IRL8(!MO@QOD9I4r`I zwIIo;bEU$ws>)qmJ%S*~xFaDh2X^ZkbXLJ!2ZJK+;C;hSJwpdj zhF+PPq8D#tu8o%2tFV3`AVhxFuXdNbPEF0}Ed!qU@4w~ncA-+a`zh1L7sv4$U!{pr z9`#Xpk_=jhzUyYgVt1Oy!D1Q=oj zj5^)HTKPH*r6ix*!X5thQU$i{>ylt_sRE#J&|t$N2oj1uh9J>cn;)a6L%Y z7rLR+p;ae=Rh9MWP;s&@Lp?i&Cz@luxvy`hF@S>T*29~0oJ!Zp$(`57D#6u3@K{o^ z9()jPuC9AFuqXSLdb9kR!C(p|FEH?nt;e9Cg2T0qou2Ggrzyq*dFX=45cqUC$bvOB zHFRkYcb_-@MRoiyx9OPbSs;w77Dl@0#CgKXKmmZ{2?|_llwUsmSR>p5$ej{c329+X z>eABEw&%_UD3Zf{@hEP2Ph5{}ZAkh28QQR$mAhjUP<8LGzZg%hJ=({ZZ9$)28)Fs%B%$LX!K!S!$Yy7jr+9q zFi{p>Bg(?k^7H2h`b97u;m|tHlmrJ8D(%WiLWmGa)HgQTFp`ZDL@V6z?lvbP3Z0mM zlg0D^r##pytL+w*VdMFkY|XStpx~&GF^Wt&>Dbx7z>S2g1Z=w!fcu&FcV@kY*{#{D zY}={?oq)LjawlJrfR=`!>)!48eyAl7ySk+|jD#>Rw+dkQ_foG4>zK)`rp(@e)4(2x z!{h=MZB!<`nQFj(z{=@zd|;pRs?N93w*Z!>L+sGM+Tmjx?S0Ai_VzQgzH1z2g^QCD z%O!IZYQ3)~Q*C*X%@+mf=R;zm_|ux#;4b^;q>-<;_Yv5PVd!k;z(jIpVZjk#BS;0; zIwAZZ-kD~1YM>%l!-lMKvW4@4?6_fH>ozxh0~QoydeFSqA$B;SUT;j^uM+);7||<@ z%KOL6>as-(;=jEBe`?37o%3{w7Az3ipPy}w^dS(biC&Ioj-Ni)KR$j6HdP6%Ky@e1 zZa*R>n{()JSrlMJDV;PQGb>weJI?T&qq&eZ7}rs~WwzRxjM-F3nF1Y2HLULe+ii)E z5X-oW8DvjRPGFezx&*lNP^hbvf;YVvx>9ka8!Nc`vpVrcoV*d@!_^)h|8VA3OA2{& z>XyMoL*C2swJUQX_gR8|SVAxXGBRq9la+)NPIxxna#zUIX9~cbh|V#)52K*z%q!fOJvVlep#V_4;(p+p+rG}=kW_fhZOIjdt*i#XOGZ1gvaHlS6IG(?IG$HI zYc7HMbmkl^X1h+p{s&+9*~(4)Ac5&=^>!{o4(%d;ww#RxOJJDPo7u2EAK&urhyBMR zAfR!c|ySsGO5TPnjioL@DBa-VL%)CA@b1Fu7q~>)tKH=lGb{95vmRi=_{JinAXP zCu@YcxYhwkfi+9yce=9Q7+8-!etd1Vwhf}crwj0}#H(Ph1=2DAsT5(qG&4E*I8`nj zuKsX;AEsU}VPfiocmw;^HOmX7o}=RgV|XMST4#^~$UnQ1WKK?w1WL^9ASE@GO4v?D zvqPWjsmIlEyen5=(NW9*WkEi{8nNfn`jgiS*6WDqY!u(Gpv@^OHg*_R-7!!>QNDCf z5Hk{HO#d;|W2=Bq_wN0BNNe@=Rs?MgqW%FedGUyow9X*0`glh>0U74y5-1m5^Ub>^ z-niZW-8-mti7|XHYmZf(A4o#dCzaSR(pj5h47Ae!`t%;mV~%)|^P7IruTEHw%IYG@i78D!k`pc`6M!j9OiM{M0x?<}tE8Z&&gnTfNw^6dgiO%tDcB;ZsHniX zD7HUCL@TMRe61;7@*6l;Y3pI<09jsHU1ihAp(G{_kBWlRY2~)Y_B;dxsi2@x`UCPa z0}~SfGAJ#dplbz_K=YHUP&H<;?+CfVED=;BJ*@AAW_C;pv&_+1apJnlP?7Ec5rY|a zX6AHg(}Vvl?sU1EamLv;sk+`ksVi z?-1w67=lYj`K?)r?a>cLLq~91?7=;C+|d|S<~@1^K-rss#&zR7<{GrBTvy?nl~_3I zC*Un45@(ZC%}BaTS?MN)Y!r0kWafj$mbd*XmtUDprNf+~ zzQ-{?>0K^nW86Js5)w*0_HHg%es4RWG|gLqm?Y0Yyk3b}$W62!weH z@I+f%Ta7PI@~o$&6bGP%QPKgP|HDaNns*o1L`)M*Qm zYpU?@p|XmKQ|&MeWdf%MIcXb;ApVsrGo~UV%@E4K9!j6py#=ZJ2!_t*jP2*I&lar1 ztN`R>xQ$T!ZnLl~##&Z^?H>fFG%qgBlDABxQF*iFVM{APF7SUqcf}BmL36(%>_Q*b{004kz6z=NhfPerH(ZJj946qBf{}IR&XJ8wmX?b$;6T?>k z?agg%s%Q^e1k6VOO_qA}h=34!GPAR@cr;>hZ0$skVMp88*a*^Tn10;G#kg?c8WB+t z6=DZc*-|@KW|%8jE&#XzARR3R;pcdF9C$F0YBho8Im$uU7a-V(!x_WWv%}hmkU`@W zXd)@b`K3ebKjB?^)nL`4Z4!cU|Nv>*!^23MzkWIRj=%6vA4T%;|U3$3umtAZE)za<;{$a3_8qij zzMOdYrK{aeyn&hFXo8XE?Y=Jh`$uYoiJ!l?HoG3j@}Lb+xhrfkfUy-*HI+IcpSu74 zz;c?fJ6SLywYKGaEK;K!Ggfi%{$P6l?=!7Q#DxbZ*(URp`y@bgRh_V9jp-Rt{p9`| z?F%rlAw2KPMN<%W0pl;y>%TgpVJ>H9e{y{ao^*TMDw<9L_gc!rbgAMyYG*4%uwb_t;0p$ZD)KOHasCLE7F?$WP6xMKh3 zQqYxW5#fPjeeORUkK_z$`V_nt`_qN}*LU45J$v}lniB2Re_hRA-;RC*$IG`O`HA`W z3q!!Xdj`0US?bUK^$Y%I8SZKl!0|}hFC6^+!koJ>s|Sl*D()kA?mrz5_w0DJns;gb z=iC3fMjuBY!nk(R2r$oiDii-S;Y%G7Bme7@F#d7IH5BxB_dNgl9skFjnP9{k7!aN~ z4|i`u^MYDduVbIe6a4>Kmw!8v1)L8BS(wykYm~r9FqGa>kWlOVcUN>*<29?WMlA*; zftrGgOTGAR|M{zLS~VSC#=85F!SetQ)_ErR(COWuysT>*gjFY$j;&S zqX>I}nm>zfH56w%Jtch;Pw>0oMXZXCcT2HGsNzRfsC4xsdT@*qGA&+ zJzm76iW~XzijtDGOt`c}dsui#NRZz4RE6)=ANLVD8CLwi?qMo<$uOeqiPoU@prF<; zi5PqfJL`bfpx{v|zKZn!$KG2Y(V zLZqcT6cLb)36j#?NY{5w-~E1jkG)yvod3@rV~sU*EtqjXcU;%6ZWh5Ic>@CpU#=)4 zQ4MDo?>CEyqVu!Uwkwm5>tyoF*KRy!_jtQ`mxatNFu1^A!gp=t!GT7Bsny0n@vf-L zN}%qjNWHAxc@(}tz=bQv-2^7d!(d9oVT;B$^eU#9@@)2QuDhR$9dI^!wj@zbL8asq zsKgE(CL6zW>DP>oh>DxhuIaY*|IGWEdVOPV;TL|M{=OadFs;E`{V}nJue)57BdnN| z(HVC3C(b7m5a%^ff#G%O?IoTotIJPqEk=tqN9o?2t7O^U)APy##Lau`z})8^FMQEz zoBD&|=#UyKrfws;?&XP)4PwrVcdJE+^m{OQ?GjE5ql&y=O-Mpk=3D)4gN-xSIr$Vf z?E9|EBiRr44M3M4rVO5r@Yk!>CNq<(%U?*k;X@|riyuU{w>6J9cdF~mtS+m58L8LV zdB?7mzqOm&h15-aJO)1!1LXg9Ol(c1hn91J#v_W4kkHjR0*->GoO`-OyR+_D zu7{T72G(R1`b7?J5~PRIoqvc5RF+fN%FdzxrT858HoU-kUGywlzs0&mKhznX8u4iA2rDt>O(`d5itVSZgS{FC{%E=*sV zLH(iA!-U|jx3dmgdos-LWSB`!w<)^@*&mHY%Nx>K+%j8ybBlRiOXfQ%vndHo_ZZf( zVPyJeexfT3L|4?E*r`NW$OM%ch(eb6(YOq%e`e^AM}D8^ZSLxA?)jD`o9P0RG32tV zfUDB{JJ*l%TDm?nQw&$o-C!=?tNLC9*1HU7%u8Uv)eM^5yTEzc%&hH6uA?*`pXcI)-z+shE>0!Td!&9@Mqrz06 zt5Z@cLxKLf@*v01ZKp1YwfX8bPtnqLjQkkv?QqSXY2l_q}vv{lH^B z_dBk<>?7mMo5&ok+_*K>Z27XDO<5(CTy*l>pa@ZHTCi=BD!!v{sZGCBwdi?{t*X>r^l zdW27IU4-r6Z&$zLJCj5k z#_CUZ0Q>NDJg-a{gMhSi=at@vxmmTMA10if#V$U!H=)AoX0!Z#dkPoha-P&W*7{W{ zppwRAx9mu_*;jM7uJr6g9ri1S-_on!-WBv2Ew@6EFwFYWT{3?1;TwNX2%gz`0b3ZPXo-NMke z*$-s@DYh0t#I#d;cqLJ30a&A?b-HciGyhjn(>W|ZDw zGDzTYIf4)X^wRH@2eS#1XFu!jW!;sa%=eqW&wrg;vrOoO|NE!+Mu60~EESU&Ao^p; zGy|fEk;!H2Re2+5rQZv089Xj^mwRe>wKLf65Tn$mp$V|5{5MaM{LT<|#Os1jdUjU? z-BpoPai~~}pOtOO&u*)XCA>OW9<)0c58DVPQD)>Fdk{u@to0PZ&`hPU3J6Y1SW2oO zPd7znO>pl{v7eDySMrRr$VL?cMzv$u36FVD@0FF~F*@$rMJk}? z4iR+7*O>(c1D1K1sa5JQ<>E@=%J@QhiCIGrMO^hRrSBn9`_A<#g2*Bu9}`4uWNP;4 zelHH!JX~-Ty zj|?;v`6Qj*u{*hCu9eKcH%}%*<}FMMv;EV8ul{%4ql8vPRw?<2Z};e`T$#ny(ur@& zVR$+Z2Hs|2FX~lzoOi^P+__GWm}j z7_Ug!lihch%c^!rf79J||GBApFEET;-1PDJr57Yf0prEZ7a-&a%QGs+je4aK7OR;M zIZYU)27sn+rlm{%1WDzUC>`$6l7NANM*Rmq4(pF0pJi9(yR`%HHq!YZb%)F_kynp~ zalXKyqziUo*hix>fB1Nv$~@j;d>2bnqHmU(tC8<;*k|H4d6pXBb)7B@NxM2zs8?i! zoMw86$1Jcim3%0ptYwo){&f8W(`kV#mE13n@3`Zt(Sr*PcxVW+2&E>I)dvnVVt$gN z6=t9|#sX<|I}EHqC;z*@8Jp$P>?PsN4j0U*?>sFqVq-2>kF{OsHcTat#27AYnBsLB zTpjn?KRj4j7BoKr`CC-8c6XBK(1&NSLk3K(2PfBB5M($ovNbrBGMwHh&ePBkZN{+~EjtI@(h3X* zoYasI>-03`sI%|GAY)R@o|+@#C|<>H~2<$@k|*5b`fUE`_UtFIy4VM`f!Z(f}n-ahOF(}C#hbOVth>gEfyCPdLxI&Z0Y8~wO)Wo+lH z@-F^GZEhnw+w(>mKT!<&h-j%>nCt$=uFAS@F9RM(6tCiP<(OJMk$n`Ix1koA`hIQh zrbdD4PSWR1S)$H|!J2l)z#eWllP%o1@ytSNR{E{c%h#>%m-&}tIQ7~r@lD<1#_BD! zh#A4`p|Kn9Vif$nKQC7#EaQ>zok(JFn`S>Yv+Cq!a z;>Ioqms!}5>M;`) z&l&DT74UZ1f7!YzD<#jzqsfrIr`QvK17lu^wLf{Ga(tK|edVI%3kZ!n7C>;q(qY|y z1?$sq2IJ0dH6{_o!2GGO7@D9Zzq?#oYjh?=bMY+qhD`8(PY7E4G0ZmW451`HQx9RQ zvVCu7G8yfQrqN#*1{D8#&%zFq??FX zP!N$*pJ_DHp-L4BMA#QDsSUCxI6ZN!GAbPcaz(RPf~M{B0XIiFvY@-zS$cJ^;vc?A zZDR<|m>dUu`gjte=st)8t@dai%!nc6;1ch>@1m;Nu~(6cw4O+F*&)sUWi^pwa4Hkj zW+4;&OlT?F|2WY{diX1@{1f|w&r=Lg9R0SP5vHTuxJ%y+ZFF}@EEdg+3(Z`=H(NL}r+azH6@9LyrlA6b*;y%Jrn#20piw5dYHn63R{WevpQfrn ze_pFSAq%SrL%LJLTYmDl#OzIS##~A&#cR#m!Qb^sSXOS~r-&M`38B?~;z_(;67CMC zkt5CDl?{&()HD}PTY5U&SuND`4kpkD@%i4N1I!%_wkm&Ofw4N?YDusebs-mW zZ?%KiC#OMc}GhKJoS>rbck+m5J=p3EeiDHP26eN6g;BE)yIc zqRgRPV5FhI&Cu-NpZ0#OsF{$sWZX&47)h$fF=2hxSg7s(mF8d;@D{P%WLB~>z8{CN z-n>sIB!%WCSF{|e0EH{m`hC3e?KxrI;*GfS=I$|_1UG)XEBzg1vBX3I+20<7sUgM9 zPo}NkM5vkk$c$xW`tdH$OW6ObMo79sRj_nguxUc_#u8&PvDBqp&K?5dqs^I21pAA| zy1(AYB>n@6SAsh$j~W%CPQA4|plm${C<}U?zLNbw=6Ej6>JxBsz~5tHq9!t}_6rNW zPeGU7k)&Iw$KQ_h15Zt<_(Mo|x=@tZS4I zHQn{GQ`dYqCU1mR!m3uy9-~!TeG8Wj;;D2hY)?U|1KhbHEfA+8!0A2kIWDO^A-n?< z?!GrQ<;4>Yf$J`5@1}h>_G`$uJcGJBx!Ndye&m)J`eBS7{F7Bm6^92?Zs;UB6;xMIoMy212 zK=`@6B)1jp!26HZzUWwu&lWnonDBh~oLE9F-;dX{3$mL>1;->o^f3;46T9)Ft*@_k z%2oz^W(xxVmyf|*U0ly58_BZgOoryE+Z*G=EW5=E(j$wDp1 z*&~E=v*$z8#-L5(Wk-ASmtVf^F7HeY=8r1yr9{1R_`O&}NMUk<_IoRc4Sd8mRf|K1 zmxb&%pQL|RROF+h)_rO8a>7SqUawV*z#)XO&V)3%{D)!dLV`CC+Z#!&DzKv-UY0_8A^JGx3NX{j2 zEBn0-%BW#jb&vzZW3pVjMZ|mymgL+IbFEAe)EX#9uEPwALY#=rc1$+>VXWa? zYKFP<%CTijD2=4Wwgy9UTh7|t$4^{>M&El&a4Si#|4sZZ7C!y<>3+e|8iw{TKf2-+%P;XZsr}pO@i}amVE53wU`PnxkPm zgL@KE;35?(fRFZn`9KJ}AHYC-ES+(p8{Lw=zsfmF-0v25P3rFcj$C>=NC9XdKb)Id zHc>_{-H~S6d5_B=yaIX)OV4a&$mKOicU6=342m-8PTO+KBMUsmgtDJ? zUdmAI9PLs80CRqnP!Girh;nlE8dM*9JGQCK$yb(lvr@*k;>tT)Z=qvfd&jE7EM*0& zO1(u%ACFm!d$cV$8_;?h9%uLKbe#a~f9!s5ks;Jo&}qc zpopbv2>BxL)}@=+mKgp-X4&W1yfmbf`Di!F2<{O zUj*X$lzeU|YrR^1o6VB9gR}xf8$P z2K!9|ur3Z06=R)?gR9FBJBpaSGgwPc1@IT5(t$Yg}fzkTIc`D!IXx zv!r|y4L~S;Tw8|zyJ#k)=jP-G1mfHWfbygq-fYm*Ttje*DFv*Yh zs18~{%0(+S0KlXk`c>uv=eTW{e+lAVwl4w{MO@ZFO}_^*JroMofHX&EDVfCFe8}x@ zR9>iGV+&~3E+wDs_}(e{F@7jDfBY05&wcP=LD-qykwFN)h^vJ6Vl=GmzC2dh zg6VBIWJKVWl9o66%lzi0=Dkt9&%Q(Fc)JMVhajKPZ{jt@`h09M!$Rk^2t$hECsMRa z*GUySJ!tT1uUxo>H!okoK>=g(Vq409KN%;Bb_Op|WGW|$dCYCUjmm7N0KraP#>XF$ zT3Q4qSGaOW^?IOe=!PUf*y7>iP>Q;K_2SgcCdHN;9VfBoAZCDrNa6i$i_ZA6D`cFu z6KQ3`gsmYrp1Y|NyyTOsOG{oseJf_lZT}C9a$^$|k*6=$8*M zh_`zW4win)1azeC^D)8sMRLlIMbwGCsGy4uJx)NhH~McKmVt-CZ>Ja={yKfJwo5#m zOhnx97kZQ2Y)ZjBt1y}1ehR3_nSUY46;>lYG?{fIYW~zFN`>5b=54J(hHAr%P~zCR zO4201-etCoHCk4&wqsOVdB%S+&H`YX+@yn_SHSVY6#bbr#yX>u;CrF`WY2)oB-w1z zz+uXR{iYA^*YH~gql`2;uj!QKkb0G0WgIpt7h)WaLY{7{*#r-liF-VZ;mm2CvKTv6 z9tzGz*gYdK3HkEv4W+m%a1?kf@%er^&B&BI&YJU{M&!r5k>c__HwM;EXnx)d0Dz1Z z3S100(5u*E&{9-_?<%7j0qWUelXWgudlpR|$4Au25bf6Jw+m_sY1Z7m-dVTwkMYTk zjb4rkC6al)`AEN+2;Vz}=sHyZoyG0U~9v z%@mui!w0D=ZsCn>@kqWB>T`Y5A6~8Di9Pkj_qWk+;22*WlVgelaUfK4=BO}v5K7*D z0@p-o_Vx9N#fhTro8gbCDll(^di`eH2V8U^nN#;-a=O$U9yw6wI?q}|o&+}YvW z8RVR>Fd&XYNm-et8RQ|7UX#f!foRp(mkNQ6+rP{}c`;Yl8V;}JMwkuxq8~FdBBa&Z zqVrZh-uM>@KMMejxTG&(YWKzPj{$+ZkbO60q2vj*pO=zqy7nzxN0=d{Qq@mBKYm?ASD8n z;kpvP`@y3QN6-zRUyVD4Ui_65_?!$TrWXN`;>BLXSuXgXuX_QWvO_^ehAEd8IWKHh z{`ES%mcOB@QUAO3{{`4WX^s92Y-0jy$)4AT{)1g$I~(9fJDIRU=f6`&|K3c4zi}C* zv0Xe5PMH1!FPv0F4etu?Uv;l8#JB#u4!o*C?QQeRXoni0u9B#;Ulh3Tn>FsBI8~J9 z#9bYwkopzdi+14;N=bm8YQ6Ilcgi)iKgcfpON<%ouVQyaAady6A{uu>~7 zo(l%Ve_pjWgbYjZ4t)yhF=UZl8^3rWy8HTNT^sXIZT=%zn}PR`Jolma2Qkg8I_ig^ zG2B9ROPjKO-)2h(O7RRdc9yOZ{KFeOZ`{EVbD6yLgH#L!zKJQ(bF-~^*vkDRyAUn- z3++&QG8=ur1sU16g8F{$s?u_CC&wPolmB5`&1zJ~Uhno0TZI9y9f8?lxV92f@((yA zs7`Byv1w{yjK%UBAL0V~dNWr5X{6oh+H4(5Y}GYRY7*k*6=+nRKJDG5<$m#poNR+N z@P}rqW!vTf zB*|-kjcYbnwPTNZJ<9!`u6I>tecm{ppgKd8_KX*&_A43}y?rck?w4<*L7{NLUeXS? zBv^;sM3Mw;y@%FMugL`O8m|XiUg@C(HHo~y&$F#JU+Z--)(bbEf6KmT8gQcQ>$r)51xwlO#A5KvJ#kGx&*K(nb(nArp zs3>L}DcGW_C@Y~tGi`2NOk3zmaa&m0N1U!zKot%b^z5YRwz5ouN`+rUT}2jPq!kG2 z59(jKJeH)i0I>US85y-aM*BkT1_BSCKz92fUF$o=mJB9ltvBR|QmO5wD?`CiCEzGA&Dr&hno`s%=p`~!h@ChzKZ*qnNe`#>szUqCsaHY6>w1z{wbP>f zexSPZn7etJ-%~z@Q>V9>|25*j<&pgdDkk>gU#Duf{iV-WFbud+L)NC74Xr%Or6Z^> z*aG&6^*!FJa*y(f|3DdXU{=vfQ)Ra%I|)K`A3Z2Aqc zHykw2sn?$;d&5Gmh$oSloLJ!5d|t}N*cQjYvhI82Qtk0dA^4YUhI8^Q%XiXN-rbe| zb-#twu+)7?c|ARvUl$e(>`{B`zYMeG`5Brc`}k&wK`BMj<{**;cNa=JAAIg#V$__6 zkMs04PgZy0^^I%PVtjg&x2mI0U*F&c?-S!_t$rQpl5jhFo#g5G%=i0GF|-_{w&>!DNwT~G=03_8{|SERdYo_|f^(U`|hn}Ot5 zZ55y`B<$K7qligxXYY?Y?BPqEs1)2aL2rh}D{^|8NL?xAY|Llu7$gFQknbVkc6MTS zeu!^z4oNl&vFZIL1F0B|B(~Gl<$fY3|aO|eM}Ej zSmPR%lpe=$$^@_U6!8wzI~}(q(u0tsWEvD@;)aYTn@#vx$|)%Kp?IM}GLN{`Rvb0? z7U_kxO(PN19s8yO2QL?Y;-M9OMiKPImi-iqrW<7^a4jlmslb+>{%l5eX^;9gNy)h3 zv&EgKhJ;QV!zF@@9DQ6;h7BLs?O802D}EVlAaj)?Kc!{xDOPR26|k95c&Ab4w$T7n z8CqR8rZ{^;XI=e zb#Wl8a8Ptq1N^sCCM0@$IZ%W1RovN=6` z)?=aHey1ROKbjZ41icKrbA&7KQPk=j1}nGBm6mgF11WSE6#X%x^auh*u0NIjE%Q;a zW^eW_fYMY7`?e7d|JDKSZ_r(#w6P2c@`gG5`LLQ^YGgrPro|AFyLz8iRy2`*@Q5buSf;`n6-4vt5~~clo1{wO(Jp zwTYlVyX_zo%&S%#L?EQfR8(2B9!5<}=sT-E^m`BLAsPxzJ}Ij-onF~3nP8~RagH4vFQ>q+X3u63w zS~i{x{lKumLEW_E4EF_c+^l{do?;asinLx-a=vhqw@`yiqO z3R2^P8giQ?*PWK+rY5IeeP2BP3aonI%$IA`yHSb&#{qq{?@P|i%vm1HpS?Vpas|?Z z5`?zC)E+~k_MufMsa|U2bl=06de=27DBqCHAVFrmk*~8VoKYGep*avR9S!T6fVV#>Fcy4%Nr@b5{!am_z#Dddlqksj+3d^;c3VOr;~qhL@pMyMlg4>>eQ{L-5Pl(dy4xxS&#u6`cT%$L&q!?8;@LJ zBn5CJ*IzCLPvM47WwMdK|Zy?|>+zWTByxjFk;Q#qc|3~8j+l^>di(K&9bY=*n z;d*aLc+j+yY_Oc>yT;VdL@cx%tVgQ9&ftUD8LjY}{|*Pc?)F>TU#_o?;bsn&-C;#O z!z%e*kW+fYK@Q7gEvQ#++jq8KIpjMNhIyEscA@m$Ls1AF>nyV?(68I8Y!UqB-Cv}x zouB-JvXX@;zmi3rfD7}{3_g|&W@s@l!NBQazRDgR{6}yu1}43jIY(KAnwO_#Tkm0zsZp9^N)p z5$~R1AcV@XfNJia}V4yW19jci?9Pm(C;ZHJ8L>0&aIDE z9vB07-QK=VqNTr*7iIF0O(vE6i>w0&*OjO^G9&_TtgSjPF%?vV6n#bUWxsT4Y{fkc zYLvu@Iza_;T81I+d?3ry?z`ASU{^cW5|R@%PxsqzoD-2(jJh6xF6^!QL{FcbA-67m49W9%69#PV;H`ADJ-7rs0)!Y}_3c1QJHe|Qn zp_jy5ul?kCw}@IPXo|5*KkfRcK$`VOeatudNUBY!XB_3|E709geVA`v&1by0>a(?7 zDqgd03-DA7ltzJYgyg2y8uGXavO3No&5!Z@+0ja_1(SRhWrt6?3Yq!e?rgzm`6vpGP* zFa_OQ1c=Mx_0p6H+8BH;kf9)qYKp^(dM>CaBDO(ySt``+tR`?;&Wg-PJ!Jvn#9v}ii z6ag~cD~4yskit?Uw*YHd#{iho_I9}W4<;-s7!u)oemaod4`v^@4sZ;5k8p(R^!~4N zdyyn)DU=kHczbIt9Uwe|vu@xOSHlg()03Sf;JnwyE6#rw&44UC$+<(^t)8`tW-INe zEmFDdJJ9IHmroc9Ls<39wW!5S6Z@BHofh7}zd?ptG%d$i!&39QI9CQFwUGBMGXeo_ zlAx*Dg$z%8S`lZsQ>?}f!GyTcQ!AZoUGd`qEiuD1Id7_j#k&9zZpFuytCbWvTyCQWe9%>90V~oCi%ZI& zre7dyT|+5rYd@27mYrTVmzA8d@6%4DX?cP<*$e+V7o z#;6-T%s}A?(baO4r%GcL=)jG!!&eF}`>Wf~)k3L{66?>(+_!ZjyY-l!3WN)-Q?zpi zm2Yq9mw$ySO4eS|=Q{`J*~?uFLA8lu*P-;O^<1MI?(6r1_@G}0>ovko3)1($OKflW zB_qgFFHB%jxsACE_oD(uIdbUNIq)L# zi&SS!LAhc8muWyz1-h5fBK6qGo*S@pb_*iXR&@)@BEp$LA$GhbsuU;U(>dY-Cbx-7 zhyDSJeky*Ca8stb$Tn9)9yRahgO7@~M;T<^=)e`*^W|$Rg4SSh^IQ>F>mb#ha#ub3 z^^4tQz0aoO=hm|pMVzbQMnY_Q>dESRt9>8Wj-#2CQTmR%YLubkiHY zPiDK!2w(P)M#-4&QEG5ryTgNR9_o7u3lynk8!LrwZfz-fKJjHq4;N(+PTjI$Q1_HA zm$h2=r-D=dUI6@`r^Cf_AQu`24G}a+j-SF$URnY`@waKHKV zl!F9PP%iQQRPgY}_xk&P-{EOrXN>1Ckk|DZs|WS58?Awc@SW?1^Wr>%`v>mT56WYa z*mb+sQpv43xst+yZ(YcA%A(ZWA8U(KMN33~6-rEXSNc+N3AM*xfCaceq*n%R9ap}a zRX?|@#%84K7vOu~Hs-f#NQjB*+U9A-fy(b1%~{6X%p3{d5nY!Fao1IMh2CjCc7V^w zV&PtcsU==B&0K?AK-^$Ehm=bVWQ7$F{|}xl1^^;vJ*H8kG(O_8{DxNW>}xEUZmH$7 zv_nMbr`ov?S9+Bm3}7av}#V?5}@Nl ziL(TG`agC*sy8xuKA0hRyZ+4cfyz&o6zXiPI<4!5ibLz-ppR+261<{Ml+~+h8~|pz z>yF!@e~I}$cf&N$np__*k+QmAJW-?Uj^!stP#Nu;ODf93n!Q8;HlJWl-N$Mi!NcnY z+Zq#ae_@NL>+coGC$pb=PqD8-yz(!|?etJB`{@tKJem~-_ZR?a!{*9?y2Q(M!=px* z6A||Gu$TnUx~(Dd{Jke7{5`S`a3(JUW=5*$6X53$z6F#CIOXrl2^Ucnz*2dlmOt5j z&D<=)s6GL^VM|$|uu_{BbL$`V? zdeX(5y?f-J4+I)}Iq5Vb$;AOC=;(jr_|7N44r@uRu7k=n>lhna3eA;bJ?u&KI7E&@~x(n&#y>zR+X zzS)X<>Q_5wyCm!vnddBQ&){Ozf^TztCF38mFX$U^X7f?iL9iu}aR|6ioj=0auUliA zWa;p^cA9%mn*X+t_sqx6-DN?%9!hrY?DD5g-{A_;JcE*Qrv+OOOc$sW9xP9V-NxpftjzXzf>slRel-sWmFXv4pkFg!k$!RUls=TE@5Wr$nS#P`yEt+h zL$33vnm*1$mH+)CC6)Wbce(w8=Ai}HPW$rB!C#@=Oi0eon6sQ-r;LKF>xmJC(vLx6 zl#Iq*6}TQU?q1@HYS-sdXN0X#La3`c`hN`>cJk=Ed(BQ9dHpjtkiOwCwa+7Qs4m{b zYID#El_t+}46Y}VVQj~O4CBP5u1D{}UH%dmK*WMxs(+*eIAh#c7lZ7V^MHFmWnerP z1jL5yXE;%!(1oA+UlawoG7KD=Tj+%(`d@x%|H=EnIAiY3VEls^@K0yR@SO+);yq}d zUASXQr8dGh_`giQm*1(-m!OMT>OWZc>n)5+pq*(UzW@k^H(>qzpGY38jVF>y_R|+n z0-Uir3VtIo*j@g2*#39e{^xA^-`VzG*6aVD7O9F9x|?b_@y~~EhytYwP|6=o1HOxU zkce2!ZE!sOAk`@o-@eYKwQ+enO`aQ#D`&!nG#_>rf8(wOKd8fS3VA2!{Vx-u&>79mzHK+|TDP79=iV5v?@W29aqZr(d%t8zPY$I2ecy~| z!8G_|>Z}Xm)U1ANI==_Py#f%hfXEmMR%T@$-U>m>zR)SgLgV#~#29gE|qTAKBx$n&0-^w=^d){Kznu+RjWD<$icL zq~(5TnLG9ny*nrUHnhX~P6?}Cno+Q@e^c_BNO19?hs?qGEt7(!qS}+zmXf3mB3GgC z1hFZdC%pgtbaijWfG?}`vEo6?H<#6cGW|gOcz(Mo%iVamN2bi`{7!=FnyAZCyjB_? z3oFPj%7rvS7_43a?RV2u^svy<0esD)bCH?}5Uf-StPH4q4AS@fUSNmZZ&nKhQ;ljl ztEuQP$LP5i856HZyA$yp$4uIs=Ez;C3__(r?M-SlOfC9YG?DGxZf0$)mZ#f!6~Yv>Slno&Jd?z^OXW9&*DUd6$A54-PN^Ew!2%u089ko1INHbZxS9<#MSlP4;G}_LG2dOCD z#iExo8zSl=r;D*D7z@DP0}Z=kDLWg3pBK`SA3QWP1RyGE+Y>=wgWnz^ht-8)IUCd^ zb$qn)$6=b}=h|stES;ac-C;m%5)wJ>g7v**Cc5$&6%N#Kkh@V*OKret3JIUk?b!C^ znScOZx5DOJwc+gHJI>=zhUXDIH@Xv7dckzKALw(8-;?k@Z~t#cw=^A#lW%exW#%>+ zm1KujV5?+k&df03FsSCZId1~d1E2LsI<9$aUNR|BU#?Y1wfGXv(5~e-_l-v}MpMZo zQ=L_JgyiEwL^%A%ndWJd%<5A*$pihM+5y{uF^}vgxLt;RHF+bzaPNt_ka0XNebPHk z$kgsG5e3-=SHpEb+dF`DC;7Vl0tVRYkCeubP^Qq+2v4z@koI44leCny{8n9KXXse` z<`A`1GOrk4|A1C8fuAcmB8tj~`5ZmfXs)7N`_pzu!-2=8zyC+Fq@T7js#&K6x&+fa zjLvt!4zMTx*?3MMKDNogO~gmb@7eME&WrwsF+A^uP}TP>Va{~l7vH&ynrmD(gaHtv zEL&Cy&ArTv+srES>G9m!RMUfNblf?PZByIMHiY%VQYi}7q;J;o3&0)MXWl|PhcgqAn zM!ESg^s|Ynw-fK%D(cKJ&3#F_xl*5eRhEf5o`h~9mb#T6akzo@i*XACQr=hNqO=@) zqnNmO?X_vPQ+<9!y!a7n2ag>y;8fJf4H*#`NFCLZ_%6$r^Sd_jqY3^d{c@aQB>!R3 z%y_102fmtn^DT4l%=wwO!;&~03-Swcz2w@NlqKxLW*+<09wm*%1vKJ2b0)b~50&is zSzFil#??3U)mKf13<^FP58X+5LhHG1GjmqTgHI!|T{oYPg-E0MeL`NoineA7; z6ZboY&iJNgw$@MWT;AKd;%goeert!pBmLJ{SP?0&bz6KJUkFfo`jGa}%adr6`KsU|My@NyPHi)u-w#(L8p<$Sl=Uesr04L`tC^A1Z1t>#I zRQo#{vo3F1RYvCPY`cKRWy;46QBgEo^Y)#*dXggeq9C z?nf+j09kA3I%yjlNE1nUH5z+0N|b|`RbC~pz&XJrvoCCpP5WEC{@bM?4DHFTsy=&F zhgXw(I7Q|Z*E4>er#U&LrvA({efAc20rLUDbs-s*wF1U*!tE!FWSna`4;fnh`M>Sb zFI8E)mNDntbx$MLlmGmx>do|z>CS@0aYBx7F~sS8nEXAvM?`~>-LD>>eMw=+Fa@w# zh%rz0Rj}p)=Oc1Lt(T@uaz$t2O{aGzl>%6szF)&*foKIb z(pae<@tGfE;{~3Xw)L3+CcvXauH;&H>u-IITVH6&kBSF(Petv+6STQa#1HO|!#KF7PEq^^%V0+Zq>E$<4MV!mHX2Nkq z8g~T1M4x6&BjKnKTOba+Isq{=o7VpR(_-=4rH>xpeRQAr(8lw2gyU3N`v1&2ZDhdQ zc1BgE`N``e`7Ll$%!|7>6!;2z<3KpFvD4ih1HvLhy8}LpkrRN$03DV)jVt}F27Ih} zUujigLjeeploEv^lT~%?jDhmuubYJ%8sL}r!6uBf8i3o??KGUZ!9LZh`V24_mYq4! z;zGI`ECp&p(rjcuunrT&CLu-+CT5@k0LmQ<>C(NG7-4U8>%E&_N2arB@u@wAhX7a| ztsciE1oEtvAb{*jeB%|Iv0TkU=`+GtKZA>#@9zL}@t)5kSp0Sj2tiHQLn#D(e+*av zp%B;b&C_FAiR=YyKts&XL;O<@|wUL330ib zGYuUjEsf~ggo;NuOso`wG6#B}ts!gnG&F42R_MrSg?sylB%K8G;^H69z9Qsd;w-64 zcY0#~3Gw7}A>uYmh1A{K#zG1M%bj`^HXCaz8|!OS3@{d(T@i<-`Ke}Ss zc8MqRx$q%Z21DGMUza&f(R*8O1b8zH#C7kIVPmnerX=0*uXrco%D7sXEiNqLIlo%V1OX8 z4Ph{D?zsMt`{xR~)RNqqtO~^F8_T+0kweVRhRb6#=r39pxLSRjMD#|(SRytt2n(na zr5Hya6<4L_CS1dw9>ZiZ4Vf9p<{jQ~{Mw*rB`;ui7;IEpn%{*d>?^>c-nO@ofFhuxgh+P`9ReZ} zf*=f?L&KrFJH#PGq@+tgq@|?0M7pGF=|*uf5j$ zzUv#d80h;Ep4OrKtr-KCK39+mgNDj;``Fv(+kod8trd<1SL$m6H|J(F+wTyy63 zV>ysQ8KER{>$gy}^6ER#5#Xgj(~ji>zgg$mz|L5p`dYxtr{>MW$+Y*at(OT`$vt^M z*AAGhTCHu6eA@7;6o%a{SF}>*W7klhoUzJ_Z_6XIX&qh4exs{7Ib)79v((zG&nZ$G z;chKE$)QaWQM?oUW!$pLS`M4%>iDrmRP1VWG{R^QfK-Nz%m3-*(xu!25Oh5Wo(#0y zNoz}%tZLEu8VQMP3P!~-jEA7gd^hC7mk|x5(9V^^Bv64*E`XRVYIwJ$_br#W+BsQi zE7(cs%ExtWCUP3RvXdBB$73E#AW3%a3s4q8I*LkC$c`Viu0B|@x8Wwt321r&y}@@b zy+ZxJ|P`>uR7;5J0hQ*Ai-q*+w#}#{O7t5r4oym>f>yA z*6qU}s8U7XDpnnBZf%iDig!GYvEV3N{6IvUotcRxE|wglQfS^3TeL)8RZj=G_qeo{oai3GkU{+uADOA!%m+3KM-0IfS~#bX>ZOyB=3IuG8stK9W>^5yy`k=a&DOw2fM^+m(G zj0=hRh6>zzUKKUX*q^+>vTgV(B1}mM@Du2f-HX=wEMqaSFDURceSXp}O#i;LrdLZ? z(H%f13ec9t#KZ(NKi{#t0?iSj5)yW{wqfDn+HT3Fb=r*yx&D%Dt5rvnngzB&Vv^P= ziMu{w-Fb<@-FX71bHl?{BLFIJG-|uL9!3urEA@DJ0M@G3S&9G<4cmo}xmvy#_Jtl5 zaqC|57#3E?*=)Q{Fz`UPYcZoQBMFHufFxw+==e-ql7k*U?Y-Q)fM{mW$s#-|8+6O+ z@9b2?f;7=R7Getxi#q9TA-(A(Zca{+vH%2h+Pk!Kh6n&2H5?%l;DRKcinN76Ei`xk ztu4F_6H#GG0+;Pu$c7;&>%-sP7jGS&3Jh8b5W(7=I^Q8&3*b7$Bnp}H?_)2N4i1JW zR>nA!%aFyA&`$Tsfb}<7QdK*nWByG2jv5)oJi3L9%+%?ul*Y_KaQ9|dUtvo?yQrf#U@q&6R49AR`^QRVLtryhHVc~hLy@2FjQ~>=%YE^T z7PQ-HJWm}>pG#{=mUEmfbifR|<9M0gykz^lQgGharGAPU9A30<7n>*clZ?62AwwQ6= z=iM<}zM|lWYSX%!x*ZzhycZ%a@p=O^PLWzJ5!;yOb@sda&58CZk=68 zY)saY(dL&~38oG0`4*b6ozF)EDrEFHyFd323`W_E5@AFj|3IQ}b#r*qD;3k)#KyGg8%ED)>{C~4CH`pi~3+sE$(Vp_4GP4dtbj=Myp zahyhzN!9$CbTqoNC^j=YjkMZ2?$i8FxrQdjr^W@oga0PRqd&wo+)bds!M+=W3gAvN zuVSOHBw;dEHa2bX{d2--Z9rvY2Q~b1|6qjz?Ia_=#rYyRJSC;UXMV)_&&yeAuum>P zg9NF|j_c0)VjUVVj4P@sYfn9D!QS!hbFO_t zBTaVyLAaF{)4zj^>T&Siu99rxG)6^S3+Q#h$4uDVeW7?Hw?k`f<`#!6c(vR%rXJ!( zgZ9fW>GfI=dN^^w2tfO_nNaNfn!}a^%?aT~%6{Dw4YpBW3p?NTU<0eW zYSp*-ZhHW(6F1w>5{38 z7Q7!$3RDcrpi$`F)`tt$)tgUJ6MP=J5Neq58sR-~DI6>MSZ;xq&HeATC4mukoQR|0 zfn8tuJmzW@C?~P$DAuMuW0ZW$)2F~+(1pD()?k7a*43vObUxy9XxAyk4;+HS=TwUp z-=&$CSgPeclz?P(a6fg7lej{ICUdAg1bG_!(e`y09ulkV4E247MgukaYG(CP*zu9@} zXe!NSB5?hrH4zF=n7C@?*!>@84;@1RGa5Q4HIK4x97 zn`Vq*Jd4#0($)@^P8PaO@PBm;;LY{d2Ue&hVAVVcWv&~Q-FYOC=Nz`2;zi!4P&AtR zP=aUt5iuWVs2^?elv*}6&tTIsd@fzY#W_VrYu{d)n3ejMU(92`SkMx=Iw6%+N(nDB@MGNSGR#Pi0`9To+-NM3L z+{qW(VzJy$D^q=m@+XKMgA=MQcUdmKZrr`yKoe3L?VII+g!Wk_438l=ITan#-2*!iq`6$E{5M}zzO2Tl%0*1{R|oTuQa zwghCI=2gzx!u8VPJ1Qhp95+VQ?LG$6zLyPrSEC^+8HkAhog27tEN1g!W>)=Bgv7xC zfQUJNaPg^)?qhODB*TeNAJjjvbVLLAij`Ony*b}Y9eF3KspLg~E29S@Zkn(u=hgjK z--;8BX?`xprx7t<2aRBv_TUMbG)v!Q-y@Z#HxP&5XY?d`oWRdkl^x&9hrVymP9MMW zn)FB*ySm34m8EiC?B`~k$hhcWDt?#a)KfvpXvAb~bld*TnCqJlC5d;xo$L&(8gg0F zj%bUIUg17n9Kjp-ENougojJ+|ONxp>L*0Ce%CRdmuuf6x#=AG@#z;(3s04lQN?TIs zpI0GFdcw(Z-ZOBT!m_~G%12s#`F&($#apV&;UEa?tia2VWae(^WM>~i6x9kM*wu+s z^fJopYjUc0kH3qFER=$sxXf~@O==-h9mL+cvp(5FIUAcRL3jS>DA~fGx2_PjmdF@tF!aGzOY{S=xWUQE@Te*=mWy*N#fh=uT)X;b1%iy@Ex?Z zuf`li1l&AMYdui!4Giv~U=>8D)%`iQ-P-o<^Tr&6{&Q(p3=b`rmWph-z0UTN6dh96 zcXCa8O(IRy7Blq;sgX9%;VgmYz)cip!kuNM`MGNfari~KRsRzaQRJ~sH2Mk{kL9vn^s_pL!#73h^4 zFQ-2I1t|P&N(IVgR-6v&W%RrdnPv|C(!(a&M0DoGb0yF^@YUMj(X7cdNvmN;N2glP z{uL8pa7(TDw#zyWY2)NWiIE4gU>$rmOyBb~3U?JfaG0(wT&bwPxIsSsAn?t~MTj{O zDXB1>{W^VdSl5L(53jhvdj02_?yYslmzVyM3{>Cx#PjC6Z-R!z8&Ae28?eP@PjolS zX25Qj(!}q$s%WY8T(bJRsZ&sJA>2*!!$<3tcX}HM6m0m|wA0G|L3qvI(8|98zmmHG zY(EtUA$aI)1MunR7oxqkhjXG7df6XZ_L@-Aibwfc#(2T*u;aVlGNoUO3mnZJMpU5N zpM@TKl-SyI3=oK!rA5)$1hTgEz7lmM3I7JPy zt24T4sIRFwuofBQ+I)9^lh>Q|TnJlSEKRL0OMh>A=TFh(P%M^>KEF11#{WEu02^6G zM^xNzSlkBwO&dAXHurL~YGYfR@2TU;NIEfI_v*{{0PA*)>hJXv z=5~jecMN-azzEx>6`(;2$Y{6LEB4%uNT*FZj=jafNzcv&X$B=6q$qO7WfA@3v=Q%3 zb9?77Y>|1SL-4R7+~>cwA*!3Kq?a}(tE{xU;MSNg%dBjH)}Sz(Z5=xo5vB6H zVo92O9iNV9B#Sy1VjtP{M_nLXdVg010=Hdbsv!uAR)FvfD0!=h| z2=gu`%yNP`>^+gMvK71Px(0b%wb$3$OZX1_3`EAc7B9<*1cGUwA{@=nf>%MXG%|zz z6&CeGgTGdmKI}a>2N@nBPQdA$1bblzdS(46iTF?{yCoMEB7+=uWF zrf0X}0txvJf=qVIhVcM6FfZbL9Zy1w11`~(by#uOhCBi7TYo)NvH8tebbf1OEUXBx z)X`*BJ(o{0&NKT@;H>T?{CMHLwk05=LqhAkQ6d181&4=nSzhh#X+P?64E34cp$6eK zU{fT;*XZoJ;RFkaBHjoVqg^imQ>P@pBW3NY=}TmXl7pbTsF>h55}fd~rXuIVSq1ZX zaC%m@)$+;)gG#mio-B>izz=dF1!Xf#a&QQuJ+h)ot&la*LAKHoCSFk z+O#iv61K#LB3OmhpH{)@1=_FbuwomdgU#;E#O&d`if5P656e3o?3ePhW`nJ#^QiP6 zC$oiL^2|uDsg zH)Ff1mYfQVw7_90nSVx;t zs}M$`AJ%<`^QLZ7ry3tDW?N~;^YF$KUUAqpuNNp}#NR8)QK>1_w*!4c#Z+C=UWo4b z_8o{cfsMOe>`ksRC|Te`q8UoOTl_$fIb<~w!{?bSYGnF!pl@`r$o8NTI4n-i5|0aS zFoN?TG+%FS8fJrJryW=mRfp>nRdqD%U`ypS+H^07n0+{|T5}dI!><`prb0zt@m+(8 zJY1nq<^z!hAEMN>t8!K<&D&deycE_!$0I|gS*p7L_Wp|cgs5x~pcgVl)?6S{HMVb~ z^Z|FhG*v09s8LvJ91hh}sWcy4URDuV7($5L6d{y&^XBDH&P08U80}2kYpMUll0JcF zVu0P`{3Ku&A}ZA}`e%Q6m2t)C0kSK$D3?7mn)KZ zL&Kd^D~vcfIG%<4QDigg9DNo|TF$5a;4r{{b-o8O>l&u_ zP0+ak`v>n~IfH&GaM~butf*QN6hDHY5S3q8RgF{-=(aX(%U3e=#dYH}4*38CkUiSv zbb8G>gJxVlej4fSaG$U)byL%|!{LEEjS}TtPAD4KrEMQBEaBpyH&HcxabW$%?h16R z&2JdvGf~GxW)A_C&{$5p?mWIT$E}Gv_XShm)3xLfGFqCI+G%QM<4<)o9~vroc>W<_u<8g* z=oja#7D9wdVXPVJ?k!GLnWF78@?71Q;$0eTj) z(lFi-5-s2`p!)Mrf$-_dMcE1;_lOs(sMT?vwHcN0!dB`L;-lGCc-|c$QMqjO)_nt+ zsvZS%?}2ls`B^@5R2NZVX8x3P+fw;K*osG4mXwDNM^LVau&loEjS`o)6YZ1g^I^z7 z?Shi7FAc+#-%$;CeW)HZ8?vbzcqN6CclxPsnW4-Q!zaw7qB6C(bK%fV=1!=Oy&IBx zJA;|oXyS^mL{!#eS1VG8V@oi?)a1)g~RnI|T+8Wa_oJ-jgr zv-_C$%1+g{f5Q{f#x7pOB3Uw2{z}+paELfky(}cD)N(2|meY?{zaU~4H5}xOgFM;& zZirVkDgfDh_}D2p#{#ADtlros?*J3@fbd;~%wdKeWlvlcn6b@hTYShcerFS*#Of+x zSp*C+=!y@y_Pb}@ny9M;pPjZ6!fOPftUY4EuqKOtPu{++54iufx&ESx$mqDw4dTY7 zRUAtAe6uR&peBmk#6Z_}ENZvCG^J&Am7r*8=}QCAa}M;b%7Z5A_zupe*xmDWc#wyQ z=tw~8aA|pDO>Wi~1a#U`UQcR|W3_5#P-GtvvS7VUeFl%9eSf>@g?aT`iTNgZpt;_P z>l!@gub=usqM(AB(w|G5VeUjXuO8<)WY(D?Ic6Hez~-6b;xR5|h{-c;#@oY%Z|D5W z*$EJu2tgbTjDj7Cf{1tEld08Ilt!i7{^rr+;)$~DgEvAjF&Xg*S%{`|^aH}3yh&x= zelxEoK|@fMje?fGW;xgc{UoEsVRyf|?3lk{#(Gfw3eq8S@@pojFvIB0 za$Qc1Ij|9qcQ5&J(|T5Zdfn3O7KXz|2Hrs&HFULy2}^9qHEdQj2Yu*K$%bq_Oqw?$w`4|Y)1F$9p)gi2BFy2VBZ z>g&#lxF;*pMKv{Jv|JhPom*Bf^B^$n)EKW${KwTdzk^h~E2Fk(p@<Lo*qFZcpJ*>% z^y<%WUOx)zN6f9x&qdvn1G5zyanoi@`K^`WL5Ra1mMgaB#T8^ozWojzY}pAPae$BO z+&@oaqRQ35F^&5(-+zY2vh$XgJjg9n5xgP1e7uXb$ovuCilid(%}1QM{74r=5lh@d z3PMZunM2Ah+aud-AMV`rn5ilvKRM$>)tGUjlers}Dc8~G*v?4$ebC z9d8A14PMMz)iR&Gt|ET=-j09Bb)!sqWu*} zl0kt`n~eUzBWg@~M`R=xz}GmF_|}sCJ03geV3PN*m-VdJS{ZrzAJ%t`iLRFb;(+|2 za&r>yWCbR-U-$!t2AvDp7s7uB%>HOc8Vm1#L&EyrN8{a8Jd4@ zo$xoWA>eW?X`lcm#~-}SW&gDjnU2e?|-}{ee zsAm9bbRHBbqTh%>cUn4f-_*z$EteajQc$W0r}XpF=-s_5%~vxWnTg#O+$Xs#u?f+w z-j*Sr}IdmHP&YGFBJdtKHG_-2r_o`4adC3?U(~ z`@b20{G8V>L=^(D;Jl|5fNz3LMn<_H-e!X44@vgXm&~sgq+^Q+i+x{4{%3DxX3`MN zJRWst8|F=6@@E&@tG`i3oK8chqDrTtrcU-dLrD#q>f69`dam>Cwhxv(ov7~$ErlQh zoxe|*m^NITG5Pkpuxw9?UFQeCA-8RtqQ5VePagQ>$J^(4TTn+AMF_2pT|W5w5)l2s z)5uukK^Y(7e6s%-Ltw+IHy{n)ZE=x~{QU_s#PAG>Xi#cJ$RggKGabz`|K=ZDuak41 zn_wrC%Y5)dj*&(bEEAbyWMscv|2a+9l9e0RW=x}h?r4C(fM)?+VUN{kLr>&?dv&-G zO0CnhH2Z4nzNGpw_Vw~da(agP@8`T9RD<-K`lEXY5%-3Eak6jQ_SefP5va)W8vqv1 z^zRqf6t8s|RkuM#s=0zSjR*ro)E5ohL+}N7WiM{!-#!v>AX4xx;&PbhvBAOIRmE99 z3xnmQx2V~7e2b?kxPYwm#|>#Ra)^UzZR~93?|lW?b`A1TJMWd6%UPXy&6PH{=CN6c zXg;S($t?_^Uib&n2k@HidH-(YKeVePRsXvE#M#y7&C9xRlFN{=0MCGWVtfWLxPic# zK+bHukbkemA~+qqdGfdCYgt17ja`E;BY-68wSVH!^w^tng>=xbU3noX z?Gx+YPudUtmA{--a1ZWpe*=dAmk3-^Gh`$I-|r4EJ>dOEL$HbdE7k4SCh|Xq^6g~3 zrcp6}zg^TBK#aWV*Yvb@=>!g(=31Bbe~iHky=lwh5h>-2Nqr7ABGG`kSZB>#jx2}t zwmbI=019Ec3MkzEulrVB-#07}D%7q-fc%^xEmgyeHKYeIG5N7^L3r_U0$G+34c>OW z75$I(A%|1G7N{IKlsWJS6a@h!&0*v8z;0=3tnBLb;7nAuy=%BsE(d<+At;f7lBC#o zsP`y;EE`2B5O;OQ``;GPi*^)=Qy8y@?N`#6h@G!$*A#QQm52z zE!axR?8~xLX)94euPN(zg%Sma4G@3(|JmIOx#_&2SZ0lC`=d#|GH$W9!D1m{hGP1J zf{e1QC4nnnmKZwNS{K)FLX}fTJHwo>JS6SCEP6@zH{1`s__?MD;?P!KEGU#-aV&qW zdM)swmonP7b@!mlH6%`y6nWD-5gJTk^5`XrA0z2=+2m_QQ@A_mZgSIVEQ=Cbs)AcL zqwe&i1_3{(paE13`TvXLVS0`rXs~drNE1rW6%}!@$X$mc$U!pTE+HNzO_(Pv)%77h9Ud8V#(1nHIDwLeAj4zV@E|!;SpLLz?@LlQck&A8@I?O+_%mkG6Ci{&0XiwB zryJQAEF}380MAIaErjfZ4{7-)97#|Aqg(S|T)q|>Jeu^{%7Z4&S;zCI%2h#4Qf{T$ ziDVc*w^#@iV?OopUp?btQpn|~xmxxHh3N36EDkj(R14uTq0voC>DN>sUk z={uz3Vgu(DXE3QC_-jx5%V);KAyMVJQ+I=26~8vr>m;khbRLHq(6f?*d2VCwTf>YW zE9J^D=%4Sh4B;qSKbZh@x6EmqBTEv1o|E z{tj&tqk2J{{y-MKDiDPNU0XCDSRt~?=V)fhF+p&R4hv4Ju1!c7^HT~wh&5dlE8L1Yp$IInwm-R zlXB@zOw!siL(Nc)GzNzJYUh>qk~Qv4pz^#e8vr%CLq#Rvy5BIe(*JUt_i2^e7{ElG zp}u(|lbYJ~eYpz^OPz~^Z-k;X^;}wNby@xuNWS*ZqIBh2SZP+jV0a8}HgSf@tXXTD zlTuVvm64KNRh4lBeuS@DgrBBtRm>mGH7j#l(gu$AsC-QluH)50xP9ss^DCUHA9%3s zj0`^S)`_}*8-d&F863p-^R)3}o~7FdLH_yG+4$7rBHlawVyM{w0V6ORQHBlaEu$ol z()`8OVo-JBL;!VT8dIToFKZYdplpnU2B|x4VgXr{b~P^qON7#R0sic0vMNwMK8#;)^W2vm%El-Cs0DAT(xIxfmMw}U&&vE1h(t})zLyn( z-UiGJruXkDU@M(m!yDsH(2n*robkqljw7%>R{D*$1e~1#HZ`Lqe<)V0!u91r9<59w z3bw*Gb~VNnZ!}2qH^sE(*GQXFI2F2=il7+-KM(EX$gcoS;U$PBvK@kheu?M*n62)6 zf&`m=TCl0(J?-<^)1Y`{WZ~k<5Udg%k?z;8Ys;1lMJ2o1^=nsechZpBd0$Zey z)=3{-YiWUmMkw}VhI^T0Jl zDT__tuxqlytS33@8s9;b_*%X(FKsBc_*MtNloAkNg8Xe6D|eC^)T$5{hgm{WJY-)9 zcy0RJUR|erP3`r=o0NN;u0>EYMvcl(dC6I&dE_jf{tf2C%Ux4pJm9n>sS4tsaJ$){ z+@Engs{;rr>(rJ;Mt-R=0Jx6*O<>`J#x|`=Pa%+uc~ktfkMpZyg9p|WR>|`OX+whS z^l0SKAZ+MPK>t9ldb=zt(G8dzZ~2vnex>{4TokzYs%3=xwq)@2 zJ^3bIHyArqdK4KMWf*j_667k!uc3ZGcB_VyetD?;&jE`?h%64->D`}hAFn3G*>kDz zai2rP)Usq(GL1Ix_Bgqm)0b#LS?kJXN#CtNL`6CVi#59F26Q1{_djPgyI_eYuKPor zK?I9Em{#5-kS1GLl-=f8s5xTm22Kh^BJaB$rs;p0YdrdwEEEfa^%8okb2U6JZp>-2 z!eg&Fcf4#XQQ&F_@Dvi5w>WSOn_=-gs95K46w7-@b>nlGDA-BbhUX`C`YxVi@c6Ag zKZ=b&ZFbvub+E^ZIvSqSP;gW%v}`G*UbI%u!2&x2&2NK((2xAVu|L`iqiN}Dbk=0K z$zG?bov*G}b_+nLT3UViWSXs!HHgo+k)R2%iXfTWl>@oilnJUxxtv3T+^63DfKVXc zv=BRxm>MbXCHMIN00h-ANlna6EuRk9F3WZ_OQo3x!8;HYO3kRJp+gi#)d+TkyXE5X zI3gcO7h+IU<+zR*MA()m7p%xitc9g9P%yDVy8!G|gQQLc`qFUrpCt3~!ykuIX=zMd z^P9jdx8;$Xk_5gTh?5c%*Vr9d?lgx1mDR=hMQ-l0+N;ZyuU`!wW;br^yzH*gv`iYG zTVl8L>(?vc0e|Tson1USILOz8rS`|4U!o)lEFbt1Nr!Z8+Pa-z*7sL#OGgjS-F>2| z6+f}CbE}+!3&Gx#Iuk00*xVi=w#;W~jjY-^^^%TuI%lpx@Nde1=O`hXob4A(sv4fr zC$y<_x3nTFBrj_rRpss@`;{!+mPaXPdDL@trRPXvitH5X^K4{5qud^p8p%zln=+k| z#BHIXZbs4FI_daIhtMHEO zGJ%=g0Z5r>M$JEqsyyJ|zNPM0o;LHHnWmz|dr0W7UkRZ>LVGm=yGtl3N4pO+coFOX z7mI_KU-nIAwXE@XA+f#+8WrSl%YBRf*q0ap;J>4N-1Yg7!*8ZRBi#* z1%TGb4c`FsARoQSr;o!Bq~*%_UR5>nfuyVn74!(YaB~K*X!(lYQT-Iag z1llQjPM-TZ`tdS(h_Xg3V^ZmN#uTEB!jy1u!TL3+~f zJaB7M7vk`-l;3j)3ly3@*MhnfXv`>K7T;|g+G<=O)FCn>?*)*c0bM2qJh-M{jD+E` zg@RO+bQNMkW5guU<1C@QSopXr;oS$;Rwfn_V$vQSY9ClN4n(rK0odjk*XhShYWIacrNiHjv5FQ+k*_N`ZuCOcb#ihj9A^=p93f%8((}y_PsJ< z^|ItATv~MZ+kCJ9ct4b(WP>;%Aes^H9a@^2=e)5SE4eBad4{v&>&Bn?t!dut%uj13 zTihA3-V_;#6s}R%eEs2J{Up!a^UWoA#yKvs*;zDb?X$a76{`WG1ueU4<=`fm<4br0 zz&hPZF*pmpR@o#VaAm>1Kdwq?x+Jbe`RIm_&9G*09*N}cotB)lc zZ`F(xP41|d!Q9TtYh|GwO7UfikIPDxwk8!+OL9}P$4Mn;(Wr`rK!sM~cuh)SaA*TP zjjK;n6Us_ol3cPOW^3BKuTmSKGkb#N3mqO7n8u_rJy?tzJEiVW<|26(5fw{Kz1&y0 z>+N@MaENnYi0jD&!1Sccnn*x9N+Vzot>`WI+>49NCDcv>0ipo_SIyQ4Bp<$%thVk( zoNN#i2MSR}$s-_-n=%1N8N&{b*lg>*1jHBWlv6_qQn&RNyrOqhfTZ9LU7labKW7NM zs%x{ot;_SM!5gegt||tsa=Qf$b4QrLU@16E#N|5Xv*6sJt^t^r(WrNVtUI80WJSPk zP3<($gW$%o+I2?W!WA^ z0lMNj-7Epy0Z5^n&wLfJE5du`vP%Ww83a!MLth^fP#HTxQk9Jyh=K z$f;@v2D3%;5bo{i25-QW>g>{*#-zf*wQ}8G_U^QsJtSbdY!^ITl3K^J9nio3dg z_wSGz4QHDE>jHE8)!x+kV$bDz5C7S!TCbZDcNj5_r`M z(XG+4D}Yyz2h*mg7zwjv8m&%CFU{L%l`9$gI2a$VJ}%^+1z@|q&0&jCT+y^TkH`0R zG5lUi1JX#$lLmnD8h2DQvlkUj&dNk6SGr)pP-x`k)6)*bHa|7pN*P?)J@NKGGXoei zf)`kQrcMb@cq;8rpx$jzw$Geq+(gd?%$;2AZ*{zxl>)qlNK?M&5JCb>@E}TNcbH|S zTYC>LI31*;WwM5b2Zmr4<2wMb2Fus1bL*+yK`yY3)F)!Lf;r`j8ig@%g53#}FrNMl zu>MN00>@%w>v~_0;r?4gcr_tn?fT%%_{0FNayA}VX3vUtA}Adn|zi{(-{ovUvI z4LP6eMgpYO^4&$CQKxJ%0Z4`e1Re?sd#$Z#;iHD^8tD1*b*L&Epqeu7Yh-KY33-oGeGIf%I)aTE;K(`HRymX z;W0Ahj}Y8L1c8Q2w}E-;OoKr1Wu|=v2+Ko_7B4ZmIIS2VN@hV6XaL6wFjo=u#4=<` znf742xD*s3lA#LiLki6S@!H`zQF2UXuGt43US1mM2R%jQn)adC;SNGEat~iO$ zZFB5^5SFhsUf_|@xFRkI1;U)BE+UZ)#j);qd2q?47_dk@CCu%UGL7U~+k@2vUMCsAk_LO%kLg%NN~^zQZnd=9$}Hi8f!7=*_Qk~1#fIBf4Pe=ChE(5Y`YW2u$+c=#jS%3AVu_N+!sp{tzJHsV*iz4}+{`}R z+fS|f^8KXF%KGZiav)AhRH$u2L!WJV*TPObV{mOPoX_pZYD%@nZg`V;unG{F`v`O8 zy`Hq z)$UK{L#34F;`p442~97 z+qoFg?F^syZw2(~E76raj4yK5YisnwEHdogIT42CX-t%ZZX3OziNgfgjrJC8OGLMJ zZb=%B1Fcu^l3Cm;AQ4#Y;Z`!Z_*ixQ;2;PY*&D>1Gy6aicD2D9wcC2S8#lV=1KV0} z3g#|G)FVk{%g#Z+t(xfg_#_^^L)s+wuqggUQ@c2(U;vFLCN{@Bnw+dHrF`j25erR; zW_bKK<+1uYD0qX=33T1$P$zEC`P8uy&+BwKQRM{G;vM#em@KVfIM!Ee5737G&NE29 z{Ug4j>f&B>MorCL(&Y{*Y0VX#+a;2j^Y;VN{#PJAl|hwaQu&Gp6;~5ygVxH%#Y7DC zjWyrg1iVm^O{(1WfL?uZ5}Q&aoHpA}g{1jSFrYA(#4UceZ4-lP;T! zpX60y;FU8Bz5%pD6MPP5B($c?a%{~oWvG5}*9^aFjyDDZm)>GLRId$!U$7$-8W9${ z8#Pp#bA7`6v?zEDhILP7q|qu*c2+P^tx$miq(1l(#3CcP?P`WOK{fv^Jh$+9(-M?&K5W?Jl(lM zT&zUna$h}9;am>nO6YxeU4j0C_S(1PgCpNfO%lMIsRw%Hbr$2nCm6Qq4_Uzacm-0E zE_-V2%@}_L2wHP{+Bu})oQ$Uu01ka!*dVdT6tY_ z`0Sb)L%6@BRquj{2MADBN;5}5%q=k+4pZm@YLRxut$;xj_Hng1^_Mcil?eW- z{vy-CF`#ZU@PHXIcX0CpjuJ4~i|9Muo@oTg!LGz`kNxEWkYkJv_*$|ERK~d2H zpJLrscrA%jRRv2M>Pje^283htrvuO291o**-=zz8HOT7Hk)I?uSpv{T9Mhp7YpBK0 zA`y?xF38p!KO50!Rij8a_3UhW_S1d@r&}WQlcEQbQk zC9}c-P%8m1g{VCH`lxDpOFdA;2qy(bK0@97We)`5u%_$BZ&79oa0d`w>D^ zL;BEbF{%+K1@PlD zI)-~kODxoO5U9SnfRf7I63hJjBJT&1vX(kaj~#DR%t_$iN9_v1i5Nsd08KyXdDZJ5 z8Xh|ns+hoR_0k9?w?mVM?V&Vy_~wmhS<9Ba3my~wBYd8=10Zu#0HzM)kWi0EtL@IZ z0oD`sXlrQ+PtBs+mnQ&tFZZhk@5A6=Zz;dS^--hJ5lzc0&;||klic2(aRUw}YqDf~ z?k*yb*~k5Dyr2cRzf4J4`vOSe}mNS*`5C&<3*G$!9g%95=`Tc=<{6*5&oB4t5d*pkeb95pG~)`_x#qrsVtgUC2R+gAc#Nur z_S7(W8VGf6YH{Gl5iY>rHe!vo$a;cs+*ffk>Vj7Soe@Lo_^+w-*elqu;o$12}V7qv7#J`^R@4ZVRSA5052gi_C7H z)#>PU*09V6NxqPZ)#6c2EvxVPrIn4uL)Eop!#HIk%#KUa86cn4X6}|=i80)hFrL>! z=)65mK0ejeRBBZ^QlVL>Lafn%pLai~&3w-p86?C^?QEIinPiS)&lIxyN=iupJ@4`G zl82D=V(;yrB{*#A`k=+#PM8-d!rA`TCIa>sD?~Q*@rUfAuh?ScTYk^riWGVnh2h0v zEMaKhd}Lp2`?4nrem%HPYmz4TZO9}Pq#)og7}J_P!y`g%69sos*UGG&+QfLu+{=k$ zRiIciN&wh2P7T;5QH5147mome8zqg*Buxdn<125e3Gx=|O99mK?@~uTKiP5jpENcA z;wKl|PqIIa9A_vnCh7)24MVr2Q)%@!3L&PVC&xLLZiYoZ7}P%Y zIclO#&!uCi$(9~VDXCnO(wYUH;|I#sai(Mavbm zPt@@|y7M$FBSe71^VPx9QlR!_6=E@u8Izg{p4FN-bpA^C&{Bew{#ltj)47(Dl$eiHpZV6oR>{N;M#$C9bW1TutrX&CpJf0G zx*l+a@3;N#06>ZpoDL0P?FWi-0guuuM-)%`RP$Z#yCegzDt2hA#ke?(U zmVyzKpI5mbLkBX>*R7>~;4q;{J$g$&C-|+iTB&_C0 zNOB-8%7@<5S0M3srsQ^YA9&A{a|~8T%%9nl+kxP@Kv(y_F<$xmC(j&zYSF^!t^q`s zcOL$-s@H-M*G~x&a_yw%U@T~!Py625Y>*8D)frSQ)}Qb42SEOT9o#FKs-}S=l#MF) zgO8Q{zr5G$dQ4E{{QItOya(mj*ZUY?0?zEmFgXSc!vBhr{u%~J;u}RxmQJn!y%C}F zYTKVw`R7>;6m?>V{?5n)QIA|fvbdXCFRn`ofXz^O5fmf^&irG+M0~dUkN$$){WS`# zxG2}3x#rY>n3Cl^9S$~5%DcsUFHoTQ8S?+}&!9U$fb%|w=tp=ND!O_%=@kAefUR~v44Fa zFyz0U7_6Dcb#ov~`+x8J_s_L5!2|jS5@;_A`!G`Nk6s*}psG-$BqjOtfK3z=G*Y;1M11)#eOP4#$<&aX z-pQf8V6PAYuFulN#Xx)|YW;-AHH8zTyQv?ADNC1g4%0Mcf39V{1x1)g_NtN; zUiDf36w7Z$+Na*=LAjkX1$q#Zf-yZqHVMDGikzyFl4gJZ_;`PRe4Sy<_l}UZ#SoXp z?nQKb#>8kD!K->5SJ8+5UmxvhPK)1yv&e$tEuM3)P5VzT^K+1Y*uh`7D5?-*k5qNs z6dKP}m#=8|enBbsi2qgCIj5OS0{_|?OBzRodEWaj+`d>1_DJ!xB0s;m?gf4NfVM^O zaI=VIN#^h_qc6_-^x<^jMs~iBZ?*Iyc{eWltC+)E$Lms3IGFhQC&Ho01;%%}x1)9# zL7IaQyFWet&$9Wu68r~ToN{{w*@*(>2(JSb71f9)L)yk>Zx?NQcgM))m@`xDTyBsbT>X`FY{ z!NF?0ARyBv$r_+b#l^*KAx!FAOEj{%6^t40$tcRJmFgMIw1sB?_z@UAlRJlF z4uq9eZcn!yCaQ)Vj>QTT^By{%0D;1FL`l{0azUwj*{xfsc+6U0U}cy1c@n3d1cZQ6 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 ZNej~W_1 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 values.yaml +``` + +Then, you will need to modify the `values.yaml` to enable metrics monitoring by setting `upstream.metrics.serviceMonitor.enabled` to true: + +```yaml title="values.yaml" +upstream: + # Other Configurations Above + metrics: + serviceMonitor: + enabled: true + path: /metrics + port: http-metrics +``` + +Then you can deploy the RHDH Helm chart with the modified `values.yaml`: + +```bash +helm upgrade -i redhat-developer/backstage -f values.yaml +``` + +You can then verify metrics are being captured by navigating to the OpenShift Console. Go to `Developer` Mode, change to the namespace the showcase is deployed on, selecting `Observe` and navigating to the `Metrics` tab. Here you can create PromQL queries to query the metrics being captured by OpenTelemetry. + +![OpenShift Metrics](./images/openshift-metrics.png) + +### Enabling Metrics Monitoring on Azure Kubernetes Service (AKS) + +To enable metrics monitoring for RHDH on Azure Kubernetes Service (AKS), you can use the [Azure Monitor managed service for Prometheus](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/prometheus-metrics-overview). The AKS cluster will need to have an associated [Azure Monitor workspace](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/prometheus-metrics-enable?tabs=azure-portal). + +One method is to configure the metrics scraping of your AKS cluster using the [Azure Monitor _metrics_ add-on](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/prometheus-metrics-scrape-configuration). + +The other method is to configure the Azure Monitor _monitoring_ add-on which also allows you to [send Prometheus metrics to the Log Analytics workspace](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-prometheus-logs). These metrics can then be queried using [Log Analytics queries](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-log-query#prometheus-metrics) as well as be visible in a Grafana instance. + +In both methods, we can configure the metrics scraping to scrape from pods based on pod annotations. Follow the steps below for Helm deployment. + +#### Helm Deployment for AKS + +To add annotations to the backstage pod, add the following to the RHDH Helm chart `values.yaml`: + +```yaml title="values.yaml" +upstream: + backstage: + # Other configurations above + podAnnotations: + # Other annotations above + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics' + prometheus.io/port: '9464' + prometheus.io/scheme: 'http' +``` + +#### Metrics Add-on + +For the _metrics_ add-on, we can modify the [`ama-metrics-settings-configmap`](https://github.com/Azure/prometheus-collector/blob/main/otelcollector/configmaps/ama-metrics-settings-configmap.yaml) Config Map and enable pod annotations based scraping for the namespace your showcase instance is in. In your example Config Map, you can change the regex for the `podannotationnamespaceregex` option to match the namespaces you want to scrape from. For more information on how to configure this refer to the [official Azure docs](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/prometheus-metrics-scrape-configuration#customize-metrics-collected-by-default-targets). + +To view the metrics, you can create a Grafana instance, [configure an Azure Monitor data source plug-in](https://learn.microsoft.com/en-us/azure/azure-monitor/visualize/grafana-plugin#configure-an-azure-monitor-data-source-plug-in) and view the metrics using PromQL queries. In terms of Monitoring Add-on, refer to the [official Azure docs](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-prometheus-logs?tabs=cluster-wide). + +To view the metrics, you can create a Grafana instance, [configure an Azure Monitor data source plug-in](https://learn.microsoft.com/en-us/azure/azure-monitor/visualize/grafana-plugin#configure-an-azure-monitor-data-source-plug-in) and view the metrics using PromQL queries. + +Alternatively, you can use [Log Analytics](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-log-query#prometheus-metrics) to query the metrics using KQL. The following is an example query to get a custom metric for the Backstage instance: + +```kql +let custom_metrics = "custom-metric-name"; +InsightsMetrics +| where Namespace contains "prometheus" +| where Name == custom_metrics +| extend tags = parse_json(Tags) +| where tostring(tags['app.kubernetes.io/component']) == "backstage" +``` + +## Configuration Examples + +### Complete Monitoring Configuration + +Here's a complete example of a `values.yaml` configuration with monitoring enabled: + +```yaml title="values.yaml" +upstream: + backstage: + # Add pod annotations for AKS monitoring (if deploying on AKS) + podAnnotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics' + prometheus.io/port: '9464' + prometheus.io/scheme: 'http' + + # Enable ServiceMonitor for OpenShift monitoring + metrics: + serviceMonitor: + enabled: true + path: /metrics + port: http-metrics +``` + +### OpenShift-specific Configuration + +For OpenShift deployments, focus on the ServiceMonitor configuration: + +```yaml title="values.yaml" +upstream: + # Enable ServiceMonitor for OpenShift Prometheus + metrics: + serviceMonitor: + enabled: true + path: /metrics + port: http-metrics + + backstage: + # Other backstage configurations as needed +``` + +### AKS-specific Configuration + +For AKS deployments, focus on pod annotations: + +```yaml title="values.yaml" +upstream: + backstage: + # Add annotations for Azure Monitor + podAnnotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics' + prometheus.io/port: '9464' + prometheus.io/scheme: 'http' + +``` + +## Troubleshooting + +### Metrics Not Appearing + +1. **Verify the ServiceMonitor is created** (OpenShift): + + ```bash + oc get servicemonitor -n + ``` + +2. **Check if metrics endpoint is accessible**: + + ```bash + oc port-forward pod/ 9464:9464 + curl http://localhost:9464/metrics + ``` + +3. **Verify pod annotations** (AKS): + + ```bash + kubectl get pod -o yaml | grep -A 5 annotations + ``` From 59b8be43731a2d379e8c9821dad73e9f4b4934a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 09:07:04 +0200 Subject: [PATCH 089/117] chore(deps): update azure/setup-helm action to v4.3.1 (#221) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/snyk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index 5f78c14b..d043e67e 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -28,7 +28,7 @@ jobs: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0 + uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: v3.17.0 From 71591927884b8a62b807399e7d0673b85a1a1403 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:24:33 +0000 Subject: [PATCH 090/117] chore(deps): update helm release backstage to v2.6.1 (#222) * chore(deps): update helm release backstage to v2.6.1 * chore(pre-commit): Auto-fix hooks Co-authored-by: renovate[bot] * Bump backstage version to 4.5.5 Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --------- Signed-off-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: renovate[bot] Co-authored-by: RHDH Bot <146280956+rhdh-bot@users.noreply.github.com> --- charts/backstage/Chart.lock | 6 +- charts/backstage/Chart.yaml | 4 +- charts/backstage/README.md | 4 +- charts/backstage/values.schema.json | 164 +++++++++++++++++----------- 4 files changed, 106 insertions(+), 72 deletions(-) diff --git a/charts/backstage/Chart.lock b/charts/backstage/Chart.lock index f415e3c6..36c1b4d2 100644 --- a/charts/backstage/Chart.lock +++ b/charts/backstage/Chart.lock @@ -4,6 +4,6 @@ dependencies: version: 2.31.4 - name: backstage repository: https://backstage.github.io/charts - version: 2.6.0 -digest: sha256:caad0f40e6eb3c67b9dd08ae7033f4d104140eabbd77d216308d3e0009e0af78 -generated: "2025-08-12T14:49:41.920263351Z" + version: 2.6.1 +digest: sha256:ffee5046827443b39337bec2d7a738f55c9e4003a37267da25150585f07348ba +generated: "2025-08-23T18:33:38.094722061Z" diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index d6e07a96..18a33528 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -25,7 +25,7 @@ dependencies: version: "2.31.4" - name: backstage repository: https://backstage.github.io/charts - version: "2.6.0" + version: "2.6.1" alias: upstream home: https://red.ht/rhdh icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjE5MS44OCIKICAgaGVpZ2h0PSIxOTEuODgiCiAgIHZpZXdCb3g9IjAgMCAxOTEuODggMTkxLjg4IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcyNCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgiIC8+CiAgPGcKICAgICBpZD0idXVpZC03OTAxZjg3OC1jZTAwLTQ0MWYtYWMyNi1kZGQzNjU0ZDRmNzkiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxyZWN0CiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIHdpZHRoPSIzNiIKICAgICAgIGhlaWdodD0iMzYiCiAgICAgICByeD0iOSIKICAgICAgIHJ5PSI5IgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InJlY3QyIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjgsMi4yNSBjIDQuMjczMzYsMCA3Ljc1LDMuNDc2NjQgNy43NSw3Ljc1IHYgMTggYyAwLDQuMjczMzYgLTMuNDc2NjQsNy43NSAtNy43NSw3Ljc1IEggMTAgQyA1LjcyNjY0LDM1Ljc1IDIuMjUsMzIuMjczMzYgMi4yNSwyOCBWIDEwIEMgMi4yNSw1LjcyNjY0IDUuNzI2NjQsMi4yNSAxMCwyLjI1IEggMjggTSAyOCwxIEggMTAgQyA1LjAyOTQ0LDEgMSw1LjAyOTQzIDEsMTAgdiAxOCBjIDAsNC45NzA1NyA0LjAyOTQ0LDkgOSw5IGggMTggYyA0Ljk3MDU2LDAgOSwtNC4wMjk0MyA5LC05IFYgMTAgQyAzNyw1LjAyOTQzIDMyLjk3MDU2LDEgMjgsMSBaIgogICAgICAgZmlsbD0iIzRkNGQ0ZCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNCIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0idXVpZC1jM2NhNjg5MS02ZTE4LTQyY2ItODUyYi0zZGVkZDZjMzFlNjgiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNS4zMywwLDAsNS4zMywtNS4zMjk5OTc2LC01LjMyOTk5NzYpIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI2LjQ0MjM4LDI1LjU1ODExIC0zLjc3Mzc0LC0zLjc3Mzc0IGMgMC41OTE0MywtMC43NzcwNCAwLjk1NjM2LC0xLjczNDggMC45NTYzNiwtMi43ODQzNiAwLC0yLjU1MDI5IC0yLjA3NTIsLTQuNjI1IC00LjYyNSwtNC42MjUgLTIuNTUwMjksMCAtNC42MjUsMi4wNzQ3MSAtNC42MjUsNC42MjUgMCwyLjU1MDI5IDIuMDc0NzEsNC42MjUgNC42MjUsNC42MjUgMS4wNDk0NCwwIDIuMDA3MjYsLTAuMzY0OTMgMi43ODQzNiwtMC45NTYzNiBsIDMuNzczMjUsMy43NzMyNSBjIDAuMTIyMDcsMC4xMjIwNyAwLjI4MjIzLDAuMTgzMTEgMC40NDIzOCwwLjE4MzExIDAuMTYwMTUsMCAwLjMyMDMxLC0wLjA2MTA0IDAuNDQyMzgsLTAuMTgzMTEgMC4yNDMxNiwtMC4yNDQxNCAwLjI0MzE2LC0wLjYzOTY1IDAsLTAuODgzNzkgeiBNIDE1LjYyNSwxOSBjIDAsLTEuODYwODQgMS41MTQxNiwtMy4zNzUgMy4zNzUsLTMuMzc1IDEuODYxMzMsMCAzLjM3NSwxLjUxNDE2IDMuMzc1LDMuMzc1IDAsMS44NjA4NCAtMS41MTM2NywzLjM3NSAtMy4zNzUsMy4zNzUgLTEuODYwODQsMCAtMy4zNzUsLTEuNTE0MTYgLTMuMzc1LC0zLjM3NSB6IgogICAgICAgZmlsbD0iI2VlMDAwMCIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoNyIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDI3LDEzLjYyNSBjIDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMS40NDcyNyAtMS4xNzc3MywtMi42MjUgLTIuNjI1LC0yLjYyNSAtMS40NDcyNywwIC0yLjYyNSwxLjE3NzczIC0yLjYyNSwyLjYyNSAwLDAuNDk2NyAwLjE0NjYxLDAuOTU2NTQgMC4zODcyNywxLjM1MzAzIGwgLTEuMjA0NjUsMS4yMDUwOCBjIC0wLjI0NDE0LDAuMjQ0MTQgLTAuMjQzMTYsMC42Mzk2NSA5LjhlLTQsMC44ODM3OSAwLjEyMTA5LDAuMTIyMDcgMC4yODEyNSwwLjE4MzExIDAuNDQxNDEsMC4xODMxMSAwLjE2MDE2LDAgMC4zMjAzMSwtMC4wNjEwNCAwLjQ0MjM4LC0wLjE4MzExIGwgMS4yMDQxLC0xLjIwNDQ3IGMgMC4zOTY2MSwwLjI0MDkxIDAuODU2NjMsMC4zODc1NyAxLjM1MzUyLDAuMzg3NTcgeiBtIDAsLTQgYyAwLjc1NzgxLDAgMS4zNzUsMC42MTY3IDEuMzc1LDEuMzc1IDAsMC43NTgzIC0wLjYxNzE5LDEuMzc1IC0xLjM3NSwxLjM3NSAtMC4zNzgxMSwwIC0wLjcyMTA3LC0wLjE1MzY5IC0wLjk2OTk3LC0wLjQwMTczIC03LjNlLTQsLTcuM2UtNCAtOS44ZS00LC0wLjAwMTggLTAuMDAxNywtMC4wMDI2IC02LjFlLTQsLTYuMWUtNCAtMC4wMDE1LC03LjllLTQgLTAuMDAyMSwtMC4wMDE0IC0wLjI0NzYyLC0wLjI0ODc4IC0wLjQwMTE4LC0wLjU5MTM3IC0wLjQwMTE4LC0wLjk2OTMgMCwtMC43NTgzIDAuNjE3MTksLTEuMzc1IDEuMzc1LC0xLjM3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5LDguMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDExIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTksMjUuMzc1IGMgLTEuMTcxODgsMCAtMi4xMjUsMC45NTMxMiAtMi4xMjUsMi4xMjUgMCwxLjE3MTg4IDAuOTUzMTIsMi4xMjUgMi4xMjUsMi4xMjUgMS4xNzE4OCwwIDIuMTI1LC0wLjk1MzEyIDIuMTI1LC0yLjEyNSAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IHogbSAwLDMgYyAtMC40ODI0MiwwIC0wLjg3NSwtMC4zOTI1OCAtMC44NzUsLTAuODc1IDAsLTAuNDgyNDIgMC4zOTI1OCwtMC44NzUgMC44NzUsLTAuODc1IDAuNDgyNDIsMCAwLjg3NSwwLjM5MjU4IDAuODc1LDAuODc1IDAsMC40ODI0MiAtMC4zOTI1OCwwLjg3NSAtMC44NzUsMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDEzIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjcuNSwxNi44NzUgYyAtMS4xNzE4OCwwIC0yLjEyNSwwLjk1MzEyIC0yLjEyNSwyLjEyNSAwLDEuMTcxODggMC45NTMxMiwyLjEyNSAyLjEyNSwyLjEyNSAxLjE3MTg4LDAgMi4xMjUsLTAuOTUzMTIgMi4xMjUsLTIuMTI1IDAsLTEuMTcxODggLTAuOTUzMTIsLTIuMTI1IC0yLjEyNSwtMi4xMjUgeiBtIDAsMyBjIC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgMCwtMC40ODI0MiAwLjM5MjU4LC0wLjg3NSAwLjg3NSwtMC44NzUgMC40ODI0MiwwIDAuODc1LDAuMzkyNTggMC44NzUsMC44NzUgMCwwLjQ4MjQyIC0wLjM5MjU4LDAuODc1IC0wLjg3NSwwLjg3NSB6IgogICAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICAgIHN0cm9rZS13aWR0aD0iMCIKICAgICAgIGlkPSJwYXRoMTUiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxMi42MjUsMTkgYyAwLC0xLjE3MTg4IC0wLjk1MzEyLC0yLjEyNSAtMi4xMjUsLTIuMTI1IC0xLjE3MTg4LDAgLTIuMTI1LDAuOTUzMTIgLTIuMTI1LDIuMTI1IDAsMS4xNzE4OCAwLjk1MzEyLDIuMTI1IDIuMTI1LDIuMTI1IDEuMTcxODgsMCAyLjEyNSwtMC45NTMxMiAyLjEyNSwtMi4xMjUgeiBtIC0zLDAgYyAwLC0wLjQ4MjQyIDAuMzkyNTgsLTAuODc1IDAuODc1LC0wLjg3NSAwLjQ4MjQyLDAgMC44NzUsMC4zOTI1OCAwLjg3NSwwLjg3NSAwLDAuNDgyNDIgLTAuMzkyNTgsMC44NzUgLTAuODc1LDAuODc1IC0wLjQ4MjQyLDAgLTAuODc1LC0wLjM5MjU4IC0wLjg3NSwtMC44NzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDE3IiAvPgogICAgPHBhdGgKICAgICAgIGQ9Ik0gMTMuMjM3NDMsMTIuMzUzNjQgQyAxMy40NzgzNCwxMS45NTcwMyAxMy42MjUsMTEuNDk2ODkgMTMuNjI1LDExIDEzLjYyNSw5LjU1MjczIDEyLjQ0NzI3LDguMzc1IDExLDguMzc1IDkuNTUyNzMsOC4zNzUgOC4zNzUsOS41NTI3MyA4LjM3NSwxMSBjIDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDAuNDk2ODksMCAwLjk1NzAzLC0wLjE0NjY3IDEuMzUzNjQsLTAuMzg3NTcgbCAxLjIwNDQ3LDEuMjA0NDcgYyAwLjEyMjA3LDAuMTIyMDcgMC4yODE3NCwwLjE4MzExIDAuNDQxODksMC4xODMxMSAwLjE2MDE1LDAgMC4zMTk4MiwtMC4wNjEwNCAwLjQ0MTg5LC0wLjE4MzExIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IEwgMTMuMjM3NDIsMTIuMzUzNjQgWiBNIDkuNjI1LDExIGMgMCwtMC43NTgzIDAuNjE2NywtMS4zNzUgMS4zNzUsLTEuMzc1IDAuNzU4MywwIDEuMzc1LDAuNjE2NyAxLjM3NSwxLjM3NSAwLDAuMzc3OTkgLTAuMTUzNSwwLjcyMDU4IC0wLjQwMTEyLDAuOTY5MzYgLTcuOWUtNCw3LjllLTQgLTAuMDAxOSwxMGUtNCAtMC4wMDI3LDAuMDAxOCAtOGUtNCw3LjllLTQgLTAuMDAxLDAuMDAxOSAtMC4wMDE4LDAuMDAyNyBDIDExLjcyMDU4LDEyLjIyMTUgMTEuMzc3OTksMTIuMzc1IDExLDEyLjM3NSAxMC4yNDE3LDEyLjM3NSA5LjYyNSwxMS43NTgzIDkuNjI1LDExIFoiCiAgICAgICBmaWxsPSIjZmZmZmZmIgogICAgICAgc3Ryb2tlLXdpZHRoPSIwIgogICAgICAgaWQ9InBhdGgxOSIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDEzLjU1ODExLDIzLjU1ODExIC0xLjIwNDQ3LDEuMjA0NDcgQyAxMS45NTcwMywyNC41MjE2NyAxMS40OTY4OSwyNC4zNzUwMSAxMSwyNC4zNzUwMSBjIC0xLjQ0NzI3LDAgLTIuNjI1LDEuMTc3NzMgLTIuNjI1LDIuNjI1IDAsMS40NDcyNyAxLjE3NzczLDIuNjI1IDIuNjI1LDIuNjI1IDEuNDQ3MjcsMCAyLjYyNSwtMS4xNzc3MyAyLjYyNSwtMi42MjUgMCwtMC40OTY4OSAtMC4xNDY2NywtMC45NTcwMyAtMC4zODc1NywtMS4zNTM2NCBMIDE0LjQ0MTksMjQuNDQxOSBjIDAuMjQ0MTQsLTAuMjQ0MTQgMC4yNDQxNCwtMC42Mzk2NSAwLC0wLjg4Mzc5IC0wLjI0NDE0LC0wLjI0NDE0IC0wLjYzOTY1LC0wLjI0NDE0IC0wLjg4Mzc5LDAgeiBNIDExLDI4LjM3NSBjIC0wLjc1ODMsMCAtMS4zNzUsLTAuNjE2NyAtMS4zNzUsLTEuMzc1IDAsLTAuNzU4MyAwLjYxNjcsLTEuMzc1IDEuMzc1LC0xLjM3NSAwLjM3ODg1LDAgMC43MjIyOSwwLjE1Mzk5IDAuOTcxMTksMC40MDI1OSAyLjRlLTQsMi40ZS00IDIuNGUtNCw0LjllLTQgNC45ZS00LDcuM2UtNCAyLjVlLTQsMi40ZS00IDQuOWUtNCwyLjRlLTQgNy4zZS00LDQuOWUtNCAwLjI0ODYsMC4yNDg5IDAuNDAyNTksMC41OTIzNSAwLjQwMjU5LDAuOTcxMTkgMCwwLjc1ODMgLTAuNjE2NywxLjM3NSAtMS4zNzUsMS4zNzUgeiIKICAgICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgICBzdHJva2Utd2lkdGg9IjAiCiAgICAgICBpZD0icGF0aDIxIiAvPgogIDwvZz4KPC9zdmc+Cg== @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.4 +version: 4.5.5 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 1575f8a3..02129029 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.4](https://img.shields.io/badge/Version-4.5.4-informational?style=flat-square) +![Version: 4.5.5](https://img.shields.io/badge/Version-4.5.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -160,7 +160,7 @@ Kubernetes: `>= 1.27.0-0` | Repository | Name | Version | |------------|------|---------| -| https://backstage.github.io/charts | upstream(backstage) | 2.6.0 | +| https://backstage.github.io/charts | upstream(backstage) | 2.6.1 | | https://charts.bitnami.com/bitnami | common | 2.31.4 | ## Values diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index de4c3bc9..bc1ad872 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -875,7 +875,7 @@ "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", "properties": { "preferredDuringSchedulingIgnoredDuringExecution": { - "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and subtracting \"weight\" from the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", "items": { "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", "properties": { @@ -1469,7 +1469,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", + "description": "Name of the environment variable. Must be a C_IDENTIFIER.", "type": "string" }, "value": { @@ -1587,7 +1587,7 @@ "x-kubernetes-patch-strategy": "merge" }, "envFrom": { - "description": "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. 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.", + "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 or Secrets", "properties": { @@ -1606,7 +1606,7 @@ "type": "object" }, "prefix": { - "description": "Optional text to prepend to the name of each environment variable. May consist of any printable ASCII characters except '='.", + "description": "Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER.", "type": "string" }, "secretRef": { @@ -2703,7 +2703,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", + "description": "Name of the environment variable. Must be a C_IDENTIFIER.", "type": "string" }, "value": { @@ -3788,7 +3788,7 @@ "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", "properties": { "endpoints": { - "description": "endpoints is the endpoint name that details Glusterfs topology.", + "description": "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", "type": "string" }, "path": { @@ -4695,7 +4695,7 @@ "description": "EnvVar represents an environment variable present in a Container.", "properties": { "name": { - "description": "Name of the environment variable. May consist of any printable ASCII characters except '='.", + "description": "Name of the environment variable. Must be a C_IDENTIFIER.", "type": "string" }, "value": { @@ -4813,7 +4813,7 @@ "x-kubernetes-patch-strategy": "merge" }, "envFrom": { - "description": "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. 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.", + "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 or Secrets", "properties": { @@ -4832,7 +4832,7 @@ "type": "object" }, "prefix": { - "description": "Optional text to prepend to the name of each environment variable. May consist of any printable ASCII characters except '='.", + "description": "Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER.", "type": "string" }, "secretRef": { @@ -7012,10 +7012,14 @@ "description": "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", "properties": { "ports": { - "description": "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "description": "ports is a list of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", "items": { "description": "NetworkPolicyPort describes a port to allow traffic on", "properties": { + "endPort": { + "description": "endPort indicates that the range of ports from port to endPort if set, inclusive, should be allowed by the policy. This field cannot be defined if the port field is not defined or if the port field is defined as a named (string) port. The endPort must be equal or greater than port.", + "type": "integer" + }, "port": { "oneOf": [ { @@ -7027,36 +7031,40 @@ ] }, "protocol": { - "description": "The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.", + "description": "protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.", "type": "string" } - } + }, + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "to": { - "description": "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + "description": "to is a list of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", "items": { - "description": "NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields must be specified.", + "description": "NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of fields are allowed", "properties": { "ipBlock": { - "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.1/24\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", + "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.0/24\",\"2001:db8::/64\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", "properties": { "cidr": { - "description": "CIDR is a string representing the IP Block Valid examples are \"192.168.1.1/24\"", + "description": "cidr is a string representing the IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\"", "type": "string" }, "except": { - "description": "Except is a slice of CIDRs that should not be included within an IP Block Valid examples are \"192.168.1.1/24\" Except values will be rejected if they are outside the CIDR range", + "description": "except is a slice of CIDRs that should not be included within an IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\" Except values will be rejected if they are outside the cidr range", "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "cidr" - ] + ], + "type": "object" }, "namespaceSelector": { "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", @@ -7068,9 +7076,7 @@ "properties": { "key": { "description": "key is the label key that the selector applies to.", - "type": "string", - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge" + "type": "string" }, "operator": { "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", @@ -7081,15 +7087,18 @@ "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" - ] + ], + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -7098,7 +7107,9 @@ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } - } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" }, "podSelector": { "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", @@ -7110,9 +7121,7 @@ "properties": { "key": { "description": "key is the label key that the selector applies to.", - "type": "string", - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge" + "type": "string" }, "operator": { "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", @@ -7123,15 +7132,18 @@ "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" - ] + ], + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -7140,13 +7152,18 @@ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } - } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" } - } + }, + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } - } + }, + "type": "object" }, "title": "", "type": "array" @@ -7173,28 +7190,30 @@ "description": "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.", "properties": { "from": { - "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", + "description": "from is a list of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the from list.", "items": { - "description": "NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields must be specified.", + "description": "NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of fields are allowed", "properties": { "ipBlock": { - "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.1/24\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", + "description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.0/24\",\"2001:db8::/64\") that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should not be included within this rule.", "properties": { "cidr": { - "description": "CIDR is a string representing the IP Block Valid examples are \"192.168.1.1/24\"", + "description": "cidr is a string representing the IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\"", "type": "string" }, "except": { - "description": "Except is a slice of CIDRs that should not be included within an IP Block Valid examples are \"192.168.1.1/24\" Except values will be rejected if they are outside the CIDR range", + "description": "except is a slice of CIDRs that should not be included within an IPBlock Valid examples are \"192.168.1.0/24\" or \"2001:db8::/64\" Except values will be rejected if they are outside the cidr range", "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "cidr" - ] + ], + "type": "object" }, "namespaceSelector": { "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", @@ -7206,9 +7225,7 @@ "properties": { "key": { "description": "key is the label key that the selector applies to.", - "type": "string", - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge" + "type": "string" }, "operator": { "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", @@ -7219,15 +7236,18 @@ "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" - ] + ], + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -7236,7 +7256,9 @@ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } - } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" }, "podSelector": { "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", @@ -7248,9 +7270,7 @@ "properties": { "key": { "description": "key is the label key that the selector applies to.", - "type": "string", - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge" + "type": "string" }, "operator": { "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", @@ -7261,15 +7281,18 @@ "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" - ] + ], + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -7278,17 +7301,25 @@ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } - } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" } - } + }, + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "ports": { - "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "description": "ports is a list of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", "items": { "description": "NetworkPolicyPort describes a port to allow traffic on", "properties": { + "endPort": { + "description": "endPort indicates that the range of ports from port to endPort if set, inclusive, should be allowed by the policy. This field cannot be defined if the port field is not defined or if the port field is defined as a named (string) port. The endPort must be equal or greater than port.", + "type": "integer" + }, "port": { "oneOf": [ { @@ -7300,14 +7331,17 @@ ] }, "protocol": { - "description": "The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.", + "description": "protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.", "type": "string" } - } + }, + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } - } + }, + "type": "object" }, "title": "", "type": "array" From 57aa46ba489d8fb6dd7f363c03b705909de5ab2a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Wed, 27 Aug 2025 20:12:59 +0200 Subject: [PATCH 091/117] ci: Fix authorization logic in pull_request_target workflows (#223) --- .github/workflows/pre-commit.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 47b4a725..72d7ef41 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -21,7 +21,7 @@ jobs: # see list of approvers in OWNERS file environment: ${{ (github.event.pull_request.head.repo.full_name == github.repository || - contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","Fortune-Ndlovu","subhashkhileri","zdrapela","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.actor)) && 'internal' || 'external' }} + contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","Fortune-Ndlovu","subhashkhileri","zdrapela","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.event.pull_request.user.login)) && 'internal' || 'external' }} runs-on: ubuntu-latest steps: - name: approved @@ -42,7 +42,7 @@ jobs: with: fetch-depth: 0 repository: ${{github.event.pull_request.head.repo.full_name}} - ref: ${{ github.event.pull_request.head.ref }} + ref: ${{ github.event.pull_request.head.sha }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: From b753b424bb77510d8cdf3acc154bf75113786b7f Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Wed, 3 Sep 2025 11:42:08 +0300 Subject: [PATCH 092/117] Orchestrator-software-templates chart: Post-RHDH install resources (#199) * Introducing Orchestrator Software Templates Chart + Infra changes * hack scripts fixed according to shellcheck-lint * reverting changes in tekton pipeline + excluding charts from ct --- .../Chart.yaml | 2 +- .../README.md | 4 +- .../ci/upstream-values.yaml | 2 +- .../templates/NOTES.txt | 2 +- .../templates/catalogsource.yaml | 10 +- ...cd-application-controller-clusterrole.yaml | 4 + ...ication-controller-clusterrolebinding.yaml | 4 +- .../templates/openshift-gitops/argocd-cr.yaml | 5 + .../argocd-credential-template.yaml | 4 + .../openshift-gitops/argocd-initial-apps.yaml | 3 + .../argocd-initial-repositories.yaml | 3 + .../argocd-server-clusterrole.yaml | 4 + .../argocd-server-clusterrolebinding.yaml | 4 + .../openshift-gitops/crd-reader.yaml | 8 +- .../templates/openshift-gitops/namespace.yaml | 3 +- .../openshift-gitops/post-cleanup.yaml | 205 +++------ .../templates/openshift-gitops/secrets.yaml | 4 + .../openshift-gitops/subscription.yaml | 3 + .../openshift-pipelines/subscription.yaml | 3 + .../values.yaml | 2 +- .../.helmignore | 23 ++ .../Chart.yaml | 17 + .../orchestrator-software-templates/README.md | 157 +++++++ .../README.md.gotmpl | 130 ++++++ .../ci/upstream-values.yaml | 15 + ...rchestrator-templates-values.yaml.template | 211 ++++++++++ .../templates/NOTES.txt | 69 ++++ .../templates/_helpers.tpl | 153 +++++++ .../templates/argocd/argocd-project.yaml | 14 + .../templates/rbac.yaml | 69 ++++ .../software-templates/orchestrator-auth.yaml | 54 +++ .../orchestrator-catalog.yaml | 40 ++ .../templates/tekton/tekton-pipeline.yaml | 175 ++++++++ .../templates/tekton/tekton-tasks.yaml | 243 +++++++++++ .../values.yaml | 50 +++ ct-install.yaml | 5 + ct.yaml | 4 + hack/merge_secrets.sh | 52 +++ hack/orchestrator-templates-setup.sh | 389 ++++++++++++++++++ 39 files changed, 1986 insertions(+), 163 deletions(-) create mode 100644 charts/orchestrator-software-templates/.helmignore create mode 100644 charts/orchestrator-software-templates/Chart.yaml create mode 100644 charts/orchestrator-software-templates/README.md create mode 100644 charts/orchestrator-software-templates/README.md.gotmpl create mode 100644 charts/orchestrator-software-templates/ci/upstream-values.yaml create mode 100644 charts/orchestrator-software-templates/orchestrator-templates-values.yaml.template create mode 100644 charts/orchestrator-software-templates/templates/NOTES.txt create mode 100644 charts/orchestrator-software-templates/templates/_helpers.tpl create mode 100644 charts/orchestrator-software-templates/templates/argocd/argocd-project.yaml create mode 100644 charts/orchestrator-software-templates/templates/rbac.yaml create mode 100644 charts/orchestrator-software-templates/templates/software-templates/orchestrator-auth.yaml create mode 100644 charts/orchestrator-software-templates/templates/software-templates/orchestrator-catalog.yaml create mode 100644 charts/orchestrator-software-templates/templates/tekton/tekton-pipeline.yaml create mode 100644 charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml create mode 100644 charts/orchestrator-software-templates/values.yaml create mode 100755 hack/merge_secrets.sh create mode 100755 hack/orchestrator-templates-setup.sh diff --git a/charts/orchestrator-software-templates-infra/Chart.yaml b/charts/orchestrator-software-templates-infra/Chart.yaml index ae59fc60..5d02a810 100644 --- a/charts/orchestrator-software-templates-infra/Chart.yaml +++ b/charts/orchestrator-software-templates-infra/Chart.yaml @@ -10,7 +10,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-software-templates-infrastructure -version: 0.2.0 +version: 0.2.2 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates-infra/README.md b/charts/orchestrator-software-templates-infra/README.md index c6928623..43467131 100644 --- a/charts/orchestrator-software-templates-infra/README.md +++ b/charts/orchestrator-software-templates-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Infra Chart for OpenShift (Community Version) -![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) +![Version: 0.2.2](https://img.shields.io/badge/Version-0.2.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. @@ -119,7 +119,7 @@ The command removes all the Kubernetes components associated with the chart and | openshiftGitops.initialApps | Initial applications to deploy | list | `[]` | | openshiftGitops.initialRepositories | Initial repositories configuration | list | `[]` | | openshiftGitops.name | name of instances | string | `"argocd"` | -| openshiftGitops.namespaces | namespace of rhdh instance, will be used to install openshift-gitops. | list | `["rhdh"]` | +| openshiftGitops.namespaces | namespace of rhdh instance, will be used to install openshift-gitops. | list | `["orchestrator-gitops"]` | | openshiftGitops.repositoryCredentials | Repository credential templates | list | `[]` | | openshiftGitops.secrets | Secrets for Git access or other repository credentials | list | `[]` | | openshiftGitops.subscription | subscription config | object | `{"namespace":"openshift-operators","spec":{"channel":"latest","disableDefaultArgoCD":true,"installPlanApproval":"Automatic","name":"openshift-gitops-operator","source":"redhat-operators","sourceNamespace":"openshift-marketplace"}}` | diff --git a/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml b/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml index 7a40d91a..4427abce 100644 --- a/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml +++ b/charts/orchestrator-software-templates-infra/ci/upstream-values.yaml @@ -12,7 +12,7 @@ openshiftPipelines: openshiftGitops: name: argocd namespaces: - - olm + - orchestrator-gitops subscription: namespace: operators name: argocd-operator diff --git a/charts/orchestrator-software-templates-infra/templates/NOTES.txt b/charts/orchestrator-software-templates-infra/templates/NOTES.txt index d400519a..d2e42196 100644 --- a/charts/orchestrator-software-templates-infra/templates/NOTES.txt +++ b/charts/orchestrator-software-templates-infra/templates/NOTES.txt @@ -46,5 +46,5 @@ oc wait --for=condition=Ready pod --all -n openshift-pipelines --timeout=120s Run the following command to validate that the openshift-gitops namespace and all pods within it are operational: oc wait --for=jsonpath='{.status.phase}'=Active namespace/openshift-gitops --timeout=80s && \ -oc wait --for=condition=Ready pod --all -n {{ .Release.Namespace }} --timeout=120s +oc wait --for=condition=Ready pod --all -n orchestrator-gitops --timeout=120s {{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml b/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml index aba0ebc3..161dcb82 100644 --- a/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml +++ b/charts/orchestrator-software-templates-infra/templates/catalogsource.yaml @@ -1,6 +1,6 @@ -{{- $pipelinesEnabled := .Values.openshiftPipelines.enabled }} -{{- $gitopsEnabled := .Values.openshiftGitops.enabled }} -{{- $shouldCreate := or $pipelinesEnabled $gitopsEnabled }} +{{- $pipelinesUsesOperatorHub := and .Values.openshiftPipelines.enabled (eq .Values.openshiftPipelines.subscription.spec.source "operatorhubio-catalog") }} +{{- $gitopsUsesOperatorHub := and .Values.openshiftGitops.enabled (eq .Values.openshiftGitops.subscription.spec.source "operatorhubio-catalog") }} +{{- $shouldCreate := or $pipelinesUsesOperatorHub $gitopsUsesOperatorHub }} {{- if $shouldCreate }} @@ -11,6 +11,10 @@ kind: CatalogSource metadata: name: operatorhubio-catalog namespace: olm + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-8" + "helm.sh/hook-delete-policy": before-hook-creation spec: sourceType: grpc image: quay.io/operator-framework/upstream-community-operators:latest diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml index 99a7e2f9..38e27161 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrole.yaml @@ -8,6 +8,10 @@ metadata: app.kubernetes.io/name: {{ .Values.openshiftGitops.namespaces | first }}-argocd-application-controller app.kubernetes.io/part-of: {{ .Values.openshiftGitops.namespaces | first }} name: {{ .Values.openshiftGitops.namespaces | first }}-argocd-application-controller + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation rules: - apiGroups: [""] resources: ["pods"] diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml index 02237012..dd6b1941 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml @@ -9,7 +9,9 @@ metadata: app.kubernetes.io/name: {{ $ns }}-argocd-application-controller app.kubernetes.io/part-of: {{ $.Values.openshiftGitops.namespaces | first }} annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation name: {{ $ns }}-argocd-application-controller roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml index 017c6b16..bd282c29 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml @@ -1,5 +1,6 @@ {{- if and .Values.openshiftGitops.namespaces (.Capabilities.APIVersions.Has "argoproj.io/v1beta1/ArgoCD") }} {{- range $ns := .Values.openshiftGitops.namespaces }} +{{- if ne $ns "openshift-gitops" }} --- apiVersion: argoproj.io/v1beta1 kind: ArgoCD @@ -8,6 +9,9 @@ metadata: labels: app: {{ $.Values.openshiftGitops.name }} namespace: {{ $ns }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "1" spec: applicationInstanceLabelKey: rht-gitops.com/{{ $ns }} {{- if $.Values.openshiftGitops.argocd_cr }} @@ -15,3 +19,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml index c4857739..eebc8d18 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-credential-template.yaml @@ -7,6 +7,10 @@ kind: Secret metadata: name: {{ $cred.name }} namespace: {{ $ns }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation labels: argocd.argoproj.io/secret-type: repo-creds stringData: diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml index 9cc62c65..4709e3dd 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-apps.yaml @@ -7,6 +7,9 @@ kind: Application metadata: name: {{ $app.name }} namespace: {{ $ns }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "2" spec: destination: namespace: {{ $ns }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml index 8922f926..50c0ebae 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-initial-repositories.yaml @@ -9,6 +9,9 @@ metadata: namespace: {{ $ns }} labels: argocd.argoproj.io/secret-type: repository + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "2" stringData: type: {{ $repo.type }} url: {{ $repo.url }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml index 9ab2784a..7b8ac74f 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrole.yaml @@ -7,6 +7,10 @@ metadata: app.kubernetes.io/name: {{ .Values.openshiftGitops.namespaces | first }}-gitops-argocd-server app.kubernetes.io/part-of: {{ .Values.openshiftGitops.namespaces | first }} name: {{ .Values.openshiftGitops.namespaces | first }}-gitops-argocd-server + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation rules: - apiGroups: [""] resources: ["pods", "logs"] diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml index 42df5c14..5983b459 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-server-clusterrolebinding.yaml @@ -9,6 +9,10 @@ metadata: app.kubernetes.io/name: {{ $ns }}-argocd-server app.kubernetes.io/part-of: {{ $ns }} name: {{ $ns }}-argocd-server + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml index 6d67f626..cefc6a7a 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/crd-reader.yaml @@ -4,8 +4,8 @@ metadata: name: crd-reader annotations: "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "-5" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation rules: - apiGroups: - apiextensions.k8s.io @@ -22,8 +22,8 @@ metadata: name: crd-reader-binding annotations: "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "-5" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml index 9c1fd402..436687d4 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/namespace.yaml @@ -1,6 +1,6 @@ {{- if .Values.openshiftGitops.namespaces }} {{- range $index, $ns := .Values.openshiftGitops.namespaces }} -{{- if ne $index 0 }} +{{- if and (ne $ns "openshift-gitops") (ne $ns "olm") }} --- apiVersion: v1 kind: Namespace @@ -9,6 +9,7 @@ metadata: "openshift.io/description": "" "openshift.io/display-name": "" "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-10" labels: kubernetes.io/metadata.name: {{ $ns }} openshift.io/cluster-monitoring: "true" diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml index e8c47cf4..6cd26058 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/post-cleanup.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.openshiftGitops.enabled }} +{{- if .Values.openshiftGitops.enabled }} apiVersion: v1 kind: ServiceAccount @@ -17,24 +17,15 @@ metadata: "helm.sh/hook": pre-install,pre-upgrade,post-delete "helm.sh/hook-weight": "10" rules: - - apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "delete"] - apiGroups: ["operators.coreos.com"] resources: ["clusterserviceversions", "subscriptions"] - verbs: ["get", "list", "delete", "watch"] - - apiGroups: ["apps", ""] - resources: ["deployments", "pods"] - verbs: ["get", "list", "delete"] - - apiGroups: [""] - resources: ["pods", "services", "namespaces"] - verbs: ["get", "list", "delete"] - - apiGroups: ["rbac.authorization.k8s.io"] - resources: ["clusterroles", "clusterrolebindings"] - verbs: ["get", "list", "delete"] + verbs: ["get", "list", "delete", "watch", "patch", "update"] - apiGroups: ["argoproj.io"] - resources: ["applications", "appprojects", "argocds", "argocd"] - verbs: ["get", "list", "watch", "delete"] + resources: ["argocds", "applications", "appprojects"] + verbs: ["get", "list", "delete", "watch", "patch", "update"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -55,7 +46,7 @@ subjects: apiVersion: batch/v1 kind: Job metadata: - name: openshift-gitops-cleanup-gitops + name: openshift-gitops-cleanup namespace: {{ .Values.openshiftGitops.subscription.namespace }} annotations: "helm.sh/hook": post-delete @@ -67,8 +58,17 @@ spec: serviceAccountName: csv-cleanup-gitops restartPolicy: Never containers: - - name: cleanup + - name: cleanup-gitops image: {{ .Values.cleanupContainerImage }} + env: + - name: NAMESPACE + value: {{ .Values.openshiftGitops.subscription.namespace }} + - name: NAME + value: {{ .Values.openshiftGitops.subscription.spec.name }} + - name: ARGOCD_NAME + value: {{ .Values.openshiftGitops.name }} + - name: ARGOCD_NAMESPACES + value: {{ join "," .Values.openshiftGitops.namespaces }} resources: requests: memory: {{ .Values.resources.requests.memory }} @@ -76,11 +76,6 @@ spec: limits: memory: {{ .Values.resources.limits.memory }} cpu: {{ .Values.resources.limits.cpu }} - env: - - name: NAMESPACE - value: {{ .Values.openshiftGitops.subscription.namespace }} - - name: NAME - value: {{ .Values.openshiftGitops.subscription.spec.name }} securityContext: allowPrivilegeEscalation: false capabilities: @@ -93,137 +88,51 @@ spec: - /bin/sh - -c - | - set +e # Continue on error - - echo "Starting OpenShift GitOps Cleanup Job..." - - # --- Phase 1: Delete OpenShift GitOps Operator Subscription FIRST --- - echo "Phase 1: Deleting OpenShift GitOps Operator Subscription to stop operator reconciliation..." - - echo "Attempting to delete Subscription $NAME in $NAMESPACE..." - kubectl delete subscription $NAME -n $NAMESPACE --ignore-not-found - echo "Waiting for Subscription to be deleted (timeout 90s)..." - kubectl wait --for=delete subscription/$NAME -n $NAMESPACE --timeout=90s || echo "Subscription deletion timed out or resource not found, continuing..." - - # Give the operator time to stop reconciling - echo "Waiting 10 seconds for operator to stop reconciling..." - sleep 10 - - echo "Phase 1: Subscription deletion complete." - echo "" + set +e - # --- Phase 2: Delete all Applications --- - echo "Phase 2: Deleting all Application resources across all namespaces..." - - # Get unique list of namespaces that contain Applications - namespaces_with_apps=$(kubectl get applications.argoproj.io -A -o custom-columns="NAMESPACE:.metadata.namespace" --no-headers 2>/dev/null | sort -u) + echo "Waiting for Subscription to be deleted..." + kubectl delete subscription.operators.coreos.com $NAME -n $NAMESPACE --ignore-not-found + kubectl wait --for=delete subscription.operators.coreos.com/$NAME -n $NAMESPACE --timeout=30s || echo "Subscription deletion timed out, continuing..." - if [ -z "$namespaces_with_apps" ]; then - echo "No Application resources found. Skipping Application deletion." - else - echo "$namespaces_with_apps" | while read -r ns; do - echo " Processing namespace: $ns for Applications" - # Get all Applications in the current namespace - applications_in_ns=$(kubectl get applications.argoproj.io -n "$ns" -o custom-columns="NAME:.metadata.name" --no-headers 2>/dev/null) + echo "Waiting for CSV to be deleted..." + kubectl delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --ignore-not-found + kubectl wait --for=delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --timeout=60s || echo "CSV deletion timed out, continuing..." - if [ -z "$applications_in_ns" ]; then - echo " No Applications found in $ns." - else - echo "$applications_in_ns" | while read -r app; do - if [ -n "$app" ]; then - echo " Deleting Application: $app in namespace: $ns" - kubectl delete applications.argoproj.io "$app" -n "$ns" --ignore-not-found - fi - done - fi - done - fi - echo "Phase 2: Application deletion complete." - echo "" - - # --- Phase 3: Delete all AppProjects --- - echo "Phase 3: Deleting all AppProject resources across all namespaces..." - - # Get unique list of namespaces that contain AppProjects - namespaces_with_appprojects=$(kubectl get appprojects.argoproj.io -A -o custom-columns="NAMESPACE:.metadata.namespace" --no-headers 2>/dev/null | sort -u) - - if [ -z "$namespaces_with_appprojects" ]; then - echo "No AppProject resources found. Skipping AppProject deletion." - else - echo "$namespaces_with_appprojects" | while read -r ns; do - echo " Processing namespace: $ns for AppProjects" - # Get all AppProjects in the current namespace - appprojects_in_ns=$(kubectl get appprojects.argoproj.io -n "$ns" -o custom-columns="NAME:.metadata.name" --no-headers 2>/dev/null) - - if [ -z "$appprojects_in_ns" ]; then - echo " No AppProjects found in $ns." - else - echo "$appprojects_in_ns" | while read -r proj; do - if [ -n "$proj" ]; then - echo " Deleting AppProject: $proj in namespace: $ns" - kubectl delete appprojects.argoproj.io "$proj" -n "$ns" --ignore-not-found - fi - done - fi - done - fi - echo "Phase 3: AppProject deletion complete." - echo "" - - # --- Phase 4: Delete all ArgoCD instances --- - echo "Phase 4: Deleting all ArgoCD instances across all namespaces..." - - # Get a list of all ArgoCD instances (namespace and name) once - argo_instances=$(kubectl get argocd -A -o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name" --no-headers 2>/dev/null) - - if [ -z "$argo_instances" ]; then - echo "No ArgoCD instances found. Skipping ArgoCD instance deletion." - else - echo "$argo_instances" | while read -r ns argo_name; do - if [ -n "$ns" ] && [ -n "$argo_name" ]; then - echo " Attempting to delete ArgoCD instance '$argo_name' in namespace: $ns" - # First, try a regular delete. If it gets stuck (due to finalizers), - # we will attempt to patch. - kubectl delete argocd "$argo_name" -n "$ns" --ignore-not-found --timeout=30s + echo "Finding and cleaning up all ArgoCD resources..." + # Get all ArgoCD resources across all namespaces + kubectl get argocd --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' | while read -r ns name; do + if [ -n "$ns" ] && [ -n "$name" ]; then + echo " - Found ArgoCD $name in namespace $ns" + + # Try normal deletion first + kubectl delete argocd $name -n $ns --ignore-not-found --timeout=15s 2>/dev/null || true + + # Check if still exists (likely stuck with finalizers) + if kubectl get argocd $name -n $ns >/dev/null 2>&1; then + echo " ArgoCD $name stuck, removing finalizers..." + + # Remove finalizers using JSON patch + kubectl patch argocd $name -n $ns --type='merge' -p '{"metadata":{"finalizers":[]}}' || true - # Check if the ArgoCD instance is still present (meaning it's stuck with a finalizer) - if kubectl get argocd "$argo_name" -n "$ns" &>/dev/null; then - echo " ArgoCD instance '$argo_name' in namespace '$ns' is stuck. Attempting to remove finalizers." - # Patch the resource to remove finalizers - kubectl patch argocd "$argo_name" -n "$ns" --type=json -p='[{"op": "remove", "path": "/metadata/finalizers"}]' || \ - echo " Warning: Failed to patch finalizers for $argo_name in $ns. It might remain." - echo " Re-attempting delete for ArgoCD instance '$argo_name' in namespace: $ns after finalizer removal." - kubectl delete argocd "$argo_name" -n "$ns" --ignore-not-found --timeout=30s - fi + # Force delete + kubectl delete argocd $name -n $ns --force --grace-period=0 2>/dev/null || true + + echo " ArgoCD $name force deleted" + else + echo " ArgoCD $name deleted successfully" fi - done - fi - echo "Phase 4: ArgoCD instance deletion complete." - echo "" + fi + done - # --- Phase 5: Delete ClusterServiceVersion (CSV) --- - echo "Phase 5: Deleting ClusterServiceVersion (CSV) for $NAME in $NAMESPACE..." - - # Find CSVs that match the operator name pattern - csv_names=$(kubectl get csv -n $NAMESPACE -o name | grep -E "(gitops|argocd)" 2>/dev/null || echo "") - - if [ -z "$csv_names" ]; then - echo "No GitOps/ArgoCD CSVs found in $NAMESPACE." - else - echo "$csv_names" | while read -r csv_name; do - if [ -n "$csv_name" ]; then - echo " Deleting CSV: $csv_name" - kubectl delete "$csv_name" -n $NAMESPACE --ignore-not-found - fi - done - fi - - echo "Waiting for CSV to be deleted (timeout 120s)..." - kubectl wait --for=delete csv -l operators.coreos.com/$NAME.$NAMESPACE='' -n $NAMESPACE --timeout=120s 2>/dev/null || echo "CSV deletion wait timed out or resource not found, continuing..." + echo "Cleaning up AppProjects and Applications in managed namespaces..." + echo "$ARGOCD_NAMESPACES" | tr ',' '\n' | while read -r ns; do + if [ -n "$ns" ]; then + echo " - Cleaning namespace $ns" + kubectl delete appproject default -n $ns --ignore-not-found --timeout=15s 2>/dev/null || true + kubectl delete application -l app.kubernetes.io/managed-by=Helm -n $ns --ignore-not-found --timeout=15s 2>/dev/null || true + fi + done - echo "Phase 5: CSV deletion complete." - echo "" + echo "Cleanup finished." - echo "OpenShift GitOps cleanup job finished successfully." - {{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml index ac8369b7..232cb52a 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/secrets.yaml @@ -7,6 +7,10 @@ kind: Secret metadata: name: {{ $secret.name }} namespace: {{ $ns }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation labels: app.kubernetes.io/name: argocd-secret app.kubernetes.io/part-of: argocd diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml index d3c0bd34..0434bc6a 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/subscription.yaml @@ -5,6 +5,9 @@ kind: Subscription metadata: name: {{ .Values.openshiftGitops.subscription.spec.name }} namespace: {{ .Values.openshiftGitops.subscription.namespace }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-5" spec: channel: {{ .Values.openshiftGitops.subscription.spec.channel }} installPlanApproval: {{ .Values.openshiftGitops.subscription.installPlanApproval }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml index 9f72e838..391f9311 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-pipelines/subscription.yaml @@ -6,6 +6,9 @@ kind: Subscription metadata: name: {{ .Values.openshiftPipelines.subscription.name }} namespace: {{ .Values.openshiftPipelines.subscription.namespace }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-5" spec: channel: {{ .Values.openshiftPipelines.subscription.spec.channel }} installPlanApproval: {{ .Values.openshiftPipelines.subscription.spec.installPlanApproval }} diff --git a/charts/orchestrator-software-templates-infra/values.yaml b/charts/orchestrator-software-templates-infra/values.yaml index 0e9b651a..6aa0c9e6 100644 --- a/charts/orchestrator-software-templates-infra/values.yaml +++ b/charts/orchestrator-software-templates-infra/values.yaml @@ -26,7 +26,7 @@ openshiftGitops: # -- namespace of rhdh instance, will be used to install openshift-gitops. namespaces: - - rhdh + - orchestrator-gitops # -- subscription config subscription: diff --git a/charts/orchestrator-software-templates/.helmignore b/charts/orchestrator-software-templates/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/orchestrator-software-templates/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/orchestrator-software-templates/Chart.yaml b/charts/orchestrator-software-templates/Chart.yaml new file mode 100644 index 00000000..0c1be062 --- /dev/null +++ b/charts/orchestrator-software-templates/Chart.yaml @@ -0,0 +1,17 @@ +annotations: + charts.openshift.io/name: Orchestrator Software Templates Infrastructure for Red Hat Developer Hub + charts.openshift.io/provider: Red Hat Developer Hub Team + charts.openshift.io/supportURL: https://issues.redhat.com/browse/RHIDP +apiVersion: v2 +name: orchestrator-software-templates +description: > + This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH) and other necessary GitOps configurations. + +kubeVersion: ">= 1.25.0-0" +type: application +sources: + - https://github.com/redhat-developer/rhdh-chart +version: 0.1.0 +maintainers: + - name: Red Hat Developer Hub Team + url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates/README.md b/charts/orchestrator-software-templates/README.md new file mode 100644 index 00000000..135e351c --- /dev/null +++ b/charts/orchestrator-software-templates/README.md @@ -0,0 +1,157 @@ + +# Orchestrator Software Templates Chart for Red Hat Developer Hub + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH) and other necessary GitOps configurations. + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| Red Hat Developer Hub Team | | | + +## Source Code + +* + +## Requirements + +Kubernetes: `>= 1.25.0-0` + +## Overview + +This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH). It creates the necessary configurations and resources to enable orchestrator functionality within RHDH, including: + +- Software template configurations for RHDH integration +- Tekton pipelines and tasks for workflow orchestration +- ArgoCD project configurations for GitOps workflows +- Authentication and catalog configurations for various SCM providers (GitHub, GitLab) + +## Prerequisites + +Before installing this chart, ensure you have installed the following: + +0. Orchestrator-infra chart: responsible for installing necessary resources for Orchestrator to work. +1. Backstage chart: responsible for **Red Hat Developer Hub** and Orchestrator. It should be deployed and configured with Orchestrator enabled. +2. Orchestrator-software-templates-infra chart: responsible for deploying **OpenShift Pipelines** (Tekton) operator and **OpenShift GitOps** (ArgoCD) operator. It should be deployed in the same namespace as the backstage chart. +3. Running the setup script: responsible for creating the required secret for the software templates chart. +4. Optional: To make full use of ArgoCD and Tekton, you must following the instruction to configure the docker secret and ssh credentials, here: https://github.com/rhdhorchestrator/orchestrator-go-operator/blob/main/docs/gitops/README.md. +5. Label the RHDH namespace with `oc label ns rhdh rhdh.redhat.com/argocd-namespace=true` to enable the configuration sync. + +The chart requires a secret named `orchestrator-auth-secret` in the RHDH namespace containing the following keys: + +- `BACKEND_SECRET`: Backend authentication secret +- `K8S_CLUSTER_TOKEN`: Kubernetes cluster token +- `K8S_CLUSTER_URL`: Kubernetes cluster URL +- `GITHUB_TOKEN`: GitHub access token (optional) +- `GITHUB_CLIENT_ID`: GitHub OAuth client ID (optional) +- `GITHUB_CLIENT_SECRET`: GitHub OAuth client secret (optional) +- `GITLAB_HOST`: GitLab host URL (optional) +- `GITLAB_TOKEN`: GitLab access token (optional) +- `ARGOCD_URL`: ArgoCD server URL (optional) +- `ARGOCD_USERNAME`: ArgoCD username (optional) +- `ARGOCD_PASSWORD`: ArgoCD password (optional) + +Acquire the following ArgoCD: + +ARGOCD_URL: https://argocd-server. +ARGOCD_USERNAME: admin +ARGOCD_PASSWORD: `oc get secret -n openshift-gitops openshift-gitops-cluster -o jsonpath='{.data.admin\.password}' | base64 -d` + +The secret can be created by the setup script or manually + +```bash +oc create secret generic orchestrator-auth-secret \ + -n rhdh \ + --from-literal=BACKEND_SECRET=your-backend-secret \ + --from-literal=K8S_CLUSTER_TOKEN=your-k8s-token \ + --from-literal=K8S_CLUSTER_URL=https://your-cluster-url \ + --from-literal=GITHUB_TOKEN=your-github-token +``` + +## Installation + +After configuring all prerequisites, you can install the chart with the following command: + +```console +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates +``` + +Now, follow the instruction on the post-installation Notes. They will include the steps to create a custom values.yaml file to allow you to update the backstage chart + +## Running a template + +In the RHDH UI, you will now have some software templates available. You can select one and click on "Run" to start the workflow. +For example, you can run the "Github Basic workflow bootstrap project" template to create a new workflow project. +After bootstrapping the project, a Tekton pipeline will build and push the workflow, and the ArgoCD project we have configured will deploy it onto the cluster in the rhdh namespace. +After all GitOps components run successfully, you can now run the workflow via Orchestrator. + +## Uninstalling the Chart + +To uninstall/delete a Helm release named `orchestrator-templates`: + +```console +helm uninstall my-orchestrator-templates +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Troubleshooting + +### Common Issues + +1. **Secret not found error**: Ensure the `orchestrator-auth-secret` exists in the correct namespace +2. **CRD not available**: Verify that Tekton and ArgoCD operators are installed and their CRDs are available +3. **RHDH not picking up configurations**: Check that the ConfigMaps have the correct label `rhdh.redhat.com/ext-config-sync: "true"` +4. **Tekton PipelineRuns are not successful**: Make sure that affinity-assistant is disabled in the TektonConfig CR. To disable it, edit the tekton config and make sure the `coschedule` feature flag is set to `false` +5. **Namespace labeling**: Label the RHDH namespace for ArgoCD management: `oc label ns rhdh argocd.argoproj.io/managed-by=orchestrator-gitops` +6. **Bootstrap project permissions**: When running templates to create bootstrap projects, ensure the bootstrap project repository has write permissions. You can change the permissions by editing the repository in Github. +7. **AppProject creation**: If no AppProject exists, create a new AppProject in the `orchestrator-gitops` namespace +8. **Required secrets**: Ensure the docker merged secret and orchestrator auth secret are present on the cluster +9. **Concurrent pipeline runs**: Avoid running multiple pipelines simultaneously - if a pipeline fails, ensure no other pipelines are running at the same time +10. **GitOps pipeline flow**: Follow the complete workflow: PipelineRun → GitOps pipeline → Workflow deployment on cluster + +### Checking Prerequisites + +The chart will validate that required CRDs are available: +- `tekton.dev/v1/Task` +- `tekton.dev/v1/Pipeline` +- `argoproj.io/v1alpha1/AppProject` + +Check the Helm release notes after installation for any warnings about missing CRDs. + +## Values + +| Key | Description | Type | Default | +|-----|-------------|------|---------| +| argocd.argocdNamespace | References the ArgoCD instance created by infra chart | string | `"orchestrator-gitops"` | +| argocd.enabled | | bool | `true` | +| orchestratorTemplates.enabled | | bool | `true` | +| orchestratorTemplates.rhdhChartNamespace | | string | `"rhdh"` | +| orchestratorTemplates.rhdhChartReleaseName | | string | `"rhdh"` | +| rhdhConfig.catalogBranch | Branch to use for catalog templates | string | `"main"` | +| rhdhConfig.enableGuestProvider | Enable guest authentication provider | bool | `false` | +| rhdhConfig.enabled | Enable RHDH operator | bool | `true` | +| rhdhConfig.secretRef.argocd.password | Key in the secret for ArgoCD password | string | `"ARGOCD_PASSWORD"` | +| rhdhConfig.secretRef.argocd.url | Key in the secret for ArgoCD URL | string | `"ARGOCD_URL"` | +| rhdhConfig.secretRef.argocd.username | Key in the secret for ArgoCD username | string | `"ARGOCD_USERNAME"` | +| rhdhConfig.secretRef.backstage.backendSecret | Key in the secret for backend authentication | string | `"BACKEND_SECRET"` | +| rhdhConfig.secretRef.github.clientId | Key in the secret for GitHub client ID | string | `"GITHUB_CLIENT_ID"` | +| rhdhConfig.secretRef.github.clientSecret | Key in the secret for GitHub client secret | string | `"GITHUB_CLIENT_SECRET"` | +| rhdhConfig.secretRef.github.token | Key in the secret for GitHub token | string | `"GITHUB_TOKEN"` | +| rhdhConfig.secretRef.gitlab.host | Key in the secret for GitLab host | string | `"GITLAB_HOST"` | +| rhdhConfig.secretRef.gitlab.token | Key in the secret for GitLab token | string | `"GITLAB_TOKEN"` | +| rhdhConfig.secretRef.k8s.clusterToken | Key in the secret for Kubernetes cluster token | string | `"K8S_CLUSTER_TOKEN"` | +| rhdhConfig.secretRef.k8s.clusterUrl | Key in the secret for Kubernetes cluster URL | string | `"K8S_CLUSTER_URL"` | +| rhdhConfig.secretRef.name | Name of the secret containing the configuration | string | `"orchestrator-auth-secret"` | +| tekton.enabled | | bool | `true` | + +## Additional Resources + +- [Red Hat Developer Hub Documentation](https://access.redhat.com/documentation/en-us/red_hat_developer_hub) +- [OpenShift Pipelines Documentation](https://docs.openshift.com/container-platform/latest/cicd/pipelines/understanding-openshift-pipelines.html) +- [OpenShift GitOps Documentation](https://docs.openshift.com/container-platform/latest/cicd/gitops/understanding-openshift-gitops.html) diff --git a/charts/orchestrator-software-templates/README.md.gotmpl b/charts/orchestrator-software-templates/README.md.gotmpl new file mode 100644 index 00000000..0325d495 --- /dev/null +++ b/charts/orchestrator-software-templates/README.md.gotmpl @@ -0,0 +1,130 @@ +# Orchestrator Software Templates Chart for Red Hat Developer Hub + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +## Overview + +This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH). It creates the necessary configurations and resources to enable orchestrator functionality within RHDH, including: + +- Software template configurations for RHDH integration +- Tekton pipelines and tasks for workflow orchestration +- ArgoCD project configurations for GitOps workflows +- Authentication and catalog configurations for various SCM providers (GitHub, GitLab) + +## Prerequisites + +Before installing this chart, ensure you have installed the following: + +0. Orchestrator-infra chart: responsible for installing necessary resources for Orchestrator to work. +1. Backstage chart: responsible for **Red Hat Developer Hub** and Orchestrator. It should be deployed and configured with Orchestrator enabled. +2. Orchestrator-software-templates-infra chart: responsible for deploying **OpenShift Pipelines** (Tekton) operator and **OpenShift GitOps** (ArgoCD) operator. It should be deployed in the same namespace as the backstage chart. +3. Running the setup script: responsible for creating the required secret for the software templates chart. +4. Optional: To make full use of ArgoCD and Tekton, you must following the instruction to configure the docker secret and ssh credentials, here: https://github.com/rhdhorchestrator/orchestrator-go-operator/blob/main/docs/gitops/README.md. +5. Label the RHDH namespace with `oc label ns rhdh rhdh.redhat.com/argocd-namespace=true` to enable the configuration sync. + + +The chart requires a secret named `orchestrator-auth-secret` in the RHDH namespace containing the following keys: + +- `BACKEND_SECRET`: Backend authentication secret +- `K8S_CLUSTER_TOKEN`: Kubernetes cluster token +- `K8S_CLUSTER_URL`: Kubernetes cluster URL +- `GITHUB_TOKEN`: GitHub access token (optional) +- `GITHUB_CLIENT_ID`: GitHub OAuth client ID (optional) +- `GITHUB_CLIENT_SECRET`: GitHub OAuth client secret (optional) +- `GITLAB_HOST`: GitLab host URL (optional) +- `GITLAB_TOKEN`: GitLab access token (optional) +- `ARGOCD_URL`: ArgoCD server URL (optional) +- `ARGOCD_USERNAME`: ArgoCD username (optional) +- `ARGOCD_PASSWORD`: ArgoCD password (optional) + +Acquire the following ArgoCD: + +ARGOCD_URL: https://argocd-server. +ARGOCD_USERNAME: admin +ARGOCD_PASSWORD: `oc get secret -n openshift-gitops openshift-gitops-cluster -o jsonpath='{.data.admin\.password}' | base64 -d` + +The secret can be created by the setup script or manually + +```bash +oc create secret generic orchestrator-auth-secret \ + -n rhdh \ + --from-literal=BACKEND_SECRET=your-backend-secret \ + --from-literal=K8S_CLUSTER_TOKEN=your-k8s-token \ + --from-literal=K8S_CLUSTER_URL=https://your-cluster-url \ + --from-literal=GITHUB_TOKEN=your-github-token +``` + +## Installation + +After configuring all prerequisites, you can install the chart with the following command: + +```console +helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart + +helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates +``` + +Now, follow the instruction on the post-installation Notes. They will include the steps to create a custom values.yaml file to allow you to update the backstage chart + +## Running a template + +In the RHDH UI, you will now have some software templates available. You can select one and click on "Run" to start the workflow. +For example, you can run the "Github Basic workflow bootstrap project" template to create a new workflow project. +After bootstrapping the project, a Tekton pipeline will build and push the workflow, and the ArgoCD project we have configured will deploy it onto the cluster in the rhdh namespace. +After all GitOps components run successfully, you can now run the workflow via Orchestrator. + + +## Uninstalling the Chart + +To uninstall/delete a Helm release named `orchestrator-templates`: + +```console +helm uninstall my-orchestrator-templates +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Troubleshooting + +### Common Issues + +1. **Secret not found error**: Ensure the `orchestrator-auth-secret` exists in the correct namespace +2. **CRD not available**: Verify that Tekton and ArgoCD operators are installed and their CRDs are available +3. **RHDH not picking up configurations**: Check that the ConfigMaps have the correct label `rhdh.redhat.com/ext-config-sync: "true"` +4. **Tekton PipelineRuns are not successful**: Make sure that affinity-assistant is disabled in the TektonConfig CR. To disable it, edit the tekton config and make sure the `coschedule` feature flag is set to `false` +5. **Namespace labeling**: Label the RHDH namespace for ArgoCD management: `oc label ns rhdh argocd.argoproj.io/managed-by=orchestrator-gitops` +6. **Bootstrap project permissions**: When running templates to create bootstrap projects, ensure the bootstrap project repository has write permissions. You can change the permissions by editing the repository in Github. +7. **AppProject creation**: If no AppProject exists, create a new AppProject in the `orchestrator-gitops` namespace +8. **Required secrets**: Ensure the docker merged secret and orchestrator auth secret are present on the cluster +9. **Concurrent pipeline runs**: Avoid running multiple pipelines simultaneously - if a pipeline fails, ensure no other pipelines are running at the same time +10. **GitOps pipeline flow**: Follow the complete workflow: PipelineRun → GitOps pipeline → Workflow deployment on cluster + +### Checking Prerequisites + +The chart will validate that required CRDs are available: +- `tekton.dev/v1/Task` +- `tekton.dev/v1/Pipeline` +- `argoproj.io/v1alpha1/AppProject` + +Check the Helm release notes after installation for any warnings about missing CRDs. + +{{ template "chart.valuesSection" . }} + +## Additional Resources + +- [Red Hat Developer Hub Documentation](https://access.redhat.com/documentation/en-us/red_hat_developer_hub) +- [OpenShift Pipelines Documentation](https://docs.openshift.com/container-platform/latest/cicd/pipelines/understanding-openshift-pipelines.html) +- [OpenShift GitOps Documentation](https://docs.openshift.com/container-platform/latest/cicd/gitops/understanding-openshift-gitops.html) diff --git a/charts/orchestrator-software-templates/ci/upstream-values.yaml b/charts/orchestrator-software-templates/ci/upstream-values.yaml new file mode 100644 index 00000000..1be7ba48 --- /dev/null +++ b/charts/orchestrator-software-templates/ci/upstream-values.yaml @@ -0,0 +1,15 @@ +argocd: + enabled: true + argocdNamespace: orchestrator-gitops + +tekton: + enabled: true + +rhdhConfig: + secretRef: + name: orchestrator-auth-secret + k8s: + clusterToken: fake-token-for-testing + +orchestratorTemplates: + enabled: true diff --git a/charts/orchestrator-software-templates/orchestrator-templates-values.yaml.template b/charts/orchestrator-software-templates/orchestrator-templates-values.yaml.template new file mode 100644 index 00000000..3478e237 --- /dev/null +++ b/charts/orchestrator-software-templates/orchestrator-templates-values.yaml.template @@ -0,0 +1,211 @@ +# Template file for orchestrator templates values +# This values file will be merged with the existing values in the backstage release. +# +# INSTRUCTIONS FOR MANUAL EDITING: +# 1. Replace __RHDH_BASE_URL__ with your actual RHDH route URL +# Example: https://rhdh-developer-hub-rhdh.apps.cluster-abc123.abc123.sandbox.opentlc.com +# 2. Replace {{CLUSTER_DOMAIN}} with your cluster's domain (if needed) +# Example: apps.cluster-abc123.abc123.sandbox.opentlc.com +# 3. Update any other environment-specific values as needed +# +# AUTOMATED GENERATION: +# You can also use sed to replace placeholders automatically: +# RHDH_ROUTE="https://$(oc get route -n your-rhdh-namespace -o jsonpath='{.items[0].spec.host}')" +# sed -i "s|__RHDH_BASE_URL__|$RHDH_ROUTE|g" this-file.yaml +# +# VALIDATION: +# After editing, verify that no __*__ placeholders remain: +# grep "__.*__" this-file.yaml + +# Configure dynamic plugins directly (no ConfigMap mounting needed) +global: + dynamic: + includes: + - "dynamic-plugins.default.yaml" + plugins: + - disabled: false + package: ./dynamic-plugins/dist/backstage-community-plugin-tekton + - disabled: false + package: ./dynamic-plugins/dist/backstage-community-plugin-redhat-argocd + - disabled: false + package: ./dynamic-plugins/dist/roadiehq-backstage-plugin-argo-cd-backend-dynamic + - disabled: false + package: ./dynamic-plugins/dist/roadiehq-scaffolder-backend-argocd-dynamic + - disabled: false + package: ./dynamic-plugins/dist/backstage-plugin-kubernetes-backend-dynamic + pluginConfig: + kubernetes: + clusterLocatorMethods: + - type: config + clusters: + - name: Default Cluster + authProvider: serviceAccount + url: https://kubernetes.default.svc + skipTLSVerify: false + customResources: + - apiVersion: v1 + group: tekton.dev + plural: pipelines + - apiVersion: v1 + group: tekton.dev + plural: pipelineruns + - apiVersion: v1 + group: tekton.dev + plural: taskruns + - apiVersion: v1 + group: route.openshift.io + plural: routes + serviceLocatorMethod: + type: multiTenant + - disabled: false + package: ./dynamic-plugins/dist/backstage-plugin-kubernetes + - disabled: false + package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic + - disabled: false + package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-gitlab-dynamic + +upstream: + backstage: + appConfig: + app: + baseUrl: __RHDH_BASE_URL__ + auth: + environment: development + providers: + guest: + dangerouslyAllowOutsideDevelopment: true + github: + development: + clientId: ${GITHUB_CLIENT_ID} + clientSecret: ${GITHUB_CLIENT_SECRET} + scopes: + - user:email + - user:name + - user:login + - repo + includes: + - orchestrator-catalog.yaml + - orchestrator-auth.yaml + backend: + auth: + externalAccess: + - options: + secret: ${BACKEND_SECRET} + subject: legacy-default-config + type: legacy + baseUrl: __RHDH_BASE_URL__ + cors: + origin: __RHDH_BASE_URL__ + database: + connection: + password: ${POSTGRESQL_ADMIN_PASSWORD} + user: postgres + argocd: + appLocatorMethods: + - instances: + - name: main + url: ${ARGOCD_URL} + username: ${ARGOCD_USERNAME} + password: ${ARGOCD_PASSWORD} + type: config + extraEnvVarsSecrets: + - orchestrator-auth-secret + extraAppConfig: + - configMapRef: orchestrator-catalog + filename: orchestrator-catalog.yaml + - configMapRef: orchestrator-auth + filename: orchestrator-auth.yaml + extraEnvVars: + - name: BACKEND_SECRET + valueFrom: + secretKeyRef: + key: backend-secret + name: '{{ include "janus-idp.backend-secret-name" $ }}' + - name: POSTGRESQL_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: '{{- include "janus-idp.postgresql.secretName" . }}' + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: "0" + +orchestrator: + enabled: true + serverlessLogicOperator: + enabled: true + serverlessOperator: + enabled: true + sonataflowPlatform: + monitoring: + enabled: true + eventing: + broker: + name: "" + namespace: "" + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + # -- Secret name for the user-created secret to connect an external DB + externalDBsecretRef: "" + + # -- Name for the user-configured external Database + externalDBName: "" + + # -- Host for the user-configured external Database + externalDBHost: "" + + # -- Port for the user-configured external Database + externalDBPort: "" + + # -- Image for the init container used by the create-db job + initContainerImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" + + # -- Image for the container used by the create-db job + createDBJobImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" + + # -- Orchestrator plugins and their configuration + plugins: + # RHDHBUGS-1464: Note that the plugins here fetch the packages from their direct HTTP download URLs from the (official) Red Hat NPM Registry. + # Previously, we were using the "@redhat/plugin@version" form along with injecting a .npmrc Secret to resolve the "@redhat" scope, + # but this caused conflicting issues with user-provided .npmrc secrets. + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz" + integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz" + integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + path: /orchestrator + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz" + integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} + - disabled: false + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz" + integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} diff --git a/charts/orchestrator-software-templates/templates/NOTES.txt b/charts/orchestrator-software-templates/templates/NOTES.txt new file mode 100644 index 00000000..2d4a53dd --- /dev/null +++ b/charts/orchestrator-software-templates/templates/NOTES.txt @@ -0,0 +1,69 @@ +{{/* Empty line */}} +Helm Release {{ .Release.Name }} installed in namespace {{ .Release.Namespace }}. + +Prerequisites check: +{{- if not (.Capabilities.APIVersions.Has "tekton.dev/v1/Task") }} +WARN: CRD for tekton.dev/v1/Task is not installed +{{- else }} +The required CRD tekton.dev/v1/Task is already installed. +{{- end }} +{{- if not (.Capabilities.APIVersions.Has "tekton.dev/v1/Pipeline") }} +WARN: CRD for tekton.dev/v1/Pipeline is not installed +{{- else }} +The required CRD tekton.dev/v1/Pipeline is already installed. +{{- end }} +{{- if not (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1/AppProject") }} +WARN: CRD for argoproj.io/v1alpha1/AppProject is not installed +{{- else }} +The required CRD argoproj.io/v1alpha1/AppProject is already installed. +{{- end }} +==================================================================== + +{{- if .Values.rhdhConfig.enabled }} +Software Templates Configuration: + - Authentication integrations configured for supported SCM providers + - Catalog locations configured for orchestrator workflows + - ConfigMaps created with label 'rhdh.redhat.com/ext-config-sync: true' + +{{- $secret := lookup "v1" "Secret" .Values.orchestratorTemplates.rhdhChartNamespace .Values.rhdhConfig.secretRef.name }} +{{- if $secret }} +Required secret '{{ .Values.rhdhConfig.secretRef.name }}' found in namespace '{{ .Values.orchestratorTemplates.rhdhChartNamespace }}' +{{- else }} +Warning: Secret '{{ .Values.rhdhConfig.secretRef.name }}' not found in namespace '{{ .Values.orchestratorTemplates.rhdhChartNamespace }}' +{{- end }} +{{- end }} + +==================================================================== +Next Steps: +==================================================================== + +1. Create your environment-specific values file: + + # Get your RHDH route URL + RHDH_ROUTE="https://$(oc get route -n {{ .Values.orchestratorTemplates.rhdhChartNamespace }} -o jsonpath='{.items[0].spec.host}')" + + # Copy template and replace placeholders + cp charts/orchestrator-software-templates/orchestrator-templates-values.yaml.template orchestrator-templates-values.yaml + sed -i "s|__RHDH_BASE_URL__|$RHDH_ROUTE|g" orchestrator-templates-values.yaml + +2. Backup current values and upgrade backstage chart: + + # Backup current configuration + helm show values charts/backstage \ + -n {{ .Values.orchestratorTemplates.rhdhChartNamespace }} > current-backstage-values.yaml + + # Upgrade with both value files + helm upgrade {{ .Values.orchestratorTemplates.rhdhChartReleaseName }} charts/backstage \ + -n {{ .Values.orchestratorTemplates.rhdhChartNamespace }} \ + -f current-backstage-values.yaml \ + -f orchestrator-templates-values.yaml + +3. Wait for the backstage deployment to finish rollout + +4. Access your RHDH instance and check the 'Create' section for new software templates. + + +For more information, visit: +https://github.com/redhat-developer/rhdh-chart + +{{/* Empty line */}} \ No newline at end of file diff --git a/charts/orchestrator-software-templates/templates/_helpers.tpl b/charts/orchestrator-software-templates/templates/_helpers.tpl new file mode 100644 index 00000000..0cc788ce --- /dev/null +++ b/charts/orchestrator-software-templates/templates/_helpers.tpl @@ -0,0 +1,153 @@ +{{/* Helper functions */}} + +{{- define "unmanaged-resource-exists" -}} + {{- $api := index . 0 -}} + {{- $kind := index . 1 -}} + {{- $namespace := index . 2 -}} + {{- $name := index . 3 -}} + {{- $releaseName := index . 4 -}} + {{- $apiCapabilities := index . 5 -}} + {{- $unmanagedSubscriptionExists := "true" -}} + {{- if $apiCapabilities.Has (printf "%s/%s" $api $kind) }} + {{- $existingOperator := lookup $api $kind $namespace $name -}} + {{- if empty $existingOperator -}} + {{- "false" -}} + {{- else -}} + {{- $isManagedResource := include "is-managed-resource" (list $existingOperator $releaseName) -}} + {{- if eq $isManagedResource "true" -}} + {{- "false" -}} + {{- else -}} + {{- "true" -}} + {{- end -}} + {{- end -}} + {{- else -}} + {{- "false" -}} + {{- end -}} +{{- end -}} + +{{- define "is-managed-resource" -}} + {{- $resource := index . 0 -}} + {{- $releaseName := index . 1 -}} + {{- $resourceReleaseName := dig "metadata" "annotations" (dict "meta.helm.sh/release-name" "NA") $resource -}} + {{- if eq (get $resourceReleaseName "meta.helm.sh/release-name") $releaseName -}} + {{- "true" -}} + {{- else -}} + {{- "false" -}} + {{- end -}} +{{- end -}} + + +{{- define "cluster.domain" -}} + {{- if .Capabilities.APIVersions.Has "config.openshift.io/v1/Ingress" -}} + {{- $cluster := (lookup "config.openshift.io/v1" "Ingress" "" "cluster") -}} + {{- if and (hasKey $cluster "spec") (hasKey $cluster.spec "domain") -}} + {{- printf "%s" $cluster.spec.domain -}} + {{- else -}} + {{ fail "Unable to obtain cluster domain, OCP Ingress Resource is missing the `spec.domain` field." }} + {{- end }} + {{- else -}} + {{ fail "Unable to obtain cluster domain, config.openshift.io/v1/Ingress is missing" }} + {{- end -}} +{{- end -}} + + +{{- define "install-tekton-task" -}} + {{- if and (and (and .Values.tekton.enabled .Values.argocd.enabled) (ne .Values.rhdhConfig.secretRef.k8s.clusterToken "")) (.Capabilities.APIVersions.Has "tekton.dev/v1/Task") }} + {{- "true" -}} + {{- else }} + {{- "false" -}} + {{- end -}} +{{- end -}} + +{{- define "install-tekton-pipeline" -}} + {{- if and (and (and .Values.tekton.enabled .Values.argocd.enabled) (ne .Values.rhdhConfig.secretRef.k8s.clusterToken "")) (.Capabilities.APIVersions.Has "tekton.dev/v1/Pipeline") }} + {{- "true" -}} + {{- else }} + {{- "false" -}} + {{- end -}} +{{- end -}} + +{{- define "install-argocd-project" -}} + {{- if and (.Values.argocd.enabled) (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1/AppProject") }} + {{- "true" -}} + {{- else }} + {{- "false" -}} + {{- end -}} +{{- end -}} + + +{{- define "get-namespace-with-label" -}} + {{- $paramValue:= index . 0 -}} + {{- $matchingLabel:= index . 1 -}} + {{- if $paramValue -}} + {{- $paramValue -}} + {{- else -}} + {{/* Only validate namespace existence during actual deployment, not during template testing */}} + {{- $ns:= "" }} + {{- $list:= lookup "v1" "Namespace" "" "" -}} + {{- if $list -}} + {{- if eq 0 (len (dig "items" (dict "" "") $list ) )}} + {{- fail (printf "No namespaces found: %d" (len (dig "items" (dict "" "") $list)) ) }} + {{- end -}} + {{- range (dig "items" (dict "" "") $list) }} + {{- $labels:= dig "metadata" "labels" (dict "" "" ) . -}} + {{- if (hasKey $labels $matchingLabel ) }} + {{- if not $ns }} + {{- $ns = dig "metadata" "name" "" . -}} + {{- else -}} + {{- fail (printf "More than one namespace found with label %s: %s and %s" $matchingLabel $ns (dig "metadata" "name" "" .) )}} + {{- end }} + {{- end -}} + {{- end -}} + {{- if not $ns -}} + {{- fail (printf "No namespace found with label '%s'. Please follow the installation instructions to properly configure the environment" $matchingLabel) -}} + {{- end }} + {{- end }} + {{- $ns }} + {{- end -}} +{{- end -}} + +{{- define "get-workflow-namespace" -}} + {{- if (not (hasKey . "workflowNamespace" ) ) -}} + {{- $workflowNamespace := include "get-namespace-with-label" (list .Values.orchestrator.namespace "rhdh.redhat.com/workflow-namespace") }} + {{- $_ := set . "workflowNamespace" $workflowNamespace }} + {{- end -}} + {{- .workflowNamespace -}} +{{- end -}} + +{{- define "get-argocd-namespace" -}} + {{- if .Values.argocd.enabled }} + {{- if (not (hasKey . "argoCDNamespace" ) ) -}} + #{{- $argoCDNamespace := include "get-namespace-with-label" (list .Values.argocd.namespace "rhdh.redhat.com/argocd-namespace") }} + #{{- $_ := set . "argoCDNamespace" $argoCDNamespace }} + {{- $_ := set . "argoCDNamespace" .Values.argocd.argocdNamespace }} + {{- end -}} + {{- .argoCDNamespace -}} + {{- end -}} +{{- end -}} + +{{- define "get-cluster-version" -}} + {{- $v := "" }} + {{- $version :=(lookup "config.openshift.io/v1" "ClusterVersion" "" "version") }} + {{- range $version.status.history }} + {{- if eq .state "Completed" }} + {{- $v = (semver .version) }} + {{- end }} + {{- end }} + + {{- $validMinors := list "4.13" "4.14" "4.15" "4.16" -}} + {{- $versionString := printf "%d.%d" $v.Major $v.Minor -}} + {{- if not (semverCompare ">=4.13 <=4.16" $versionString) -}} + {{- fail (printf "Unsupported OCP version: %s. Supported versions: %s." $versionString $validMinors) -}} + {{- end -}} + {{- $versionString -}} +{{- end -}} + +{{- define "get-tekton-version" -}} + {{- $pipelinesSubs := lookup "operators.coreos.com/v1alpha1" "Subscription" "openshift-operators" "openshift-pipelines-operator-rh" -}} + {{- $pipelineInstalledVersion := $pipelinesSubs.status.installedCSV}} + {{- $pipelineVersion := substr 33 ( len $pipelineInstalledVersion) $pipelineInstalledVersion}} + {{- $pipelineVersion = semver $pipelineVersion }} + {{- $pipelineVersionString := printf "%d.%d" $pipelineVersion.Major $pipelineVersion.Minor -}} + {{- $pipelineVersionString -}} +{{- end -}} \ No newline at end of file diff --git a/charts/orchestrator-software-templates/templates/argocd/argocd-project.yaml b/charts/orchestrator-software-templates/templates/argocd/argocd-project.yaml new file mode 100644 index 00000000..0d8dcf3d --- /dev/null +++ b/charts/orchestrator-software-templates/templates/argocd/argocd-project.yaml @@ -0,0 +1,14 @@ +{{- if eq "true" (include "install-argocd-project" .) }} +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: orchestrator-gitops + namespace: {{ .Values.argocd.argocdNamespace }} +spec: + destinations: + - name: '*' + namespace: '*' + server: '*' + sourceRepos: + - '*' +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates/templates/rbac.yaml b/charts/orchestrator-software-templates/templates/rbac.yaml new file mode 100644 index 00000000..1ac0ca81 --- /dev/null +++ b/charts/orchestrator-software-templates/templates/rbac.yaml @@ -0,0 +1,69 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: backstage + namespace: {{ .Values.orchestratorTemplates.rhdhChartNamespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "-5" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: {{ .Values.argocd.argocdNamespace }} + name: backstage-k8s-read + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "-4" +rules: + - apiGroups: + [ + "", + "apps", + "batch", + "networking.k8s.io", + "tekton.dev", + "route.openshift.io", + "autoscaling", + ] + resources: + [ + "pods", + "services", + "configmaps", + "limitranges", + "resourcequotas", + "deployments", + "replicasets", + "statefulsets", + "jobs", + "cronjobs", + "ingresses", + "horizontalpodautoscalers", + "pipelines", + "pipelineruns", + "taskruns", + "routes", + "daemonsets", + ] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + namespace: {{ .Values.argocd.argocdNamespace }} + name: backstage-k8s-read-binding + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "-3" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: backstage-k8s-read +subjects: + - kind: ServiceAccount + name: backstage + namespace: {{ .Values.orchestratorTemplates.rhdhChartNamespace }} diff --git a/charts/orchestrator-software-templates/templates/software-templates/orchestrator-auth.yaml b/charts/orchestrator-software-templates/templates/software-templates/orchestrator-auth.yaml new file mode 100644 index 00000000..bbef9274 --- /dev/null +++ b/charts/orchestrator-software-templates/templates/software-templates/orchestrator-auth.yaml @@ -0,0 +1,54 @@ +{{- if .Values.rhdhConfig.enabled }} +{{- if not .Values.rhdhConfig.secretRef.name }} + {{- fail "Backstage's secret name defined in 'rhdhConfig.secretRef.name' is required" }} +{{- end }} +{{/* Only validate secret existence during actual deployment, not during template testing */}} +{{- $secret := dict }} +{{- $nsExists := lookup "v1" "Namespace" "" .Values.orchestratorTemplates.rhdhChartNamespace }} +{{- if $nsExists }} + {{- $secret = lookup "v1" "Secret" .Values.orchestratorTemplates.rhdhChartNamespace .Values.rhdhConfig.secretRef.name }} + {{- if not $secret }} + {{- fail (printf "Secret %s not found in namespace %s" .Values.rhdhConfig.secretRef.name .Values.orchestratorTemplates.rhdhChartNamespace ) }} + {{- end }} +{{- end }} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: orchestrator-auth + namespace: {{ .Release.Namespace }} + labels: + rhdh.redhat.com/ext-config-sync: "true" +data: + orchestrator-auth.yaml: | + {{- if and .Values.rhdhConfig.secretRef.github.token (dig "data" .Values.rhdhConfig.secretRef.github.token "" $secret) }} + integrations: + github: + - host: github.com + token: {{ printf "${%s}" .Values.rhdhConfig.secretRef.github.token }} + {{- end }} + {{- if and (and .Values.rhdhConfig.secretRef.gitlab.token (dig "data" .Values.rhdhConfig.secretRef.gitlab.token "" $secret) ) + (and .Values.rhdhConfig.secretRef.gitlab.host (dig "data" .Values.rhdhConfig.secretRef.gitlab.host "" $secret ) ) }} + gitlab: + - host: {{ printf "${%s}" .Values.rhdhConfig.secretRef.gitlab.host }} + token: {{ printf "${%s}" .Values.rhdhConfig.secretRef.gitlab.token }} + apiBaseUrl: https://{{ printf "${%s}" .Values.rhdhConfig.secretRef.gitlab.host }}/api/v4 + {{- end }} + {{- if and .Values.rhdhConfig.secretRef.github.token (dig "data" .Values.rhdhConfig.secretRef.github.token "" $secret) }} + auth: + environment: development + providers: + {{- end }} + {{- if and (and .Values.rhdhConfig.secretRef.github.clientId (dig "data" .Values.rhdhConfig.secretRef.github.clientId "" $secret) ) + (and .Values.rhdhConfig.secretRef.github.clientSecret (dig "data" .Values.rhdhConfig.secretRef.github.clientSecret "" $secret ) ) }} + github: + development: + clientId: {{ printf "${%s}" .Values.rhdhConfig.secretRef.github.clientId }} + clientSecret: {{ printf "${%s}" .Values.rhdhConfig.secretRef.github.clientSecret }} + {{- end }} + {{- if .Values.rhdhConfig.enableGuestProvider }} + guest: + dangerouslyAllowOutsideDevelopment: true + userEntityRef: user:default/guest + {{- end }} +{{- end }} diff --git a/charts/orchestrator-software-templates/templates/software-templates/orchestrator-catalog.yaml b/charts/orchestrator-software-templates/templates/software-templates/orchestrator-catalog.yaml new file mode 100644 index 00000000..20072a30 --- /dev/null +++ b/charts/orchestrator-software-templates/templates/software-templates/orchestrator-catalog.yaml @@ -0,0 +1,40 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: orchestrator-catalog + namespace: {{ .Release.Namespace }} + labels: + rhdh.redhat.com/ext-config-sync: "true" +data: + "orchestrator-catalog.yaml": | + catalog: + rules: + - allow: + [ + Component, + System, + Group, + Resource, + Location, + Template, + API, + User, + Domain, + ] + locations: + {{- if .Values.rhdhConfig.enableGuestProvider }} + - type: url + target: https://github.com/rhdhorchestrator/orchestrator-helm-chart/blob/main/resources/users.yaml + {{- end }} + - type: url + target: https://github.com/rhdhorchestrator/workflow-software-templates/blob/main/entities/workflow-resources.yaml + - type: url + target: https://github.com/rhdhorchestrator/workflow-software-templates/blob/main/scaffolder-templates/github-workflows/basic-workflow/template.yaml + - type: url + target: https://github.com/rhdhorchestrator/workflow-software-templates/blob/main/scaffolder-templates/github-workflows/advanced-workflow/template.yaml + - type: url + target: https://github.com/rhdhorchestrator/workflow-software-templates/blob/main/scaffolder-templates/gitlab-workflows/basic-workflow/template.yaml + - type: url + target: https://github.com/rhdhorchestrator/workflow-software-templates/blob/main/scaffolder-templates/gitlab-workflows/advanced-workflow/template.yaml + - type: url + target: https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/orchestrator/plugins/orchestrator-common/src/generated/docs/api-doc/orchestrator-api.yaml diff --git a/charts/orchestrator-software-templates/templates/tekton/tekton-pipeline.yaml b/charts/orchestrator-software-templates/templates/tekton/tekton-pipeline.yaml new file mode 100644 index 00000000..f881d7d1 --- /dev/null +++ b/charts/orchestrator-software-templates/templates/tekton/tekton-pipeline.yaml @@ -0,0 +1,175 @@ +{{- if eq "true" (include "install-tekton-pipeline" .) }} + {{- $gitopsNamespace := include "get-argocd-namespace" . }} + {{- $version := include "get-tekton-version" . }} +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: workflow-deployment + namespace: {{ .Values.argocd.argocdNamespace }} +spec: + description: | + This pipeline clones a git repo, builds a Docker image with Kaniko and pushes it to a registry + params: + - name: gitUrl + description: The SSH URL of the repository to clone + type: string + - name: gitOpsUrl + description: The SSH URL of the config repository for pushing the changes + type: string + - name: workflowId + description: The workflow ID from the repository + type: string + - name: convertToFlat + description: Whether conversion to flat layout is needed or it's already flattened + type: string + default: "true" + - name: quayOrgName + description: The Quay Organization Name of the published workflow + type: string + - name: quayRepoName + description: The Quay Repository Name of the published workflow + type: string + workspaces: + - name: workflow-source + - name: workflow-gitops + - name: ssh-creds + - name: docker-credentials + tasks: + - name: fetch-workflow + taskRef: + name: git-cli + workspaces: + - name: source + workspace: workflow-source + - name: ssh-directory + workspace: ssh-creds + params: + - name: GIT_USER_NAME + value: The Orchestrator Tekton Pipeline + - name: GIT_USER_EMAIL + value: rhdhorchestrator@redhat.com + - name: USER_HOME + value: /home/git + - name: GIT_SCRIPT + value: | + eval "$(ssh-agent -s)" + ssh-add "${PARAM_USER_HOME}"/.ssh/id_rsa + git clone $(params.gitUrl) workflow + cd workflow + - name: fetch-workflow-gitops + taskRef: + name: git-cli + workspaces: + - name: source + workspace: workflow-gitops + - name: ssh-directory + workspace: ssh-creds + params: + - name: GIT_USER_NAME + value: The Orchestrator Tekton Pipeline + - name: GIT_USER_EMAIL + value: rhdhorchestrator@redhat.com + - name: USER_HOME + value: /home/git + - name: GIT_SCRIPT + value: | + eval "$(ssh-agent -s)" + ssh-add "${PARAM_USER_HOME}"/.ssh/id_rsa + git clone $(params.gitOpsUrl) workflow-gitops + - name: flatten-workflow + runAfter: ["fetch-workflow"] + taskRef: + name: flattener + workspaces: + - name: workflow-source + workspace: workflow-source + params: + - name: workflowId + value: $(params.workflowId) + - name: convertToFlat + value: $(params.convertToFlat) + - name: build-manifests + runAfter: ["flatten-workflow"] + taskRef: + name: build-manifests + workspaces: + - name: workflow-source + workspace: workflow-source + params: + - name: workflowId + value: $(params.workflowId) + - name: build-gitops + runAfter: ["build-manifests", "fetch-workflow-gitops"] + taskRef: + name: build-gitops + workspaces: + - name: workflow-source + workspace: workflow-source + - name: workflow-gitops + workspace: workflow-gitops + params: + - name: workflowId + value: $(params.workflowId) + - name: imageTag + value: $(tasks.fetch-workflow.results.commit) + - name: build-and-push-image + runAfter: ["flatten-workflow"] + taskRef: +{{- if (semverCompare ">=1.17" $version) }} + resolver: cluster + params: + - name: kind + value: task + - name: name + value: buildah + - name: namespace + value: openshift-pipelines +{{- else }} + name: buildah + kind: ClusterTask +{{- end }} + workspaces: + - name: source + workspace: workflow-source + - name: dockerconfig + workspace: docker-credentials + params: + - name: IMAGE + value: quay.io/$(params.quayOrgName)/$(params.quayRepoName):$(tasks.fetch-workflow.results.commit) + - name: DOCKERFILE + value: flat/workflow-builder.Dockerfile + - name: CONTEXT + value: flat/$(params.workflowId) + - name: BUILD_EXTRA_ARGS + value: '--authfile=/workspace/dockerconfig/.dockerconfigjson --ulimit nofile=4096:4096 --build-arg WF_RESOURCES=. ' + - name: push-workflow-gitops + runAfter: ["build-gitops", "build-and-push-image"] + taskRef: + name: git-cli + workspaces: + - name: source + workspace: workflow-gitops + - name: ssh-directory + workspace: ssh-creds + params: + - name: GIT_USER_NAME + value: The Orchestrator Tekton Pipeline + - name: GIT_USER_EMAIL + value: rhdhorchestrator@redhat.com + - name: USER_HOME + value: /home/git + - name: GIT_SCRIPT + value: | + WORKFLOW_COMMIT=$(tasks.fetch-workflow.results.commit) + + eval "$(ssh-agent -s)" + ssh-add "${PARAM_USER_HOME}"/.ssh/id_rsa + + cd workflow-gitops + git add . + git diff + # TODO: create PR + git commit -m "Deployment for workflow commit $WORKFLOW_COMMIT from $(params.gitUrl)" + # TODO: parametrize branch + git push origin main +{{- end }} \ No newline at end of file diff --git a/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml b/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml new file mode 100644 index 00000000..0f0fcca2 --- /dev/null +++ b/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml @@ -0,0 +1,243 @@ +# From https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-cli/0.4/git-cli.yaml +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: git-cli + namespace: {{ .Values.argocd.argocdNamespace }} + labels: + app.kubernetes.io/version: "0.4" + annotations: + tekton.dev/pipelines.minVersion: "0.21.0" + tekton.dev/categories: Git + tekton.dev/tags: git + tekton.dev/displayName: "git cli" + tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le" +spec: + description: >- + This task can be used to perform git operations. + + Git command that needs to be run can be passed as a script to + the task. This task needs authentication to git in order to push + after the git operation. + + workspaces: + - name: source + description: A workspace that contains the fetched git repository. + + - name: input + optional: true + description: | + An optional workspace that contains the files that need to be added to git. You can + access the workspace from your script using `$(workspaces.input.path)`, for instance: + + cp $(workspaces.input.path)/file_that_i_want . + git add file_that_i_want + # etc + + - name: ssh-directory + optional: true + description: | + A .ssh directory with private key, known_hosts, config, etc. Copied to + the user's home before git commands are executed. Used to authenticate + with the git remote when performing the clone. Binding a Secret to this + Workspace is strongly recommended over other volume types. + + - name: basic-auth + optional: true + description: | + A Workspace containing a .gitconfig and .git-credentials file. These + will be copied to the user's home before any git commands are run. Any + other files in this Workspace are ignored. It is strongly recommended + to use ssh-directory over basic-auth whenever possible and to bind a + Secret to this Workspace over other volume types. + params: + - name: BASE_IMAGE + description: | + The base image for the task. + type: string + # TODO: Deprecate use of root image. + default: cgr.dev/chainguard/git:root-2.39@sha256:7759f87050dd8bacabe61354d75ccd7f864d6b6f8ec42697db7159eccd491139 + + - name: GIT_USER_NAME + type: string + description: | + Git user name for performing git operation. + default: "" + + - name: GIT_USER_EMAIL + type: string + description: | + Git user email for performing git operation. + default: "" + + - name: GIT_SCRIPT + description: The git script to run. + type: string + default: | + git help + + - name: USER_HOME + description: | + Absolute path to the user's home directory. Set this explicitly if you are running the image as a non-root user or have overridden + the gitInitImage param with an image containing custom user configuration. + type: string + default: "/root" + + - name: VERBOSE + description: Log the commands that are executed during `git-clone`'s operation. + type: string + default: "true" + + results: + - name: commit + description: The precise commit SHA after the git operation. + + steps: + - name: git + image: $(params.BASE_IMAGE) + workingDir: $(workspaces.source.path) + env: + - name: HOME + value: $(params.USER_HOME) + - name: PARAM_VERBOSE + value: $(params.VERBOSE) + - name: PARAM_USER_HOME + value: $(params.USER_HOME) + - name: WORKSPACE_OUTPUT_PATH + value: $(workspaces.output.path) + - name: WORKSPACE_SSH_DIRECTORY_BOUND + value: $(workspaces.ssh-directory.bound) + - name: WORKSPACE_SSH_DIRECTORY_PATH + value: $(workspaces.ssh-directory.path) + - name: WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND + value: $(workspaces.basic-auth.bound) + - name: WORKSPACE_BASIC_AUTH_DIRECTORY_PATH + value: $(workspaces.basic-auth.path) + script: | + #!/usr/bin/env sh + set -eu + + if [ "${PARAM_VERBOSE}" = "true" ] ; then + set -x + fi + + if [ "${WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND}" = "true" ] ; then + cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.git-credentials" "${PARAM_USER_HOME}/.git-credentials" + cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.gitconfig" "${PARAM_USER_HOME}/.gitconfig" + chmod 400 "${PARAM_USER_HOME}/.git-credentials" + chmod 400 "${PARAM_USER_HOME}/.gitconfig" + fi + + if [ "${WORKSPACE_SSH_DIRECTORY_BOUND}" = "true" ] ; then + cp -R "${WORKSPACE_SSH_DIRECTORY_PATH}" "${PARAM_USER_HOME}"/.ssh + chmod 700 "${PARAM_USER_HOME}"/.ssh + chmod -R 400 "${PARAM_USER_HOME}"/.ssh/* + fi + + # Setting up the config for the git. + git config --global user.email "$(params.GIT_USER_EMAIL)" + git config --global user.name "$(params.GIT_USER_NAME)" + + eval '$(params.GIT_SCRIPT)' + + RESULT_SHA="$(git rev-parse HEAD | tr -d '\n')" + EXIT_CODE="$?" + if [ "$EXIT_CODE" != 0 ] + then + exit $EXIT_CODE + fi + # Make sure we don't add a trailing newline to the result! + printf "%s" "$RESULT_SHA" > "$(results.commit.path)" + # Patch to apply on OpenShift + securityContext: + runAsNonRoot: true + runAsUser: 65532 +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: flattener + namespace: {{ .Values.argocd.argocdNamespace }} +spec: + workspaces: + - name: workflow-source + params: + - name: workflowId + description: The workflow ID from the repository + type: string + - name: convertToFlat + description: Whether conversion to flat layout is needed or it's already flattened + type: string + default: "true" + steps: + - name: flatten + image: registry.access.redhat.com/ubi9-minimal + workingDir: $(workspaces.workflow-source.path) + script: | + ROOT=/workspace/workflow + TARGET=flat + mkdir -p flat + + if [ -d "workflow/$(params.workflowId)" ]; then + cp -r workflow/$(params.workflowId)/src/main/resources flat/$(params.workflowId) + cp workflow/$(params.workflowId)/LICENSE flat/$(params.workflowId) + else + cp -r workflow/src/main/resources flat/$(params.workflowId) + cp workflow/LICENSE flat/$(params.workflowId) + fi + + if [ "$(params.convertToFlat)" == "false" ]; then + rm -rf workflow/src/main/resources + mv workflow/src flat/$(params.workflowId)/ + fi + + ls flat/$(params.workflowId) + + curl -L https://raw.githubusercontent.com/rhdhorchestrator/serverless-workflows/v1.4.x/pipeline/workflow-builder.Dockerfile -o flat/workflow-builder.Dockerfile +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: build-manifests + namespace: {{ .Values.argocd.argocdNamespace }} +spec: + workspaces: + - name: workflow-source + params: + - name: workflowId + description: The workflow ID from the repository + type: string + steps: + - name: build-manifests + image: registry.access.redhat.com/ubi9-minimal + workingDir: $(workspaces.workflow-source.path)/flat/$(params.workflowId) + script: | + microdnf install -y tar gzip + KN_CLI_URL="https://developers.redhat.com/content-gateway/file/pub/cgw/serverless-logic/1.35.0/kn-workflow-linux-amd64.tar.gz" + curl -L "$KN_CLI_URL" | tar -xz --no-same-owner && chmod +x kn-workflow-linux-amd64 && mv kn-workflow-linux-amd64 kn-workflow + ./kn-workflow gen-manifest --namespace "" +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: build-gitops + namespace: {{ .Values.argocd.argocdNamespace }} +spec: + workspaces: + - name: workflow-source + - name: workflow-gitops + params: + - name: workflowId + description: The workflow ID from the repository + type: string + - name: imageTag + type: string + steps: + - name: build-gitops + image: registry.access.redhat.com/ubi9-minimal + workingDir: $(workspaces.workflow-gitops.path)/workflow-gitops + script: | + cp $(workspaces.workflow-source.path)/flat/$(params.workflowId)/manifests/* kustomize/base + microdnf install -y findutils && microdnf clean all + cd kustomize + ./updater.sh $(params.workflowId) $(params.imageTag) diff --git a/charts/orchestrator-software-templates/values.yaml b/charts/orchestrator-software-templates/values.yaml new file mode 100644 index 00000000..1dfd2b5a --- /dev/null +++ b/charts/orchestrator-software-templates/values.yaml @@ -0,0 +1,50 @@ +orchestratorTemplates: + enabled: true + rhdhChartReleaseName: rhdh + rhdhChartNamespace: rhdh + +tekton: + enabled: true + +argocd: + enabled: true + # -- References the ArgoCD instance created by infra chart + argocdNamespace: orchestrator-gitops + +rhdhConfig: + secretRef: + # -- Name of the secret containing the configuration + name: orchestrator-auth-secret + backstage: + # -- Key in the secret for backend authentication + backendSecret: BACKEND_SECRET + k8s: + # -- Key in the secret for Kubernetes cluster token + clusterToken: K8S_CLUSTER_TOKEN + # -- Key in the secret for Kubernetes cluster URL + clusterUrl: K8S_CLUSTER_URL + github: + # -- Key in the secret for GitHub token + token: GITHUB_TOKEN + # -- Key in the secret for GitHub client ID + clientId: GITHUB_CLIENT_ID + # -- Key in the secret for GitHub client secret + clientSecret: GITHUB_CLIENT_SECRET + gitlab: + # -- Key in the secret for GitLab host + host: GITLAB_HOST + # -- Key in the secret for GitLab token + token: GITLAB_TOKEN + argocd: + # -- Key in the secret for ArgoCD URL + url: ARGOCD_URL + # -- Key in the secret for ArgoCD username + username: ARGOCD_USERNAME + # -- Key in the secret for ArgoCD password + password: ARGOCD_PASSWORD + # -- Enable RHDH operator + enabled: true + # -- Enable guest authentication provider + enableGuestProvider: false + # -- Branch to use for catalog templates + catalogBranch: main diff --git a/ct-install.yaml b/ct-install.yaml index 7d076c14..defa1975 100644 --- a/ct-install.yaml +++ b/ct-install.yaml @@ -3,3 +3,8 @@ chart-dirs: validate-maintainers: false remote: origin helm-extra-args: --timeout 500s +# Excluding software template charts - which are for demo purposes +excluded-charts: + - orchestrator-software-templates + - orchestrator-software-templates-infra + diff --git a/ct.yaml b/ct.yaml index 7d076c14..9774ed02 100644 --- a/ct.yaml +++ b/ct.yaml @@ -3,3 +3,7 @@ chart-dirs: validate-maintainers: false remote: origin helm-extra-args: --timeout 500s +# Excluding software template charts - which are for demo purposes +excluded-charts: + - orchestrator-software-templates + - orchestrator-software-templates-infra diff --git a/hack/merge_secrets.sh b/hack/merge_secrets.sh new file mode 100755 index 00000000..037432d4 --- /dev/null +++ b/hack/merge_secrets.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# The script merges two given K8s secrets in yaml format into a single secret named 'docker-credentials' in 'orchestrator-gitops' namespace. +# This script is required to simplify the instruction of +# https://github.com/rhdhorchestrator/orchestrator-helm-operator/tree/main/docs/gitops#installing-docker-credentials + +# Check if the correct number of arguments is provided +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Read the input arguments +secret1_file=$1 +secret2_file=$2 + +# Function to base64 encode a file +base64_encode() { + local input_file=$1 + local output_file=$2 + base64 -w 0 "$input_file" > "$output_file" +} + +# Decode the secrets into temporary files +temp_secret1_file=$(mktemp) +temp_secret2_file=$(mktemp) + +yq e '.data[".dockerconfigjson"]' "$secret1_file" | base64 -d > "$temp_secret1_file" +yq e '.data[".dockerconfigjson"]' "$secret2_file" | base64 -d > "$temp_secret2_file" + +# Merge the decoded files +merged_secret_file=$(mktemp) +cat "$temp_secret1_file" "$temp_secret2_file" | jq -s '.[0] * .[1]' > "$merged_secret_file" + +# Encode the merged secret back to base64 +encoded_merged_secret=$(mktemp) +base64_encode "$merged_secret_file" "$encoded_merged_secret" + +# Create the new secret YAML +echo "apiVersion: v1 +kind: Secret +metadata: + name: docker-credentials + namespace: orchestrator-gitops +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: $(cat "$encoded_merged_secret")" > docker-credentials-secret.yaml + +# Clean up temporary files +rm "$temp_secret1_file" "$temp_secret2_file" "$merged_secret_file" "$encoded_merged_secret" + +echo "Merged secret created: docker-credentials-secret.yaml" diff --git a/hack/orchestrator-templates-setup.sh b/hack/orchestrator-templates-setup.sh new file mode 100755 index 00000000..b789892a --- /dev/null +++ b/hack/orchestrator-templates-setup.sh @@ -0,0 +1,389 @@ +#!/usr/bin/env bash + + +function captureSetupNewRHDHDeployment { + if $use_default; then + NEW_ENVIRONMENT=true + else + echo "Setup for a new RHDH deployment?:" + select yn in "Yes" "No"; do + case $yn in + Yes ) NEW_ENVIRONMENT=true; break;; + No ) NEW_ENVIRONMENT=false; break;; + esac + done + fi +} + +function captureRHDHNamespace { + default="rhdh" + if [ "$use_default" == true ]; then + rhdh_workspace="$default" + else + read -r -p "Enter RHDH Operator namespace (default: $default): " value + if [ -z "$value" ]; then + rhdh_workspace="$default" + else + rhdh_workspace="$value" + fi + fi + RHDH_NAMESPACE=$rhdh_workspace +} + +function captureWorkflowNamespace { + default="rhdh" + if [ "$use_default" == true ]; then + workflow_namespace="$default" + else + read -r -p "Enter workflow namespace (default: $default): " value + if [ -z "$value" ]; then + workflow_namespace="$default" + else + workflow_namespace="$value" + fi + fi + WORKFLOW_NAMESPACE=$workflow_namespace +} + +function captureK8sURL { + url="$(oc whoami --show-server)" + K8S_CLUSTER_URL=$url +} + +function generateBackendSecret { + BACKEND_SECRET=$(mktemp -u XXXXXXXXXXXXXXXXXXXXXXX) +} + +function generateK8sToken { + sa_namespace="rhdh" + sa_name="rhdh" + if [ "$use_default" == false ]; then + read -r -p "Which namespace should be used or created to check the SA holding the persistent token? (default: $sa_namespace): " selected_namespace + if [ -n "$selected_namespace" ]; then + sa_namespace="$selected_namespace" + fi + + read -r -p "What is the name of the SA? (default: $sa_name): " selected_name + if [ -n "$selected_name" ]; then + sa_name="$selected_name" + fi + fi + if oc get namespace "$sa_namespace" &> /dev/null; then + echo "Namespace '$sa_namespace' already exists." + else + echo "Namespace '$sa_namespace' does not exist. Creating..." + oc create namespace "$sa_namespace" + fi + + if oc get sa -n "$sa_namespace" "$sa_name" &> /dev/null; then + echo "ServiceAccount '$sa_name' already exists in '$sa_namespace'." + else + echo "ServiceAccount '$sa_name' does not exist in '$sa_namespace'. Creating..." + oc create sa "$sa_name" -n "$sa_namespace" + fi + + oc adm policy add-cluster-role-to-user cluster-admin -z "$sa_name" -n "$sa_namespace" + echo "Added cluster-admin role to '$sa_name' in '$sa_namespace'." + + cat < /dev/null; then + echo "oc is required for this script to run. Exiting." + exit 1 + fi +} + +function createBackstageSecret { + if 2>/dev/null 1>&2 oc get secret orchestrator-auth-secret -n "$RHDH_NAMESPACE"; then + oc delete secret orchestrator-auth-secret -n "$RHDH_NAMESPACE" + fi + declare -A secretKeys + if [ -n "$K8S_CLUSTER_URL" ]; then + secretKeys[K8S_CLUSTER_URL]=$K8S_CLUSTER_URL + fi + if [ -n "$K8S_CLUSTER_TOKEN" ]; then + secretKeys[K8S_CLUSTER_TOKEN]=$K8S_CLUSTER_TOKEN + fi + if [ -n "$ARGOCD_USERNAME" ]; then + secretKeys[ARGOCD_USERNAME]=$ARGOCD_USERNAME + fi + if [ -n "$ARGOCD_URL" ]; then + secretKeys[ARGOCD_URL]=$ARGOCD_URL + fi + if [ -n "$ARGOCD_PASSWORD" ]; then + secretKeys[ARGOCD_PASSWORD]=$ARGOCD_PASSWORD + fi + if [ -n "$GITHUB_TOKEN" ]; then + secretKeys[GITHUB_TOKEN]=$GITHUB_TOKEN + fi + if [ -n "$GITLAB_TOKEN" ]; then + secretKeys[GITLAB_TOKEN]=$GITLAB_TOKEN + fi + if [ -n "$GITLAB_HOST" ]; then + secretKeys[GITLAB_HOST]=$GITLAB_HOST + fi + if [ -n "$GITHUB_CLIENT_ID" ]; then + secretKeys[GITHUB_CLIENT_ID]=$GITHUB_CLIENT_ID + fi + if [ -n "$GITHUB_CLIENT_SECRET" ]; then + secretKeys[GITHUB_CLIENT_SECRET]=$GITHUB_CLIENT_SECRET + fi + if [ -n "$NOTIFICATIONS_EMAIL_HOSTNAME" ] && [ "$SETUP_NOTIFICATIONS_EMAIL" = true ]; then + secretKeys[NOTIFICATIONS_EMAIL_HOSTNAME]=$NOTIFICATIONS_EMAIL_HOSTNAME + fi + if [ -n "$NOTIFICATIONS_EMAIL_USERNAME" ] && [ "$SETUP_NOTIFICATIONS_EMAIL" = true ]; then + secretKeys[NOTIFICATIONS_EMAIL_USERNAME]=$NOTIFICATIONS_EMAIL_USERNAME + fi + if [ -n "$NOTIFICATIONS_EMAIL_PASSWORD" ] && [ "$SETUP_NOTIFICATIONS_EMAIL" = true ]; then + secretKeys[NOTIFICATIONS_EMAIL_PASSWORD]=$NOTIFICATIONS_EMAIL_PASSWORD + fi + cmd="oc create secret generic orchestrator-auth-secret -n $RHDH_NAMESPACE --from-literal=BACKEND_SECRET=$BACKEND_SECRET" + for key in "${!secretKeys[@]}"; do + cmd="${cmd} --from-literal=${key}=${secretKeys[$key]}" + done + eval "$cmd" +} + +function labelNamespaces { + for a in $(oc get namespace -l rhdh.redhat.com/workflow-namespace -oname); do + oc label "$a" rhdh.redhat.com/workflow-namespace- ; + done + for a in $(oc get namespace -l rhdh.redhat.com/argocd-namespace -oname); do + oc label "$a" rhdh.redhat.com/argocd-namespace- ; + done + if [ -n "$WORKFLOW_NAMESPACE" ]; then + oc label namespace "$WORKFLOW_NAMESPACE" rhdh.redhat.com/workflow-namespace= + fi + if [[ -n "$ARGOCD_NAMESPACE" && -n "$ARGOCD_PASSWORD" && -n "$ARGOCD_URL" && -n "$ARGOCD_USERNAME" ]]; then + oc label namespace "$ARGOCD_NAMESPACE" rhdh.redhat.com/argocd-namespace= + fi +} + +# Function to display usage instructions +display_usage() { + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -h, --help Display usage instructions" + echo " --use-default Specify to use all default values" + exit 1 +} + +# Initialize variable +use_default=false + +# Parse command-line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + display_usage + ;; + --use-default) + use_default=true + ;; + *) + echo "Error: Invalid option $1" + display_usage + ;; + esac + shift +done + +function main { + + # Check if using default values or not + if $use_default; then + echo "Using default values." + else + echo "Not using default values." + fi + + checkPrerequisite + captureSetupNewRHDHDeployment + captureWorkflowNamespace + captureArgoCDNamespace + captureArgoCDURL + captureArgoCDCreds + labelNamespaces + if $NEW_ENVIRONMENT; then + generateBackendSecret + captureK8sURL + generateK8sToken + captureGitToken + captureGitClientId + captureGitClientSecret + captureGitlabHost + captureGitlabToken + setupNotificationsEmailConfig + if $SETUP_NOTIFICATIONS_EMAIL; then + captureNotificationsEmailHostname + captureNotificationsEmailUsername + captureNotificationsEmailPassword + fi + captureRHDHNamespace + createBackstageSecret + fi + echo "Setup completed successfully!" +} + +main + From 473875d18e0bbec280be98541f767686f5252574 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 17:18:20 +0200 Subject: [PATCH 093/117] chore(deps): update actions/github-script action to v8 (#229) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index cd3857da..888ec6f0 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -19,7 +19,7 @@ jobs: - name: Check for command id: command continue-on-error: true - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const commentBody = context.payload.comment.body; diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 72d7ef41..266be94f 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -80,7 +80,7 @@ jobs: git push - name: Comment on PR if manifests were updated - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 if: ${{ !cancelled() && steps.diff-checker.outputs.CHANGED == 'true' }} continue-on-error: true with: From f29a99e6790f4d75b4256275781c72ad459dafae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 17:19:20 +0200 Subject: [PATCH 094/117] chore(deps): update actions/setup-python action to v6 (#227) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- .github/workflows/test.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 888ec6f0..8c747fc7 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -59,7 +59,7 @@ jobs: comment-id: ${{ github.event.comment.id }} reactions: eyes - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 if: steps.command.outputs.command-name == 'bump' with: python-version: 3.13 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 00b3fee6..f8c8ece6 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,7 +31,7 @@ jobs: with: version: v3.10.0 - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: python-version: 3.13 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 266be94f..30a12591 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -44,7 +44,7 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} ref: ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: python-version: 3.13 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9704984b..52217412 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,7 +44,7 @@ jobs: with: version: v3.10.0 - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: python-version: 3.13 From d2bf7226a765d293f18521e88ff3172bcf01efcd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:22:40 +0000 Subject: [PATCH 095/117] chore(deps): update actions/setup-go action to v6 (#226) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 8c747fc7..461986c9 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -64,7 +64,7 @@ jobs: with: python-version: 3.13 - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6 if: steps.command.outputs.command-name == 'bump' with: go-version: ^1 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 30a12591..b3f62f35 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -48,7 +48,7 @@ jobs: with: python-version: 3.13 - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6 with: go-version: ^1 From c32497f1bc44a0668f772a3ae1c795141ab2dcfb Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Mon, 8 Sep 2025 11:18:29 +0300 Subject: [PATCH 096/117] =?UTF-8?q?adding=20sonataflowPlatform's=20jobserv?= =?UTF-8?q?ice=20and=20dataindex=20custom=20images=20to=E2=80=A6=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding sonataflowPlatform's jobservice and dataindex custom images to values schema * adding null params to the values.yaml and the readme --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 4 +++- charts/backstage/values.schema.json | 10 ++++++++++ charts/backstage/values.schema.tmpl.json | 10 ++++++++++ charts/backstage/values.yaml | 7 +++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 18a33528..851c2ee4 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.5 +version: 4.5.6 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 02129029..2128f352 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.5](https://img.shields.io/badge/Version-4.5.5-informational?style=flat-square) +![Version: 4.5.6](https://img.shields.io/badge/Version-4.5.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -183,6 +183,7 @@ Kubernetes: `>= 1.27.0-0` | orchestrator.serverlessLogicOperator.enabled | | bool | `true` | | orchestrator.serverlessOperator.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | +| orchestrator.sonataflowPlatform.dataIndexImage | Image for the container used by the sonataflow data index, optional and used for disconnected environments | string | `""` | | orchestrator.sonataflowPlatform.eventing.broker.name | | string | `""` | | orchestrator.sonataflowPlatform.eventing.broker.namespace | | string | `""` | | orchestrator.sonataflowPlatform.externalDBHost | Host for the user-configured external Database | string | `""` | @@ -190,6 +191,7 @@ Kubernetes: `>= 1.27.0-0` | orchestrator.sonataflowPlatform.externalDBPort | Port for the user-configured external Database | string | `""` | | orchestrator.sonataflowPlatform.externalDBsecretRef | Secret name for the user-created secret to connect an external DB | string | `""` | | orchestrator.sonataflowPlatform.initContainerImage | Image for the init container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | +| orchestrator.sonataflowPlatform.jobServiceImage | Image for the container used by the sonataflow jobs service, optional and used for disconnected environments | string | `""` | | orchestrator.sonataflowPlatform.monitoring.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.resources.limits.cpu | | string | `"500m"` | | orchestrator.sonataflowPlatform.resources.limits.memory | | string | `"1Gi"` | diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index bc1ad872..17e2e1c8 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -160,6 +160,11 @@ "title": "Image for the container used by the create-db job", "type": "string" }, + "dataIndexImage": { + "additionalProperties": false, + "title": "Image for the container used by the sonataflow data index", + "type": "string" + }, "eventing": { "additionalProperties": false, "properties": { @@ -209,6 +214,11 @@ "title": "Image for the init container used by the create-db job", "type": "string" }, + "jobServiceImage": { + "additionalProperties": false, + "title": "Image for the container used by the sonataflow jobs service", + "type": "string" + }, "monitoring": { "additionalProperties": false, "properties": { diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index 746d3459..c9f0352b 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -409,6 +409,16 @@ "title": "Image for the container used by the create-db job", "type": "string", "additionalProperties": false + }, + "jobServiceImage": { + "title": "Image for the container used by the sonataflow jobs service", + "type": "string", + "additionalProperties": false + }, + "dataIndexImage": { + "title": "Image for the container used by the sonataflow data index", + "type": "string", + "additionalProperties": false } } } diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index c4014fe0..c588c459 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -393,6 +393,13 @@ orchestrator: # -- Image for the container used by the create-db job createDBJobImage: "{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}" + # -- Image for the container used by the sonataflow jobs service, optional and used for disconnected environments + jobServiceImage: "" + + # -- Image for the container used by the sonataflow data index, optional and used for disconnected environments + dataIndexImage: "" + + # -- Orchestrator plugins and their configuration plugins: # RHDHBUGS-1464: Note that the plugins here fetch the packages from their direct HTTP download URLs from the (official) Red Hat NPM Registry. From 1d2eff6e89d8f42c51f48776c2a4b351a5ace20e Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 8 Sep 2025 11:28:17 +0200 Subject: [PATCH 097/117] fix(ci): Make sure to checkout the PR branch before pushing the diff generated from the pre-commit hooks (#230) Context: https://github.com/redhat-developer/rhdh-chart/actions/runs/17545997169/job/49827512012?pr=231 --- .github/workflows/pre-commit.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index b3f62f35..b13de2ff 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -21,7 +21,7 @@ jobs: # see list of approvers in OWNERS file environment: ${{ (github.event.pull_request.head.repo.full_name == github.repository || - contains(fromJSON('["coreydaley","gazarenkov","kadel","nickboldt","rm3l","kim-tsao","Fortune-Ndlovu","subhashkhileri","zdrapela","openshift-cherrypick-robot", "Fortune-Ndlovu", "subhashkhileri", "zdrapela"]'), github.event.pull_request.user.login)) && 'internal' || 'external' }} + contains(fromJSON('["gazarenkov","kadel","nickboldt","rm3l","kim-tsao","Fortune-Ndlovu","subhashkhileri","zdrapela","openshift-cherrypick-robot"]'), github.event.pull_request.user.login)) && 'internal' || 'external' }} runs-on: ubuntu-latest steps: - name: approved @@ -66,18 +66,22 @@ jobs: run: | echo "CHANGED=$(if git diff --quiet; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT - - name: Commit any changes + - name: Commit and push any manifest changes if: ${{ steps.diff-checker.outputs.CHANGED == 'true' }} run: | + git remote add fork "https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git" + git fetch fork ${{ github.event.pull_request.head.ref }} + git checkout -B pr-branch fork/${{ github.event.pull_request.head.ref }} + git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]@users.noreply.github.com' - git fetch --prune - git pull --rebase --autostash + git add -A . git commit \ -m "chore(pre-commit): Auto-fix hooks" \ -m "Co-authored-by: $GITHUB_ACTOR <$GITHUB_ACTOR@users.noreply.github.com>" - git push + + git push fork pr-branch:${{ github.event.pull_request.head.ref }} - name: Comment on PR if manifests were updated uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 From 2312184b84bba2305a522825f00aef658b3a001c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:39:40 +0200 Subject: [PATCH 098/117] chore(deps): update mikefarah/yq action to v4.47.2 (#233) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 461986c9..0e23ab84 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -96,7 +96,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@f03c9dc599c37bfcaf533427211d05e51e6fee64 # v4.47.1 + uses: mikefarah/yq@6251e95af8df3505def48c71f3119836701495d6 # v4.47.2 with: cmd: yq ".version" charts/${{ steps.command.outputs.chart-name }}/Chart.yaml @@ -109,7 +109,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@f03c9dc599c37bfcaf533427211d05e51e6fee64 # v4.47.1 + uses: mikefarah/yq@6251e95af8df3505def48c71f3119836701495d6 # v4.47.2 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/${{ steps.command.outputs.chart-name }}/Chart.yaml From 0bc62a4e8053140f3ffaece0bc5a6c5c1168d860 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:07:55 +0300 Subject: [PATCH 099/117] Installing network policies when OSL is disabled (#234) * Installing network policies when OSL is disabled * chart bump * typo fix --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/network-policies.yaml | 2 +- charts/backstage/values.yaml | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 851c2ee4..2c25f990 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.6 +version: 4.5.7 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 2128f352..ef5bff09 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.6](https://img.shields.io/badge/Version-4.5.6-informational?style=flat-square) +![Version: 4.5.7](https://img.shields.io/badge/Version-4.5.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/network-policies.yaml b/charts/backstage/templates/network-policies.yaml index 25722d09..6cfb9e7a 100644 --- a/charts/backstage/templates/network-policies.yaml +++ b/charts/backstage/templates/network-policies.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.orchestrator.enabled .Values.orchestrator.serverlessLogicOperator.enabled }} +{{- if .Values.orchestrator.enabled }} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index c588c459..a4b8ad6c 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -357,8 +357,10 @@ test: orchestrator: enabled: false + # Disable Serverless Logic Operator if it already exists on the cluster serverlessLogicOperator: enabled: true + # Disable Serverless Operator if it already exists on the cluster serverlessOperator: enabled: true sonataflowPlatform: @@ -399,7 +401,6 @@ orchestrator: # -- Image for the container used by the sonataflow data index, optional and used for disconnected environments dataIndexImage: "" - # -- Orchestrator plugins and their configuration plugins: # RHDHBUGS-1464: Note that the plugins here fetch the packages from their direct HTTP download URLs from the (official) Red Hat NPM Registry. From c6b7328bbad223c8e16e671c7d3fac3bc18a58c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Mac=C3=ADk?= Date: Tue, 9 Sep 2025 15:50:51 +0200 Subject: [PATCH 100/117] fix(RHDHBUGS-2015): fix sonataflow DB ref name for replication architecture of PostgreSQL (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(RHDHBUGS-2015): fix sonataflow DB ref name for replication architecture of PostgreSQL Signed-off-by: Pavel Macík * chore(pre-commit): Auto-fix hooks Co-authored-by: pmacik --------- Signed-off-by: Pavel Macík Co-authored-by: github-actions[bot] Co-authored-by: pmacik --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 2 +- charts/backstage/templates/sonataflows.yaml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 2c25f990..0fc1f178 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.7 +version: 4.5.8 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index ef5bff09..5fb6e5ea 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.7](https://img.shields.io/badge/Version-4.5.7-informational?style=flat-square) +![Version: 4.5.8](https://img.shields.io/badge/Version-4.5.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. diff --git a/charts/backstage/templates/sonataflows.yaml b/charts/backstage/templates/sonataflows.yaml index d845c7cd..d957f89d 100644 --- a/charts/backstage/templates/sonataflows.yaml +++ b/charts/backstage/templates/sonataflows.yaml @@ -41,7 +41,7 @@ spec: userKey: username passwordKey: password serviceRef: - name: {{ .Release.Name }}-postgresql + name: {{ .Release.Name }}-postgresql{{- if eq .Values.upstream.postgresql.architecture "replication" }}-primary{{- end }} namespace: {{ .Release.Namespace }} databaseName: sonataflow {{- else }} @@ -66,7 +66,7 @@ spec: userKey: username passwordKey: password serviceRef: - name: {{ .Release.Name }}-postgresql + name: {{ .Release.Name }}-postgresql{{- if eq .Values.upstream.postgresql.architecture "replication" }}-primary{{- end }} namespace: {{ .Release.Namespace }} databaseName: sonataflow {{- else }} @@ -113,7 +113,7 @@ spec: - -c - | {{- if .Values.upstream.postgresql.enabled }} - dbHost="{{ .Release.Name }}-postgresql" + dbHost="{{ .Release.Name }}-postgresql{{- if eq .Values.upstream.postgresql.architecture "replication" }}-primary{{- end }}" dbPort="5432" {{- else }} dbHost=${POSTGRES_HOST} @@ -186,7 +186,7 @@ spec: command: [ "sh", "-c" ] {{- if .Values.upstream.postgresql.enabled }} args: - - "psql -h {{ .Release.Name }}-postgresql -p 5432 -U postgres -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" + - "psql -h {{ .Release.Name }}-postgresql{{- if eq .Values.upstream.postgresql.architecture "replication" }}-primary{{- end }} -p 5432 -U postgres -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" {{- else }} args: - "psql -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} -d {{ .Values.orchestrator.sonataflowPlatform.externalDBName }} -c 'CREATE DATABASE sonataflow;' || echo WARNING: Could not create database" From bfb79f03d01561792f7f69a82347cdba52e4c9bc Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Wed, 10 Sep 2025 09:20:26 +0200 Subject: [PATCH 101/117] fix(orchestrator-infra): Rename the chart in the README [RHDHBUGS-2021] (#236) --- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 4 ++-- charts/orchestrator-infra/README.md.gotmpl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index f6ad685d..c57a4be6 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.2.1 +version: 0.2.2 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index 70bc9635..bc871720 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift -![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.1-informational?style=flat-square) +![Version: 0.2.2](https://img.shields.io/badge/Version-0.2.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. @@ -25,7 +25,7 @@ Kubernetes: `>= 1.25.0-0` ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-infra redhat-developer/orchestrator-infra +helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra ``` > **Tip**: List all releases using `helm list` diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index ee8fc96f..aa347321 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -20,7 +20,7 @@ ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-infra redhat-developer/orchestrator-infra +helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra ``` > **Tip**: List all releases using `helm list` From 1fda646c9eac627c5cd7596c3522f806483c3578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbyn=C4=9Bk=20Dr=C3=A1pela?= <61500440+zdrapela@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:37:06 +0200 Subject: [PATCH 102/117] chore: initial Qodo configuration (#240) --- .github/workflows/toml-checks.yaml | 21 +++++++++++++++++ .pr_agent.toml | 38 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 .github/workflows/toml-checks.yaml create mode 100644 .pr_agent.toml diff --git a/.github/workflows/toml-checks.yaml b/.github/workflows/toml-checks.yaml new file mode 100644 index 00000000..ec385981 --- /dev/null +++ b/.github/workflows/toml-checks.yaml @@ -0,0 +1,21 @@ +name: PR TOML Validator + +on: + push: + paths: + - '**.toml' + pull_request: + paths: + - '**.toml' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: tombi-toml/setup-tombi@faa41d967480fa9bb78cd3197a6c004eb4fd2d3e # v1 + with: + version: 'v0.6.3' + checksum: '85fbd6ab07c6faf2cc6d5c826c8a16255e10b5bc391621d391a76e9323124a4c' + - name: Validate TOML files + run: tombi lint \ No newline at end of file diff --git a/.pr_agent.toml b/.pr_agent.toml new file mode 100644 index 00000000..893f59e9 --- /dev/null +++ b/.pr_agent.toml @@ -0,0 +1,38 @@ +[jira] +jira_api_token = "${{ secrets.JIRA_API_TOKEN }}" +jira_base_url = "https://issues.redhat.com" + +[github_app] +# what should be launched automatically +pr_commands = [ + "/review", + "/describe --pr_description.final_update_message=false", + "/improve", +] +feedback_on_draft_pr = true + +[pr_reviewer] # /review # +persistent_comment = true +require_tests_review = false +require_ticket_analysis_review = true +enable_review_labels_security = true +enable_review_labels_effort = true + +[pr_description] # /describe # +enable_pr_diagram = false +publish_labels = true +final_update_message = true +# without this, it can interfere with Sourcery AI +publish_description_as_comment = true +publish_description_as_comment_persistent = true +add_original_user_description = false + +[pr_code_suggestions] +commitable_code_suggestions = false + +[config] +ignore_pr_authors = ["renovate","openshift-cherrypick-robot","rhdh-bot"] + +[rag_arguments] +enable_rag=true +rag_repo_list=['redhat-developer/rhdh','redhat-developer/red-hat-developers-documentation-rhdh','redhat-developer/rhdh-operator','redhat-developer/rhdh-chart'] From 15aa7efab4de9c4f85a64b32e9c72ab3eef726d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:05:31 +0000 Subject: [PATCH 103/117] chore(deps): update tombi-toml/setup-tombi digest to 3129912 (#242) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/toml-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/toml-checks.yaml b/.github/workflows/toml-checks.yaml index ec385981..4bbf3f8c 100644 --- a/.github/workflows/toml-checks.yaml +++ b/.github/workflows/toml-checks.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - - uses: tombi-toml/setup-tombi@faa41d967480fa9bb78cd3197a6c004eb4fd2d3e # v1 + - uses: tombi-toml/setup-tombi@31299123992b4a61821319e63752aaee8fd7996a # v1 with: version: 'v0.6.3' checksum: '85fbd6ab07c6faf2cc6d5c826c8a16255e10b5bc391621d391a76e9323124a4c' From d28eca076f20d345c1b4f6eca2b49be1e64211ce Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Thu, 11 Sep 2025 18:30:11 +0300 Subject: [PATCH 104/117] fix(orchestrator-software-templates-infra): fixing issue with namespace range (#241) --- charts/orchestrator-software-templates-infra/Chart.yaml | 2 +- charts/orchestrator-software-templates-infra/README.md | 2 +- .../argocd-application-controller-clusterrolebinding.yaml | 6 +++--- .../templates/openshift-gitops/argocd-cr.yaml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/orchestrator-software-templates-infra/Chart.yaml b/charts/orchestrator-software-templates-infra/Chart.yaml index 5d02a810..2ade692b 100644 --- a/charts/orchestrator-software-templates-infra/Chart.yaml +++ b/charts/orchestrator-software-templates-infra/Chart.yaml @@ -10,7 +10,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-software-templates-infrastructure -version: 0.2.2 +version: 0.2.3 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates-infra/README.md b/charts/orchestrator-software-templates-infra/README.md index 43467131..f70aad29 100644 --- a/charts/orchestrator-software-templates-infra/README.md +++ b/charts/orchestrator-software-templates-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Infra Chart for OpenShift (Community Version) -![Version: 0.2.2](https://img.shields.io/badge/Version-0.2.2-informational?style=flat-square) +![Version: 0.2.3](https://img.shields.io/badge/Version-0.2.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml index dd6b1941..32eb9cfe 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-application-controller-clusterrolebinding.yaml @@ -7,7 +7,7 @@ metadata: labels: app.kubernetes.io/component: application-controller app.kubernetes.io/name: {{ $ns }}-argocd-application-controller - app.kubernetes.io/part-of: {{ $.Values.openshiftGitops.namespaces | first }} + app.kubernetes.io/part-of: {{ $ns }} annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "0" @@ -16,10 +16,10 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ $.Values.openshiftGitops.namespaces | first }}-argocd-application-controller + name: {{ $ns }}-argocd-application-controller subjects: - kind: ServiceAccount - name: {{ $.Values.openshiftGitops.name }}-argocd-application-controller + name: {{ $ns }}-argocd-application-controller namespace: {{ $ns }} {{- end }} {{- end }} diff --git a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml index bd282c29..183d22bf 100644 --- a/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml +++ b/charts/orchestrator-software-templates-infra/templates/openshift-gitops/argocd-cr.yaml @@ -5,9 +5,9 @@ apiVersion: argoproj.io/v1beta1 kind: ArgoCD metadata: - name: {{ $.Values.openshiftGitops.name }} + name: {{ $ns }} labels: - app: {{ $.Values.openshiftGitops.name }} + app: {{ $ns }} namespace: {{ $ns }} annotations: helm.sh/hook: post-install,post-upgrade From 552d233b9a3563bc28601342480f3a4308ce66a3 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 11 Sep 2025 22:30:13 +0200 Subject: [PATCH 105/117] chore: Make sure `ct lint` runs against all charts (#243) Co-authored-by: rm3l Co-authored-by: github-actions[bot] --- .github/workflows/lint.yaml | 2 +- ct-lint.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 ct-lint.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index f8c8ece6..ea9ccee4 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -58,5 +58,5 @@ jobs: run: | ct lint \ --debug \ - --config ct.yaml \ + --config ct-lint.yaml \ --target-branch "${{ github.event.pull_request.base.ref }}" diff --git a/ct-lint.yaml b/ct-lint.yaml new file mode 100644 index 00000000..7d076c14 --- /dev/null +++ b/ct-lint.yaml @@ -0,0 +1,5 @@ +chart-dirs: + - charts +validate-maintainers: false +remote: origin +helm-extra-args: --timeout 500s From c8cc27f6cf869906c23d4603182dd3e95c9520c1 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:52:48 +0300 Subject: [PATCH 106/117] chore(backstage): bump orchestrator plugins to 1.7.1 prod release (#219) * bumping orch plugins to 1.7 prod release * updating plugin config * update orchestrator plugins to version 1.7.1 * Bump chart version * Apply pre-commit hooks * Fix trailing spaces * Fix lint errors by adding new line character at the end of file --------- Co-authored-by: Jennifer Ubah Co-authored-by: Armel Soro --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 4 ++-- charts/backstage/values.yaml | 37 ++++++++++++++++++++++++------------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 0fc1f178..a743e106 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.8 +version: 4.5.9 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 5fb6e5ea..526aa2e7 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.8](https://img.shields.io/badge/Version-4.5.8-informational?style=flat-square) +![Version: 4.5.9](https://img.shields.io/badge/Version-4.5.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -179,7 +179,7 @@ Kubernetes: `>= 1.27.0-0` | global.host | Custom hostname shorthand, overrides `global.clusterRouterBase`, `upstream.ingress.host`, `route.host`, and url values in `upstream.backstage.appConfig`. | string | `""` | | nameOverride | | string | `"developer-hub"` | | orchestrator.enabled | | bool | `false` | -| orchestrator.plugins | Orchestrator plugins and their configuration | list | `[{"disabled":false,"integrity":"sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator":{"appIcons":[{"importName":"OrchestratorIcon","name":"orchestratorIcon"}],"dynamicRoutes":[{"importName":"OrchestratorPage","menuItem":{"icon":"orchestratorIcon","text":"Orchestrator"},"path":"/orchestrator"}]}}}}},{"disabled":false,"integrity":"sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets":{}}}}}]` | +| orchestrator.plugins | Orchestrator plugins and their configuration | list | `[{"disabled":false,"integrity":"sha512-9cXbedr0lC7ns7SNqARrWSQI4JGcZFw5xpfpUzA1tJaMMUjzAdPHTXqljf62/fs4hYBK8TJsWJ2KJkGVMzbrHQ==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.7.1.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-Cqu9EQwVQ4mpdgWTUA0MW89Gul0IklhvkkqVoO3CloQ1dnAj1XyXikCphzH5TmNDDd9K66dOpaKKCaW9KeJ4WA==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.7.1.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator":{"appIcons":[{"importName":"OrchestratorIcon","name":"orchestratorIcon"}],"dynamicRoutes":[{"importName":"OrchestratorPage","menuItem":{"icon":"orchestratorIcon","text":"Orchestrator"},"path":"/orchestrator"}],"entityTabs":[{"mountPoint":"entity.page.workflows","path":"/workflows","title":"Workflows"}],"mountPoints":[{"config":{"if":{"anyOf":["IsOrchestratorCatalogTabAvailable"]},"layout":{"gridColumn":"1 / -1"}},"importName":"OrchestratorCatalogTab","mountPoint":"entity.page.workflows/cards"}]}}}}},{"disabled":false,"integrity":"sha512-J1sTjA5kj6DphG8D65go9KlpIfKyLN/wq+XlY5Cb5djEo8mvF3wn3Haf60OGFo5cP4OfRSWqFwT7LM5/dNVwAg==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.7.1.tgz","pluginConfig":{"orchestrator":{"dataIndexService":{"url":"http://sonataflow-platform-data-index-service.{{ .Release.Namespace }}"}}}},{"disabled":false,"integrity":"sha512-0KIXrZoJ+O4xNNzN/zB4+VMuaRPuiUviAmM+fIhTo/P9aLA36F9aIlyMbUbki49uaJ0zd8KXMBvmJSHZNrYkGQ==","package":"https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.7.1.tgz","pluginConfig":{"dynamicPlugins":{"frontend":{"red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets":{}}}}}]` | | orchestrator.serverlessLogicOperator.enabled | | bool | `true` | | orchestrator.serverlessOperator.enabled | | bool | `true` | | orchestrator.sonataflowPlatform.createDBJobImage | Image for the container used by the create-db job | string | `"{{ .Values.upstream.postgresql.image.registry }}/{{ .Values.upstream.postgresql.image.repository }}:{{ .Values.upstream.postgresql.image.tag }}"` | diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index a4b8ad6c..0924d91a 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -407,38 +407,51 @@ orchestrator: # Previously, we were using the "@redhat/plugin@version" form along with injecting a .npmrc Secret to resolve the "@redhat" scope, # but this caused conflicting issues with user-provided .npmrc secrets. - disabled: false - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.6.0.tgz" - integrity: sha512-Kr55YbuVwEADwGef9o9wyimcgHmiwehPeAtVHa9g2RQYoSPEa6BeOlaPzB6W5Ke3M2bN/0j0XXtpLuvrlXQogA== + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-backend-dynamic/-/backstage-plugin-orchestrator-backend-dynamic-1.7.1.tgz" + integrity: sha512-9cXbedr0lC7ns7SNqARrWSQI4JGcZFw5xpfpUzA1tJaMMUjzAdPHTXqljf62/fs4hYBK8TJsWJ2KJkGVMzbrHQ== pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - disabled: false - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.6.0.tgz" - integrity: sha512-fOSJv2PgtD2urKwBM7p9W6gV/0UIHSf4pkZ9V/wQO0eg0Zi5Mys/CL1ba3nO9x9l84MX11UBZ2r7PPVJPrmOtw== + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator/-/backstage-plugin-orchestrator-1.7.1.tgz" + integrity: sha512-Cqu9EQwVQ4mpdgWTUA0MW89Gul0IklhvkkqVoO3CloQ1dnAj1XyXikCphzH5TmNDDd9K66dOpaKKCaW9KeJ4WA== pluginConfig: dynamicPlugins: frontend: red-hat-developer-hub.backstage-plugin-orchestrator: appIcons: - - importName: OrchestratorIcon - name: orchestratorIcon + - name: orchestratorIcon + importName: OrchestratorIcon dynamicRoutes: - - importName: OrchestratorPage + - path: /orchestrator + importName: OrchestratorPage menuItem: icon: orchestratorIcon text: Orchestrator - path: /orchestrator + entityTabs: + - path: /workflows + title: Workflows + mountPoint: entity.page.workflows + mountPoints: + - mountPoint: entity.page.workflows/cards + importName: OrchestratorCatalogTab + config: + layout: + gridColumn: "1 / -1" + if: + anyOf: + - IsOrchestratorCatalogTabAvailable - disabled: false - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.6.0.tgz" - integrity: sha512-Bueeix4661fXEnfJ9y31Yw91LXJgw6hJUG7lPVdESCi9VwBCjDB9Rm8u2yPqP8sriwr0OMtKtqD+Odn3LOPyVw== + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic/-/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.7.1.tgz" + integrity: sha512-J1sTjA5kj6DphG8D65go9KlpIfKyLN/wq+XlY5Cb5djEo8mvF3wn3Haf60OGFo5cP4OfRSWqFwT7LM5/dNVwAg== pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service.{{ .Release.Namespace }} - disabled: false - package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.6.0.tgz" - integrity: sha512-Tqn6HO21Q1TQ7TFUoRhwBVCtSBzbQYz+OaanzzIB0R24O6YtVx3wR7Chtr5TzC05Vz5GkBO1+FZid8BKpqljgA== + package: "https://npm.registry.redhat.com/@redhat/backstage-plugin-orchestrator-form-widgets/-/backstage-plugin-orchestrator-form-widgets-1.7.1.tgz" + integrity: sha512-0KIXrZoJ+O4xNNzN/zB4+VMuaRPuiUviAmM+fIhTo/P9aLA36F9aIlyMbUbki49uaJ0zd8KXMBvmJSHZNrYkGQ== pluginConfig: dynamicPlugins: frontend: From 0a09b81f284c680699785bf1400899bc74da1375 Mon Sep 17 00:00:00 2001 From: Elai Shalev <129178340+elai-shalev@users.noreply.github.com> Date: Sun, 14 Sep 2025 15:32:54 +0300 Subject: [PATCH 107/117] Bumping OSL version in tekton task for workflow builder (#244) --- charts/orchestrator-software-templates/Chart.yaml | 2 +- charts/orchestrator-software-templates/README.md | 2 +- .../templates/tekton/tekton-tasks.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/orchestrator-software-templates/Chart.yaml b/charts/orchestrator-software-templates/Chart.yaml index 0c1be062..5a1e8f87 100644 --- a/charts/orchestrator-software-templates/Chart.yaml +++ b/charts/orchestrator-software-templates/Chart.yaml @@ -11,7 +11,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.1.0 +version: 0.1.1 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates/README.md b/charts/orchestrator-software-templates/README.md index 135e351c..88bff095 100644 --- a/charts/orchestrator-software-templates/README.md +++ b/charts/orchestrator-software-templates/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Chart for Red Hat Developer Hub -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Version: 0.1.1](https://img.shields.io/badge/Version-0.1.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH) and other necessary GitOps configurations. diff --git a/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml b/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml index 0f0fcca2..d3e32940 100644 --- a/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml +++ b/charts/orchestrator-software-templates/templates/tekton/tekton-tasks.yaml @@ -213,7 +213,7 @@ spec: workingDir: $(workspaces.workflow-source.path)/flat/$(params.workflowId) script: | microdnf install -y tar gzip - KN_CLI_URL="https://developers.redhat.com/content-gateway/file/pub/cgw/serverless-logic/1.35.0/kn-workflow-linux-amd64.tar.gz" + KN_CLI_URL="https://developers.redhat.com/content-gateway/file/pub/cgw/serverless-logic/1.36.0/kn-workflow-linux-amd64.tar.gz" curl -L "$KN_CLI_URL" | tar -xz --no-same-owner && chmod +x kn-workflow-linux-amd64 && mv kn-workflow-linux-amd64 kn-workflow ./kn-workflow gen-manifest --namespace "" --- From 5dab63f134073945db4aeaf17daaa8f10db289c4 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 16 Sep 2025 16:29:07 +0200 Subject: [PATCH 108/117] chore: Add version to the `helm install` command mentioned in the charts READMEs [RHDHBUGS-2037] (#239) Co-authored-by: rm3l Co-authored-by: github-actions[bot] --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 6 +++--- charts/backstage/README.md.gotmpl | 4 ++-- charts/orchestrator-infra/Chart.yaml | 2 +- charts/orchestrator-infra/README.md | 4 ++-- charts/orchestrator-infra/README.md.gotmpl | 2 +- charts/orchestrator-software-templates-infra/Chart.yaml | 2 +- charts/orchestrator-software-templates-infra/README.md | 4 ++-- .../orchestrator-software-templates-infra/README.md.gotmpl | 2 +- charts/orchestrator-software-templates/Chart.yaml | 2 +- charts/orchestrator-software-templates/README.md | 4 ++-- charts/orchestrator-software-templates/README.md.gotmpl | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index a743e106..0019ca48 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.9 +version: 4.5.10 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 526aa2e7..842fba85 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.9](https://img.shields.io/badge/Version-4.5.9-informational?style=flat-square) +![Version: 4.5.10](https://img.shields.io/badge/Version-4.5.10-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -13,7 +13,7 @@ The telemetry data collection feature is enabled by default. Red Hat Developer H ## Productized RHDH This repository now provides the productized RHDH chart. -For the **GENERALLY AVAILABLE** version of this chart, see: +For the **Generally Available** version of this chart, see: * https://github.com/openshift-helm-charts/charts - official releases to https://charts.openshift.io/ @@ -30,7 +30,7 @@ helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-backstage redhat-developer/backstage +helm install my-backstage redhat-developer/backstage --version 4.5.10 ``` ## Introduction diff --git a/charts/backstage/README.md.gotmpl b/charts/backstage/README.md.gotmpl index f9a280a7..c68a7119 100644 --- a/charts/backstage/README.md.gotmpl +++ b/charts/backstage/README.md.gotmpl @@ -12,7 +12,7 @@ ## Productized RHDH This repository now provides the productized RHDH chart. -For the **GENERALLY AVAILABLE** version of this chart, see: +For the **Generally Available** version of this chart, see: * https://github.com/openshift-helm-charts/charts - official releases to https://charts.openshift.io/ @@ -28,7 +28,7 @@ helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-backstage redhat-developer/backstage +helm install my-backstage redhat-developer/backstage --version {{ template "chart.version" . }} ``` ## Introduction diff --git a/charts/orchestrator-infra/Chart.yaml b/charts/orchestrator-infra/Chart.yaml index c57a4be6..50e31c43 100644 --- a/charts/orchestrator-infra/Chart.yaml +++ b/charts/orchestrator-infra/Chart.yaml @@ -14,4 +14,4 @@ maintainers: type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.2.2 +version: 0.2.3 diff --git a/charts/orchestrator-infra/README.md b/charts/orchestrator-infra/README.md index bc871720..5e2fc9be 100644 --- a/charts/orchestrator-infra/README.md +++ b/charts/orchestrator-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Infra Chart for OpenShift -![Version: 0.2.2](https://img.shields.io/badge/Version-0.2.2-informational?style=flat-square) +![Version: 0.2.3](https://img.shields.io/badge/Version-0.2.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) Helm chart to deploy the Orchestrator solution's required infrastructure suite on OpenShift, including OpenShift Serverless Operator and OpenShift Serverless Logic Operator, both required to configure Red Hat Developer Hub to use the Orchestrator. @@ -25,7 +25,7 @@ Kubernetes: `>= 1.25.0-0` ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra +helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra --version 0.2.3 ``` > **Tip**: List all releases using `helm list` diff --git a/charts/orchestrator-infra/README.md.gotmpl b/charts/orchestrator-infra/README.md.gotmpl index aa347321..ccc6ee15 100644 --- a/charts/orchestrator-infra/README.md.gotmpl +++ b/charts/orchestrator-infra/README.md.gotmpl @@ -20,7 +20,7 @@ ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra +helm install my-orchestrator-infra redhat-developer/redhat-developer-hub-orchestrator-infra --version {{ template "chart.version" . }} ``` > **Tip**: List all releases using `helm list` diff --git a/charts/orchestrator-software-templates-infra/Chart.yaml b/charts/orchestrator-software-templates-infra/Chart.yaml index 2ade692b..b3820ef1 100644 --- a/charts/orchestrator-software-templates-infra/Chart.yaml +++ b/charts/orchestrator-software-templates-infra/Chart.yaml @@ -10,7 +10,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-software-templates-infrastructure -version: 0.2.3 +version: 0.2.4 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates-infra/README.md b/charts/orchestrator-software-templates-infra/README.md index f70aad29..a76f45fb 100644 --- a/charts/orchestrator-software-templates-infra/README.md +++ b/charts/orchestrator-software-templates-infra/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Infra Chart for OpenShift (Community Version) -![Version: 0.2.3](https://img.shields.io/badge/Version-0.2.3-informational?style=flat-square) +![Version: 0.2.4](https://img.shields.io/badge/Version-0.2.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart to install Openshift GitOps and Openshift Pipelines, which are required operators for installing the Orchestrator Software Templates to be available on RHDH. @@ -25,7 +25,7 @@ Kubernetes: `>= 1.25.0-0` ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra +helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra --version 0.2.4 ``` > **Tip**: List all releases using `helm list` diff --git a/charts/orchestrator-software-templates-infra/README.md.gotmpl b/charts/orchestrator-software-templates-infra/README.md.gotmpl index 79f95dea..fe224d39 100644 --- a/charts/orchestrator-software-templates-infra/README.md.gotmpl +++ b/charts/orchestrator-software-templates-infra/README.md.gotmpl @@ -20,7 +20,7 @@ ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra +helm install my-orchestrator-software-templates-infra redhat-developer/orchestrator-software-templates-infra --version {{ template "chart.version" . }} ``` > **Tip**: List all releases using `helm list` diff --git a/charts/orchestrator-software-templates/Chart.yaml b/charts/orchestrator-software-templates/Chart.yaml index 5a1e8f87..fb272532 100644 --- a/charts/orchestrator-software-templates/Chart.yaml +++ b/charts/orchestrator-software-templates/Chart.yaml @@ -11,7 +11,7 @@ kubeVersion: ">= 1.25.0-0" type: application sources: - https://github.com/redhat-developer/rhdh-chart -version: 0.1.1 +version: 0.1.2 maintainers: - name: Red Hat Developer Hub Team url: https://github.com/redhat-developer/rhdh-chart diff --git a/charts/orchestrator-software-templates/README.md b/charts/orchestrator-software-templates/README.md index 88bff095..51198792 100644 --- a/charts/orchestrator-software-templates/README.md +++ b/charts/orchestrator-software-templates/README.md @@ -1,7 +1,7 @@ # Orchestrator Software Templates Chart for Red Hat Developer Hub -![Version: 0.1.1](https://img.shields.io/badge/Version-0.1.1-informational?style=flat-square) +![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) This Helm chart deploys the Orchestrator Software Templates for Red Hat Developer Hub (RHDH) and other necessary GitOps configurations. @@ -78,7 +78,7 @@ After configuring all prerequisites, you can install the chart with the followin ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates +helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates --version 0.1.2 ``` Now, follow the instruction on the post-installation Notes. They will include the steps to create a custom values.yaml file to allow you to update the backstage chart diff --git a/charts/orchestrator-software-templates/README.md.gotmpl b/charts/orchestrator-software-templates/README.md.gotmpl index 0325d495..265a4975 100644 --- a/charts/orchestrator-software-templates/README.md.gotmpl +++ b/charts/orchestrator-software-templates/README.md.gotmpl @@ -74,7 +74,7 @@ After configuring all prerequisites, you can install the chart with the followin ```console helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates +helm install my-orchestrator-templates redhat-developer/orchestrator-software-templates --version {{ template "chart.version" . }} ``` Now, follow the instruction on the post-installation Notes. They will include the steps to create a custom values.yaml file to allow you to update the backstage chart From 907ae5e7a4bc1730631376e6b11e01ecbdfd6f4c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:06:19 +0000 Subject: [PATCH 109/117] chore(deps): update snyk/actions action to v1 (#247) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/snyk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml index d043e67e..093fe74a 100644 --- a/.github/workflows/snyk.yaml +++ b/.github/workflows/snyk.yaml @@ -45,7 +45,7 @@ jobs: - name: Run Snyk IaC Scan for ${{ matrix.chartConfig.name }} continue-on-error: true - uses: snyk/actions/iac@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0 + uses: snyk/actions/iac@9adf32b1121593767fc3c057af55b55db032dc04 # v1.0.0 env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} SNYK_ORG_ID: ${{ secrets.SNYK_ORG_ID }} From e922273c4b7f236ab4dc63924296d8aa947f8f4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:18:34 +0000 Subject: [PATCH 110/117] chore(deps): update peter-evans/create-or-update-comment action to v5 (#246) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 0e23ab84..749ea583 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -52,7 +52,7 @@ jobs: - name: Add eyes reaction if: steps.command.outputs.command-name == 'bump' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} From 8bbb87c9a1fc7ed2b9b1bc5d4a7e9539382112be Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:05:53 +0000 Subject: [PATCH 111/117] chore(deps): update dependency python to 3.14 (#249) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/pre-commit.yaml | 2 +- .github/workflows/test.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index 749ea583..c198c1c6 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -62,7 +62,7 @@ jobs: - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 if: steps.command.outputs.command-name == 'bump' with: - python-version: 3.13 + python-version: 3.14 - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6 if: steps.command.outputs.command-name == 'bump' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index ea9ccee4..14a89a2f 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: 3.13 + python-version: 3.14 - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index b13de2ff..f64dacd4 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -46,7 +46,7 @@ jobs: - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: 3.13 + python-version: 3.14 - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6 with: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 52217412..8c94e058 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -46,7 +46,7 @@ jobs: - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: 3.13 + python-version: 3.14 - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 From 41dfe4519900f736da6cd9d5e0b9234beb3e30be Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Wed, 8 Oct 2025 18:19:12 +0200 Subject: [PATCH 112/117] chore(deps): Bump the versions of the tools installed by helm/chart-testing-action (#250) This is a follow-up to [1], to ensure that they work properly with Python 3.14 - see [2] [1] https://github.com/redhat-developer/rhdh-chart/pull/249 [2] https://github.com/redhat-developer/rhdh-chart/actions/runs/18350355942/job/52268343561?pr=248 --- .github/workflows/lint.yaml | 4 ++++ .github/workflows/test.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 14a89a2f..41a4dfd9 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -37,6 +37,10 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 + with: + version: '3.14.0' + yamllint_version: '1.37.1' + yamale_version: '6.0.0' - name: Run chart-testing (list-changed) id: list-changed diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8c94e058..951fc506 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -50,6 +50,10 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@0d28d3144d3a25ea2cc349d6e59901c4ff469b3b # v2.7.0 + with: + version: '3.14.0' + yamllint_version: '1.37.1' + yamale_version: '6.0.0' - name: Run chart-testing (list-changed) id: list-changed From 6012030dcb579695971452660bfe5bdd75e97094 Mon Sep 17 00:00:00 2001 From: Leanne Ahern <127859485+OpinionatedHeron@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:22:40 +0100 Subject: [PATCH 113/117] [chore] Setting initContainers pullPolicy to IfNotPresent (#248) * Setting pullPolicy to IfNotPresent Signed-off-by: Leanne Ahern * Updating chart version Signed-off-by: Leanne Ahern * chore(pre-commit): Auto-fix hooks Co-authored-by: OpinionatedHeron * Consistent IfNotPresent changes Signed-off-by: Leanne Ahern --------- Signed-off-by: Leanne Ahern Co-authored-by: github-actions[bot] Co-authored-by: OpinionatedHeron --- charts/backstage/Chart.yaml | 2 +- charts/backstage/README.md | 4 ++-- charts/backstage/templates/tests/test-connection.yaml | 2 +- charts/backstage/values.schema.json | 4 ++-- charts/backstage/values.schema.tmpl.json | 3 ++- charts/backstage/values.yaml | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 0019ca48..2a5ccc68 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.10 +version: 4.5.11 diff --git a/charts/backstage/README.md b/charts/backstage/README.md index 842fba85..da60f2df 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.10](https://img.shields.io/badge/Version-4.5.10-informational?style=flat-square) +![Version: 4.5.11](https://img.shields.io/badge/Version-4.5.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -30,7 +30,7 @@ helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-backstage redhat-developer/backstage --version 4.5.10 +helm install my-backstage redhat-developer/backstage --version 4.5.11 ``` ## Introduction diff --git a/charts/backstage/templates/tests/test-connection.yaml b/charts/backstage/templates/tests/test-connection.yaml index 85205b25..94b298ae 100644 --- a/charts/backstage/templates/tests/test-connection.yaml +++ b/charts/backstage/templates/tests/test-connection.yaml @@ -31,7 +31,7 @@ spec: - ls - /usr/bin/curl image: "{{ .Values.test.image.registry }}/{{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}" - imagePullPolicy: Always + imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c"] args: - | diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index 17e2e1c8..ff2b67f8 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -4556,7 +4556,7 @@ "type": "string" }, "pullPolicy": { - "default": "Always", + "default": "IfNotPresent", "description": "Ref: https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy", "enum": [ "Always", @@ -4620,7 +4620,7 @@ } ], "image": "{{ include \"backstage.image\" . }}", - "imagePullPolicy": "Always", + "imagePullPolicy": "IfNotPresent", "name": "install-dynamic-plugins", "resources": { "limits": { diff --git a/charts/backstage/values.schema.tmpl.json b/charts/backstage/values.schema.tmpl.json index c9f0352b..0b5fb37f 100644 --- a/charts/backstage/values.schema.tmpl.json +++ b/charts/backstage/values.schema.tmpl.json @@ -12,7 +12,8 @@ "image": { "registry": "quay.io", "repository": "janus-idp/redhat-backstage-build", - "tag": "latest" + "tag": "latest", + "pullPolicy": "IfNotPresent" } } } diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 0924d91a..9575468a 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -39,6 +39,7 @@ upstream: registry: quay.io repository: rhdh/rhdh-hub-rhel9 tag: latest + pullPolicy: IfNotPresent command: [] # FIXME (tumido): USE POSTGRES_PASSWORD and POSTGRES_USER instead of POSTGRES_ADMIN_PASSWORD # This is a hack. In {fedora,rhel}/postgresql images, regular user is forbidden @@ -212,7 +213,7 @@ upstream: # This following variable is required for orchestrator to startup properly. - name: MAX_ENTRY_SIZE value: "30000000" - imagePullPolicy: Always + imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root From 4c36ff5e9fd6ebbf46838f258dc49d13d23471ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:08:01 +0200 Subject: [PATCH 114/117] chore(deps): update stefanzweifel/git-auto-commit-action action to v7 (#252) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index c198c1c6..f56a88f2 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -124,7 +124,7 @@ jobs: - name: Commit pre-commit changes if: steps.command.outputs.command-name == 'bump' - uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6 + uses: stefanzweifel/git-auto-commit-action@28e16e81777b558cc906c8750092100bbb34c5e3 # v7 with: commit_message: Bump ${{ steps.command.outputs.chart-name }} version to ${{ steps.semver.outputs.new_version }} commit_options: "-s" From bbed806c6bbd06bd02b7b69263e30b0065d6bd67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:08:37 +0200 Subject: [PATCH 115/117] chore(deps): update mikefarah/yq action to v4.48.1 (#251) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/bump-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index f56a88f2..d8121587 100755 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -96,7 +96,7 @@ jobs: - name: Get version if: steps.command.outputs.command-name == 'bump' id: get_version - uses: mikefarah/yq@6251e95af8df3505def48c71f3119836701495d6 # v4.47.2 + uses: mikefarah/yq@0ecdce24e83f0fa127940334be98c86b07b0c488 # v4.48.1 with: cmd: yq ".version" charts/${{ steps.command.outputs.chart-name }}/Chart.yaml @@ -109,7 +109,7 @@ jobs: - name: Bump the version if: steps.command.outputs.command-name == 'bump' - uses: mikefarah/yq@6251e95af8df3505def48c71f3119836701495d6 # v4.47.2 + uses: mikefarah/yq@0ecdce24e83f0fa127940334be98c86b07b0c488 # v4.48.1 with: cmd: yq -i '.version = "${{ steps.semver.outputs.new_version }}"' charts/${{ steps.command.outputs.chart-name }}/Chart.yaml From 4e6e2df1d748c1e4bb25c62e17fd1325e543ea93 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 16 Oct 2025 17:01:05 +0200 Subject: [PATCH 116/117] chore: pin the default RHDH image to 1.6 This is to prevent future issues like if the next tag introduces breaking changes for the tests in this release branch --- charts/backstage/Chart.yaml | 2 +- charts/backstage/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/backstage/Chart.yaml b/charts/backstage/Chart.yaml index 2a5ccc68..bbfd0ddd 100644 --- a/charts/backstage/Chart.yaml +++ b/charts/backstage/Chart.yaml @@ -47,4 +47,4 @@ sources: [] # Versions are expected to follow Semantic Versioning (https://semver.org/) # Note that when this chart is published to https://github.com/openshift-helm-charts/charts # it will follow the RHDH versioning 1.y.z -version: 4.5.11 +version: 4.5.12 diff --git a/charts/backstage/values.yaml b/charts/backstage/values.yaml index 9575468a..05b87d23 100644 --- a/charts/backstage/values.yaml +++ b/charts/backstage/values.yaml @@ -38,7 +38,7 @@ upstream: image: registry: quay.io repository: rhdh/rhdh-hub-rhel9 - tag: latest + tag: "1.6" pullPolicy: IfNotPresent command: [] # FIXME (tumido): USE POSTGRES_PASSWORD and POSTGRES_USER instead of POSTGRES_ADMIN_PASSWORD From aafeb84081803a4707ad209687692f2799929bbb Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 16 Oct 2025 17:01:49 +0200 Subject: [PATCH 117/117] Apply pre-commit hooks --- charts/backstage/README.md | 4 ++-- charts/backstage/values.schema.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/backstage/README.md b/charts/backstage/README.md index da60f2df..d252b0ac 100644 --- a/charts/backstage/README.md +++ b/charts/backstage/README.md @@ -1,7 +1,7 @@ # RHDH Backstage Helm Chart for OpenShift -![Version: 4.5.11](https://img.shields.io/badge/Version-4.5.11-informational?style=flat-square) +![Version: 4.5.12](https://img.shields.io/badge/Version-4.5.12-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying Red Hat Developer Hub, which is a Red Hat supported version of Backstage. @@ -30,7 +30,7 @@ helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add backstage https://backstage.github.io/charts helm repo add redhat-developer https://redhat-developer.github.io/rhdh-chart -helm install my-backstage redhat-developer/backstage --version 4.5.11 +helm install my-backstage redhat-developer/backstage --version 4.5.12 ``` ## Introduction diff --git a/charts/backstage/values.schema.json b/charts/backstage/values.schema.json index ff2b67f8..44572de5 100644 --- a/charts/backstage/values.schema.json +++ b/charts/backstage/values.schema.json @@ -4593,7 +4593,7 @@ "type": "string" }, "tag": { - "default": "latest", + "default": "1.6", "description": "Immutable tags are recommended.", "title": "Backstage image tag", "type": "string"