diff --git a/.ci/clusters/global_backend_config.yaml b/.ci/clusters/global_backend_config.yaml index 8318b44f..1b24d81b 100644 --- a/.ci/clusters/global_backend_config.yaml +++ b/.ci/clusters/global_backend_config.yaml @@ -3,6 +3,7 @@ kind: BackendConfig metadata: name: global-backend-config spec: + autoUpdate: true env: global1: globalvalue1 shared1: fromglobal diff --git a/.ci/clusters/global_backend_config_without_env.yaml b/.ci/clusters/global_backend_config_without_env.yaml new file mode 100644 index 00000000..d3e3ba7a --- /dev/null +++ b/.ci/clusters/global_backend_config_without_env.yaml @@ -0,0 +1,10 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: BackendConfig +metadata: + name: global-backend-config +spec: + autoUpdate: true + pod: + liveness: + initialDelaySeconds: 10 + periodSeconds: 30 diff --git a/.ci/helm.sh b/.ci/helm.sh index 25c983ed..a1b8003e 100644 --- a/.ci/helm.sh +++ b/.ci/helm.sh @@ -598,7 +598,7 @@ function ci::verify_log_topic_with_auth() { } function ci::verify_env() { - pod="$1-function-0" + pod=$1 key=$2 expect=$3 result=$(kubectl exec -n ${NAMESPACE} ${pod} -- env | grep "${key}") diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/manifests.yaml b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/manifests.yaml new file mode 100644 index 00000000..5b3d872d --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/manifests.yaml @@ -0,0 +1,47 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: Source +metadata: + name: test-datagen-source +spec: + className: org.apache.pulsar.io.datagenerator.DataGeneratorSource + clusterName: test-pulsar + forwardSourceMessageProperty: true + image: docker.io/streamnative/pulsar-io-data-generator:3.2.2.1 + java: + extraDependenciesDir: /pulsar/lib + jar: connectors/pulsar-io-data-generator-3.2.2.1.nar + minReplicas: 1 + name: test-datagen-source + namespace: default + output: + producerConf: {} + topic: public/default/test-datagen-source + typeClassName: org.apache.pulsar.io.datagenerator.Person + processingGuarantee: atleast_once + pulsar: + authConfig: + oauth2Config: + audience: urn:sn:pulsar:sndev:test + issuerUrl: https://auth.sncloud-stg.dev/ + keySecretName: sn-platform-oauth2-private-key + keySecretKey: auth.json + pulsarConfig: test-source + replicas: 1 + resources: + limits: + cpu: "0.2" + memory: 1.1G + requests: + cpu: "0.1" + memory: 1G + sourceConfig: + sleepBetweenMessages: "5000" + tenant: public +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-source +data: + webServiceURL: http://sn-platform-pulsar-broker.default.svc.cluster.local:8080 + brokerServiceURL: pulsar://sn-platform-pulsar-broker.default.svc.cluster.local:6650 diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/mesh-config.yaml b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/mesh-config.yaml new file mode 100644 index 00000000..f691f91f --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/mesh-config.yaml @@ -0,0 +1,14 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: BackendConfig +metadata: + name: backend-config + namespace: default +spec: + env: + namespaced1: namespacedvalue1 + shared1: fromnamespace + podenv: backendconfigvalue + pod: + liveness: + initialDelaySeconds: 30 + periodSeconds: 10 diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/verify.sh b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/verify.sh new file mode 100644 index 00000000..61c5db38 --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/verify.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -e + +E2E_DIR=$(dirname "$0") +BASE_DIR=$(cd "${E2E_DIR}"/../../../../..;pwd) +PULSAR_NAMESPACE=${PULSAR_NAMESPACE:-"default"} +PULSAR_RELEASE_NAME=${PULSAR_RELEASE_NAME:-"sn-platform"} +E2E_KUBECONFIG=${E2E_KUBECONFIG:-"/tmp/e2e-k8s.config"} + +source "${BASE_DIR}"/.ci/helm.sh + +if [ ! "$KUBECONFIG" ]; then + export KUBECONFIG=${E2E_KUBECONFIG} +fi + +manifests_file="${BASE_DIR}"/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/manifests.yaml +mesh_config_file="${BASE_DIR}"/.ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/mesh-config.yaml +global_mesh_config_file="${BASE_DIR}"/.ci/clusters/global_backend_config.yaml + +kubectl apply -f "${mesh_config_file}" > /dev/null 2>&1 +kubectl apply -f "${manifests_file}" > /dev/null 2>&1 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-source 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" global1 global1=globalvalue1 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" namespaced1 namespaced1=namespacedvalue1 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# if global and namespaced config has same key, the value from namespace should be used +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" shared1 shared1=fromnamespace 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# verify liveness config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-source-source-0 '{"failureThreshold":3,"httpGet":{"path":"/","port":9094,"scheme":"HTTP"},"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":10}' 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# update the namespaced config, it should not trigger the reconcile since the autoUpdate is false +kubectl patch BackendConfig backend-config --type='json' -p='[{"op": "replace", "path": "/spec/env/shared1", "value": "newvalue"}]' > /dev/null 2>&1 +sleep 30 + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" shared1 shared1=fromnamespace 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# delete the namespaced config, the source should not be reconciled since the autoUpdate is false +kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 +sleep 30 + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" namespaced1 namespaced1=namespacedvalue1 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" shared1 shared1=fromnamespace 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-source-source-0 '{"failureThreshold":3,"httpGet":{"path":"/","port":9094,"scheme":"HTTP"},"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":10}' 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# delete the global config, the source should be reconciled since the autoUpdate is true in the global config +kubectl delete -f "${global_mesh_config_file}" -n $FUNCTION_MESH_NAMESPACE > /dev/null 2>&1 || true +sleep 30 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-source 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" global1 "" 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" namespaced1 "" 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "test-datagen-source-source-0" shared1 "" 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# it should use liveness config from namespaced config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-source-source-0 "" 2>&1) +if [ $? -eq 0 ]; then + echo "e2e-test: ok" | yq eval - +else + echo "$verify_liveness_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/manifests.yaml b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/manifests.yaml new file mode 100644 index 00000000..710aa747 --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/manifests.yaml @@ -0,0 +1,50 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: Sink +metadata: + name: test-datagen-sink +spec: + autoAck: true + className: org.apache.pulsar.io.datagenerator.DataGeneratorPrintSink + clusterName: test-pulsar + image: docker.io/streamnative/pulsar-io-data-generator:3.2.2.1 + input: + sourceSpecs: + public/default/datagen: + receiverQueueSize: 1000 + topics: + - public/default/datagen + typeClassName: org.apache.pulsar.io.datagenerator.Person + java: + extraDependenciesDir: /pulsar/lib + jar: connectors/pulsar-io-data-generator-3.2.2.1.nar + minReplicas: 1 + namespace: default + processingGuarantee: atleast_once + sinkConfig: {} + pulsar: + authConfig: + oauth2Config: + audience: urn:sn:pulsar:sndev:test + issuerUrl: https://auth.sncloud-stg.dev/ + keySecretName: sn-platform-oauth2-private-key + keySecretKey: auth.json + pulsarConfig: test-sink + replicas: 1 + resources: + limits: + cpu: "0.2" + memory: 1.1G + requests: + cpu: "0.1" + memory: 1G + subscriptionName: mysub + subscriptionPosition: latest + tenant: public +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-sink +data: + webServiceURL: http://sn-platform-pulsar-broker.default.svc.cluster.local:8080 + brokerServiceURL: pulsar://sn-platform-pulsar-broker.default.svc.cluster.local:6650 diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config-kube-system.yaml b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config-kube-system.yaml new file mode 100644 index 00000000..87164859 --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config-kube-system.yaml @@ -0,0 +1,11 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: BackendConfig +metadata: + name: backend-config + namespace: kube-system +spec: + autoUpdate: true + pod: + liveness: + initialDelaySeconds: 50 + periodSeconds: 60 diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config.yaml b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config.yaml new file mode 100644 index 00000000..6e2beef1 --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config.yaml @@ -0,0 +1,11 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: BackendConfig +metadata: + name: backend-config + namespace: default +spec: + autoUpdate: true + pod: + liveness: + initialDelaySeconds: 30 + periodSeconds: 10 diff --git a/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/verify.sh b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/verify.sh new file mode 100644 index 00000000..8f52ee53 --- /dev/null +++ b/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/verify.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +set -e + +E2E_DIR=$(dirname "$0") +BASE_DIR=$(cd "${E2E_DIR}"/../../../../..;pwd) +PULSAR_NAMESPACE=${PULSAR_NAMESPACE:-"default"} +PULSAR_RELEASE_NAME=${PULSAR_RELEASE_NAME:-"sn-platform"} +E2E_KUBECONFIG=${E2E_KUBECONFIG:-"/tmp/e2e-k8s.config"} + +source "${BASE_DIR}"/.ci/helm.sh + +if [ ! "$KUBECONFIG" ]; then + export KUBECONFIG=${E2E_KUBECONFIG} +fi + +manifests_file="${BASE_DIR}"/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/manifests.yaml +mesh_config_file="${BASE_DIR}"/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config.yaml +mesh_config_file_in_kube_system="${BASE_DIR}"/.ci/tests/integration-oauth2/cases/global-and-namespaced-config/mesh-config-kube-system.yaml +global_mesh_config_file="${BASE_DIR}"/.ci/clusters/global_backend_config.yaml + + +kubectl apply -f "${mesh_config_file}" > /dev/null 2>&1 +kubectl apply -f "${manifests_file}" > /dev/null 2>&1 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-sink 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# verify liveness config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-sink-sink-0 '{"failureThreshold":3,"httpGet":{"path":"/","port":9094,"scheme":"HTTP"},"initialDelaySeconds":30,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":10}' 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# delete the namespaced config, the sink should be reconciled +kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 +sleep 30 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-sink 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# it should use liveness config from global config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-sink-sink-0 '{"failureThreshold":3,"httpGet":{"path":"/","port":9094,"scheme":"HTTP"},"initialDelaySeconds":10,"periodSeconds":30,"successThreshold":1,"timeoutSeconds":30}' 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# delete the global config, the sink should be reconciled +kubectl delete -f "${global_mesh_config_file}" -n $FUNCTION_MESH_NAMESPACE > /dev/null 2>&1 || true +sleep 30 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-sink 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# it should has no liveness config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-sink-sink-0 "" 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# config created in an another namespace should not affect sinks in other namespaces +kubectl apply -f "${mesh_config_file_in_kube_system}" > /dev/null 2>&1 +sleep 30 + +verify_fm_result=$(ci::verify_function_mesh test-datagen-sink 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_fm_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# it should has no liveness config +verify_liveness_result=$(ci::verify_liveness_probe test-datagen-sink-sink-0 "" 2>&1) +if [ $? -eq 0 ]; then + echo "e2e-test: ok" | yq eval - +else + echo "$verify_liveness_result" + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true diff --git a/.ci/tests/integration-oauth2/e2e.yaml b/.ci/tests/integration-oauth2/e2e.yaml index a5915cf7..a7e8660c 100644 --- a/.ci/tests/integration-oauth2/e2e.yaml +++ b/.ci/tests/integration-oauth2/e2e.yaml @@ -100,12 +100,15 @@ setup: image="function-mesh:latest" docker build --platform linux/amd64 -f operator.Dockerfile -t $image . kind load docker-image ${image} - helm install ${FUNCTION_MESH_RELEASE_NAME} -n ${FUNCTION_MESH_NAMESPACE} --set operatorImage=${image} --create-namespace charts/function-mesh-operator + helm install ${FUNCTION_MESH_RELEASE_NAME} -n ${FUNCTION_MESH_NAMESPACE} --set operatorImage=${image} --set controllerManager.globalBackendConfig=global-backend-config --set controllerManager.globalBackendConfigNamespace=${FUNCTION_MESH_NAMESPACE} --set controllerManager.namespacedBackendConfig=backend-config --create-namespace charts/function-mesh-operator wait: - namespace: function-mesh resource: pod label-selector: app.kubernetes.io/name=function-mesh-operator for: condition=Ready + - name: apply global env config map + command: | + kubectl create -n ${FUNCTION_MESH_NAMESPACE} -f .ci/clusters/global_backend_config_without_env.yaml timeout: 60m cleanup: @@ -140,3 +143,5 @@ verify: expected: expected.data.yaml - query: timeout 5m bash .ci/tests/integration-oauth2/cases/google-pubsub-source/verify.sh expected: expected.data.yaml + - query: timeout 5m bash .ci/tests/integration-oauth2/cases/global-and-namespaced-config/verify.sh + expected: expected.data.yaml diff --git a/.ci/tests/integration-oauth2/e2e_with_downloader.yaml b/.ci/tests/integration-oauth2/e2e_with_downloader.yaml index 7888e342..486885ed 100644 --- a/.ci/tests/integration-oauth2/e2e_with_downloader.yaml +++ b/.ci/tests/integration-oauth2/e2e_with_downloader.yaml @@ -95,12 +95,16 @@ setup: image="function-mesh:latest" docker build --platform linux/amd64 -f operator.Dockerfile -t $image . kind load docker-image ${image} - helm install ${FUNCTION_MESH_RELEASE_NAME} -n ${FUNCTION_MESH_NAMESPACE} --set operatorImage=${image} --set controllerManager.enableInitContainers=true --create-namespace charts/function-mesh-operator + helm install ${FUNCTION_MESH_RELEASE_NAME} -n ${FUNCTION_MESH_NAMESPACE} --set operatorImage=${image} --set controllerManager.enableInitContainers=true --set controllerManager.globalBackendConfig=global-backend-config --set controllerManager.globalBackendConfigNamespace=${FUNCTION_MESH_NAMESPACE} --set controllerManager.namespacedBackendConfig=backend-config --create-namespace charts/function-mesh-operator wait: - namespace: function-mesh resource: pod label-selector: app.kubernetes.io/name=function-mesh-operator for: condition=Ready + + - name: apply global env config map + command: | + kubectl create -n ${FUNCTION_MESH_NAMESPACE} -f .ci/clusters/global_backend_config.yaml timeout: 60m cleanup: @@ -127,3 +131,5 @@ verify: expected: expected.data.yaml - query: timeout 5m bash .ci/tests/integration-oauth2/cases/py-download-from-http-function/verify.sh expected: expected.data.yaml + - query: timeout 5m bash .ci/tests/integration-oauth2/cases/global-and-namespaced-config-without-auto-update/verify.sh + expected: expected.data.yaml diff --git a/.ci/tests/integration/cases/global-and-namespaced-config/manifests.yaml b/.ci/tests/integration/cases/global-and-namespaced-config/manifests.yaml index d298d20c..9bf4ea09 100644 --- a/.ci/tests/integration/cases/global-and-namespaced-config/manifests.yaml +++ b/.ci/tests/integration/cases/global-and-namespaced-config/manifests.yaml @@ -15,6 +15,10 @@ spec: topics: - persistent://public/default/input-java-topic typeClassName: java.lang.String + pod: + env: + - name: "podenv" + value: "podvalue" output: topic: persistent://public/default/output-java-topic typeClassName: java.lang.String diff --git a/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config-kube-system.yaml b/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config-kube-system.yaml index 332f259a..a4a33b11 100644 --- a/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config-kube-system.yaml +++ b/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config-kube-system.yaml @@ -4,6 +4,7 @@ metadata: name: backend-config namespace: kube-system spec: + autoUpdate: true env: namespaced1: namespacedvalue1 shared1: fromnamespace diff --git a/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config.yaml b/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config.yaml index e4d48787..30c00eed 100644 --- a/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config.yaml +++ b/.ci/tests/integration/cases/global-and-namespaced-config/mesh-config.yaml @@ -4,9 +4,11 @@ metadata: name: backend-config namespace: default spec: + autoUpdate: true env: namespaced1: namespacedvalue1 shared1: fromnamespace + podenv: backendconfigvalue pod: liveness: initialDelaySeconds: 30 diff --git a/.ci/tests/integration/cases/global-and-namespaced-config/verify.sh b/.ci/tests/integration/cases/global-and-namespaced-config/verify.sh index 1363b0d9..b3044f62 100644 --- a/.ci/tests/integration/cases/global-and-namespaced-config/verify.sh +++ b/.ci/tests/integration/cases/global-and-namespaced-config/verify.sh @@ -48,7 +48,8 @@ if [ $? -ne 0 ]; then exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" global1 global1=globalvalue1 2>&1) +# if the function defines the same env, it will use the value from the function instead of the backend config +verify_env_result=$(ci::verify_env "function-sample-env-function-0" podenv podenv=podvalue 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 @@ -56,7 +57,15 @@ if [ $? -ne 0 ]; then exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" namespaced1 namespaced1=namespacedvalue1 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" global1 global1=globalvalue1 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +verify_env_result=$(ci::verify_env "function-sample-env-function-0" namespaced1 namespaced1=namespacedvalue1 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 @@ -65,7 +74,7 @@ if [ $? -ne 0 ]; then fi # if global and namespaced config has same key, the value from namespace should be used -verify_env_result=$(ci::verify_env "function-sample-env" shared1 shared1=fromnamespace 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" shared1 shared1=fromnamespace 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 @@ -82,6 +91,30 @@ if [ $? -ne 0 ]; then exit 1 fi +# update the namespaced config, it should trigger the reconcile since the autoUpdate is true +kubectl patch BackendConfig backend-config --type='json' -p='[{"op": "replace", "path": "/spec/env/shared1", "value": "newvalue"}]' > /dev/null 2>&1 +sleep 30 + +verify_env_result=$(ci::verify_env "function-sample-env-function-0" shared1 shared1=newvalue 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_env_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + +# the liveness probe should also be updated +kubectl patch BackendConfig backend-config --type='json' -p='[{"op": "replace", "path": "/spec/pod/liveness/initialDelaySeconds", "value": 20}]' > /dev/null 2>&1 +sleep 30 + +verify_liveness_result=$(ci::verify_liveness_probe function-sample-env-function-0 '{"failureThreshold":3,"httpGet":{"path":"/","port":9094,"scheme":"HTTP"},"initialDelaySeconds":20,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":10}' 2>&1) +if [ $? -ne 0 ]; then + echo "$verify_liveness_result" + kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 + kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true + exit 1 +fi + # delete the namespaced config, the function should be reconciled without namespaced env injected kubectl delete -f "${mesh_config_file}" > /dev/null 2>&1 sleep 30 @@ -93,21 +126,21 @@ if [ $? -ne 0 ]; then exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" global1 global1=globalvalue1 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" global1 global1=globalvalue1 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" shared1 shared1=fromglobal 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" shared1 shared1=fromglobal 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" namespaced1 "" 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" namespaced1 "" 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true @@ -133,7 +166,7 @@ if [ $? -ne 0 ]; then exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" global1 "" 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" global1 "" 2>&1) if [ $? -ne 0 ]; then echo "$verify_env_result" kubectl delete -f "${manifests_file}" > /dev/null 2>&1 || true @@ -167,7 +200,7 @@ if [ $? -ne 0 ]; then exit 1 fi -verify_env_result=$(ci::verify_env "function-sample-env" namespaced1 "" 2>&1) +verify_env_result=$(ci::verify_env "function-sample-env-function-0" namespaced1 "" 2>&1) if [ $? -eq 0 ]; then echo "e2e-test: ok" | yq eval - else diff --git a/.github/workflows/bundle-release.yml b/.github/workflows/bundle-release.yml index 65a4ae59..6fa1ba5a 100644 --- a/.github/workflows/bundle-release.yml +++ b/.github/workflows/bundle-release.yml @@ -42,10 +42,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder @@ -166,10 +166,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder diff --git a/.github/workflows/olm-verify.yml b/.github/workflows/olm-verify.yml index e2da86f8..f95939ec 100644 --- a/.github/workflows/olm-verify.yml +++ b/.github/workflows/olm-verify.yml @@ -27,10 +27,10 @@ jobs: - name: checkout uses: actions/checkout@v2 - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 04228781..7854d2bb 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.21.9, 1.22.4] + go-version: [1.21.9, 1.22.5] steps: - name: clean disk run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c07cfa8..aa680198 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,10 +30,10 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder diff --git a/.github/workflows/test-helm-charts.yml b/.github/workflows/test-helm-charts.yml index f0f33034..4ae84ad8 100644 --- a/.github/workflows/test-helm-charts.yml +++ b/.github/workflows/test-helm-charts.yml @@ -76,11 +76,11 @@ jobs: run: hack/kind-cluster-build.sh --name chart-testing -c 1 -v 10 --k8sVersion v1.23.17 if: steps.list-changed.outputs.changed == 'true' - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 if: steps.list-changed.outputs.changed == 'true' uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: setup kubebuilder 3.6.0 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 285ae4d6..25146e1c 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -31,10 +31,10 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} ref: ${{ github.event.pull_request.head.sha }} - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder diff --git a/.github/workflows/trivy_scheduled_master.yml b/.github/workflows/trivy_scheduled_master.yml index db7ba163..84b4ed3d 100644 --- a/.github/workflows/trivy_scheduled_master.yml +++ b/.github/workflows/trivy_scheduled_master.yml @@ -42,10 +42,10 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} ref: ${{ github.event.pull_request.head.sha }} - - name: Set up GO 1.22.4 + - name: Set up GO 1.22.5 uses: actions/setup-go@v1 with: - go-version: 1.22.4 + go-version: 1.22.5 id: go - name: InstallKubebuilder diff --git a/Dockerfile b/Dockerfile index ed2c9966..819be6f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22.4-bullseye as builder +FROM golang:1.22.5-bullseye as builder WORKDIR /workspace/api COPY api/ . diff --git a/api/compute/v1alpha1/backendconfig_types.go b/api/compute/v1alpha1/backendconfig_types.go index 8b7253de..8a900982 100644 --- a/api/compute/v1alpha1/backendconfig_types.go +++ b/api/compute/v1alpha1/backendconfig_types.go @@ -36,6 +36,10 @@ type BackendConfigSpec struct { // +kubebuilder:validation:Optional Pod *BackendConfigPodPolicy `json:"pod,omitempty"` + + // +kubebuilder:validation:Optional + // indicate whether auto update functions&sinks&source when the BackendConfig is updated + AutoUpdate bool `json:"autoUpdate,omitempty"` } // BackendConfigPodPolicy defines the policy for the pod diff --git a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-backendconfigs.yaml b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-backendconfigs.yaml index bd30e9f0..6119c571 100644 --- a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-backendconfigs.yaml +++ b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-backendconfigs.yaml @@ -48,6 +48,8 @@ spec: type: object spec: properties: + autoUpdate: + type: boolean env: additionalProperties: type: string diff --git a/config/crd/bases/compute.functionmesh.io_backendconfigs.yaml b/config/crd/bases/compute.functionmesh.io_backendconfigs.yaml index 5db956b8..7456de85 100644 --- a/config/crd/bases/compute.functionmesh.io_backendconfigs.yaml +++ b/config/crd/bases/compute.functionmesh.io_backendconfigs.yaml @@ -26,6 +26,8 @@ spec: type: object spec: properties: + autoUpdate: + type: boolean env: additionalProperties: type: string diff --git a/controllers/common.go b/controllers/common.go index f43d0a7c..d2321ffd 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -24,8 +24,11 @@ import ( "github.com/streamnative/function-mesh/utils" autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/api/meta" "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/go-logr/logr" "github.com/streamnative/function-mesh/api/compute/v1alpha1" @@ -380,3 +383,50 @@ func getBackgroundDeletionPolicy() client.DeleteOption { } return deleteOptions } + +func listAndEnqueueRequests(ctx context.Context, mgr manager.Manager, list client.ObjectList, namespace string) ([]reconcile.Request, error) { + err := mgr.GetClient().List(ctx, list, client.InNamespace(namespace)) + if err != nil { + mgr.GetLogger().Error(err, "failed to list resources in namespace: "+namespace) + return nil, err + } + requests := []reconcile.Request{} + items, err := meta.ExtractList(list) + if err != nil { + return nil, err + } + for _, item := range items { + metaObj, err := meta.Accessor(item) + if err != nil { + return nil, err + } + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{Namespace: metaObj.GetNamespace(), Name: metaObj.GetName()}, + }) + } + return requests, nil +} + +func handleBackendConfigEnqueueRequests(mgr manager.Manager, object client.Object, listType client.ObjectList) []reconcile.Request { + backendConfig := object.(*v1alpha1.BackendConfig) + // if autoUpdate is false, we don't need to reconcile functions + if !backendConfig.Spec.AutoUpdate { + return nil + } + + var namespace string + if object.GetName() == utils.GlobalBackendConfig && object.GetNamespace() == utils.GlobalBackendConfigNamespace { + namespace = "" + } else if object.GetName() == utils.NamespacedBackendConfig { + namespace = object.GetNamespace() + } else { + return nil + } + + ctx := context.Background() + requests, err := listAndEnqueueRequests(ctx, mgr, listType, namespace) + if err != nil { + return nil + } + return requests +} diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 41096ed2..cba15048 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -23,7 +23,6 @@ import ( "github.com/streamnative/function-mesh/pkg/monitoring" v1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -188,37 +187,7 @@ func (r *FunctionReconciler) SetupWithManager(mgr ctrl.Manager) error { manager.Watches(&v1alpha1.BackendConfig{}, handler.EnqueueRequestsFromMapFunc( func(ctx context.Context, object client.Object) []reconcile.Request { - if object.GetName() == utils.GlobalBackendConfig && object.GetNamespace() == utils.GlobalBackendConfigNamespace { - ctx := context.Background() - functions := &v1alpha1.FunctionList{} - err := mgr.GetClient().List(ctx, functions) - if err != nil { - mgr.GetLogger().Error(err, "failed to list all functions") - } - var requests []reconcile.Request - for _, function := range functions.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: function.Namespace, Name: function.Name}, - }) - } - return requests - } else if object.GetName() == utils.NamespacedBackendConfig { - ctx := context.Background() - functions := &v1alpha1.FunctionList{} - err := mgr.GetClient().List(ctx, functions, client.InNamespace(object.GetNamespace())) - if err != nil { - mgr.GetLogger().Error(err, "failed to list functions in namespace: "+object.GetNamespace()) - } - var requests []reconcile.Request - for _, function := range functions.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: function.Namespace, Name: function.Name}, - }) - } - return requests - } else { - return nil - } + return handleBackendConfigEnqueueRequests(mgr, object, &v1alpha1.FunctionList{}) }), ) diff --git a/controllers/sink_controller.go b/controllers/sink_controller.go index 52f831cb..711f362c 100644 --- a/controllers/sink_controller.go +++ b/controllers/sink_controller.go @@ -23,7 +23,6 @@ import ( "github.com/streamnative/function-mesh/pkg/monitoring" v1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -193,37 +192,7 @@ func (r *SinkReconciler) SetupWithManager(mgr ctrl.Manager) error { manager.Watches(&v1alpha1.BackendConfig{}, handler.EnqueueRequestsFromMapFunc( func(ctx context.Context, object client.Object) []reconcile.Request { - if object.GetName() == utils.GlobalBackendConfig && object.GetNamespace() == utils.GlobalBackendConfigNamespace { - ctx := context.Background() - sinks := &v1alpha1.SinkList{} - err := mgr.GetClient().List(ctx, sinks) - if err != nil { - mgr.GetLogger().Error(err, "failed to list all sinks") - } - var requests []reconcile.Request - for _, sink := range sinks.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: sink.Namespace, Name: sink.Name}, - }) - } - return requests - } else if object.GetName() == utils.NamespacedBackendConfig { - ctx := context.Background() - sinks := &v1alpha1.SinkList{} - err := mgr.GetClient().List(ctx, sinks, client.InNamespace(object.GetNamespace())) - if err != nil { - mgr.GetLogger().Error(err, "failed to list sinks in namespace: "+object.GetNamespace()) - } - var requests []reconcile.Request - for _, sink := range sinks.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: sink.Namespace, Name: sink.Name}, - }) - } - return requests - } else { - return nil - } + return handleBackendConfigEnqueueRequests(mgr, object, &v1alpha1.SinkList{}) }), ) diff --git a/controllers/source_controller.go b/controllers/source_controller.go index 5b1a0b75..70da00cc 100644 --- a/controllers/source_controller.go +++ b/controllers/source_controller.go @@ -31,7 +31,6 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -191,36 +190,7 @@ func (r *SourceReconciler) SetupWithManager(mgr ctrl.Manager) error { } manager.Watches(&v1alpha1.BackendConfig{}, handler.EnqueueRequestsFromMapFunc( func(ctx context.Context, object client.Object) []reconcile.Request { - if object.GetName() == utils.GlobalBackendConfig && object.GetNamespace() == utils.GlobalBackendConfigNamespace { - sources := &v1alpha1.SourceList{} - err := mgr.GetClient().List(ctx, sources) - if err != nil { - mgr.GetLogger().Error(err, "failed to list all sources") - } - var requests []reconcile.Request - for _, source := range sources.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: source.Namespace, Name: source.Name}, - }) - } - return requests - } else if object.GetName() == utils.NamespacedBackendConfig { - ctx := context.Background() - sources := &v1alpha1.SourceList{} - err := mgr.GetClient().List(ctx, sources, client.InNamespace(object.GetNamespace())) - if err != nil { - mgr.GetLogger().Error(err, "failed to list sources in namespace: "+object.GetNamespace()) - } - var requests []reconcile.Request - for _, source := range sources.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: types.NamespacedName{Namespace: source.Namespace, Name: source.Name}, - }) - } - return requests - } else { - return nil - } + return handleBackendConfigEnqueueRequests(mgr, object, &v1alpha1.SourceList{}) }), ) return manager.Complete(r) diff --git a/controllers/spec/common.go b/controllers/spec/common.go index 25ca428e..d9d2dd82 100644 --- a/controllers/spec/common.go +++ b/controllers/spec/common.go @@ -341,10 +341,7 @@ func PatchStatefulSet(ctx context.Context, cli client.Client, namespace string, } // merge env - if len(envData) == 0 { - return globalBackendConfigVersion, namespacedBackendConfigVersion, nil - } - globalEnvs := make([]corev1.EnvVar, 0, len(envData)) + globalEnvs := []corev1.EnvVar{} for key, val := range envData { globalEnvs = append(globalEnvs, corev1.EnvVar{ Name: key, @@ -353,7 +350,9 @@ func PatchStatefulSet(ctx context.Context, cli client.Client, namespace string, } for i := range statefulSet.Spec.Template.Spec.Containers { container := &statefulSet.Spec.Template.Spec.Containers[i] - container.Env = append(container.Env, globalEnvs...) + if len(globalEnvs) > 0 { + container.Env = append(globalEnvs, container.Env...) + } // configs which only work for the workload container switch container.Name { @@ -2083,6 +2082,7 @@ func CheckIfStatefulSetSpecIsEqual(spec *appsv1.StatefulSetSpec, desiredSpec *ap if !reflect.DeepEqual(container.Command, desiredContainer.Command) || container.Image != desiredContainer.Image || container.ImagePullPolicy != desiredContainer.ImagePullPolicy || + container.LivenessProbe != desiredContainer.LivenessProbe || !reflect.DeepEqual(ports, desiredPorts) || !reflect.DeepEqual(containerEnvFrom, desiredContainerEnvFrom) || !reflect.DeepEqual(container.Resources, desiredContainer.Resources) { diff --git a/redhat.Dockerfile b/redhat.Dockerfile index fdad419b..31dec7e0 100644 --- a/redhat.Dockerfile +++ b/redhat.Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22.4-bullseye as builder +FROM golang:1.22.5-bullseye as builder WORKDIR /workspace/api COPY api/ .