diff --git a/charts/gitops-runtime/templates/hooks/pre-install/validate-values.yaml b/charts/gitops-runtime/templates/hooks/pre-install/validate-values.yaml index 543c124b..9b86b81e 100644 --- a/charts/gitops-runtime/templates/hooks/pre-install/validate-values.yaml +++ b/charts/gitops-runtime/templates/hooks/pre-install/validate-values.yaml @@ -35,11 +35,139 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - - name: VERSION + - name: CHART_VERSION value: {{ .Chart.Version }} + - name: ARGOCD_CHECK_VERSION + value: {{ not (get .Values "argo-cd").enabled | quote }} + - name: ARGOCD_LABELS + value: "{{ range $k, $v := .Values.installer.argoCdVersionCheck.argoServerLabels }}{{ $k }}={{ $v }},{{ end }}" + - name: ARGOCD_VERSION_PATH + value: "/api/version" + - name: REQUIRED_VERSION_CONSTRAINT + value: ">=2.12 <3" command: ["sh", "-c"] - args: - - cf helm validate --values /job_tmp/values.yaml --namespace ${NAMESPACE} --version ${VERSION} --hook --log-level debug + args: + - | # shell + # Function to find Argo CD service and export its name and port + get_argocd_service_info() { + local service_info + local service_count + + # Clean labels + CLEAN_LABELS=$(echo "$ARGOCD_LABELS" | sed 's/,$//') + + echo "Searching for Argo CD service in namespace '$NAMESPACE' with labels '$CLEAN_LABELS'" + service_info=$(kubectl get svc -n "$NAMESPACE" -l "$CLEAN_LABELS" -o json) + service_count=$(echo "$service_info" | jq '.items | length') + + if [ "$service_count" -eq 0 ]; then + echo "Error: No Argo CD service found matching labels '$CLEAN_LABELS' in namespace '$NAMESPACE'." + exit 1 + elif [ "$service_count" -gt 1 ]; then + echo "Warning: Found multiple services matching labels '$CLEAN_LABELS'. Using the first one found." + fi + + # Set global variables + SERVICE_NAME=$(echo "$service_info" | jq -r '.items[0].metadata.name') + SERVICE_PORT=$(echo "$service_info" | jq -r '.items[0].spec.ports[0].port') + + if [ -z "$SERVICE_NAME" ] || [ "$SERVICE_NAME" = "null" ] || [ -z "$SERVICE_PORT" ] || [ "$SERVICE_PORT" = "null" ]; then + echo "Error: Could not extract service name or port from the found service." + exit 1 + fi + + echo "Found Argo CD service '$SERVICE_NAME' on port '$SERVICE_PORT'" + } + + # Function to get and normalize the Argo CD root path + get_argocd_root_path() { + local root_path + + echo "Fetching Argo CD root path from ConfigMap '$ARGOCD_CM_PARAMS_NAME' in namespace '$NAMESPACE'..." + root_path=$(kubectl get configmap "$ARGOCD_CM_PARAMS_NAME" -n "$NAMESPACE" -o jsonpath='{.data.server\.rootpath}' 2>/dev/null || echo "") + + if [ -n "$root_path" ] && [ "$root_path" != "/" ]; then + root_path=$(echo "$root_path" | sed 's:/*$::') # Remove trailing slash + [ "${root_path#\/}" = "$root_path" ] && root_path="/$root_path" # Add leading slash if missing + elif [ "$root_path" = "/" ]; then + root_path="" # Treat as empty for URL construction + else + echo "Warning: 'server.rootpath' not found in ConfigMap '$ARGOCD_CM_PARAMS_NAME' or ConfigMap not found. Assuming default root path '/'. " + root_path="" # Default to empty string + fi + + # Set global variable + ARGOCD_ROOT_PATH="$root_path" + echo "Using Argo CD root path: '${ARGOCD_ROOT_PATH:-/}'" + } + + # Function to get the Argo CD version string via API + get_argocd_version_string() { + # Local variables for values obtained internally + local api_full_path + local target_url + local curl_opts + local version_json + local curl_exit_code + + # Call functions to get required info - they set global vars + # We'll use the global vars directly after calling + get_argocd_service_info + get_argocd_root_path + + # Construct Target URL using the globally set variables + api_full_path=$(echo "${ARGOCD_ROOT_PATH}${ARGOCD_VERSION_PATH}" | sed 's://:/:g') + target_url="http://${SERVICE_NAME}.${NAMESPACE}.svc.cluster.local:${SERVICE_PORT}${api_full_path}" + echo "Checking Argo CD version via API: $target_url" + + # Curl Execution + curl_opts="-sS --fail --connect-timeout 10 -L -k" # Base options, follow redirects + version_json=$(curl $curl_opts "$target_url") + curl_exit_code=$? + + if [ $curl_exit_code -ne 0 ]; then + echo "Error: Failed to connect to Argo CD API at $target_url (curl exit code: $curl_exit_code)." + exit 1 + fi + + # Version Parsing - Set global variable + VERSION_STRING=$(echo "$version_json" | jq -r '.Version') + if [ -z "$VERSION_STRING" ] || [ "$VERSION_STRING" = "null" ]; then + echo "Error: Could not parse '.Version' field from API response using jq." + echo "Response JSON: $version_json" + exit 1 + fi + } + + # Function to validate Argo CD version and perform semver check + validate_argocd_version() { + # Call function to get version string (sets VERSION_STRING) + # This function now internally calls get_argocd_service_info and get_argocd_root_path + get_argocd_version_string + + # Clean potential 'v' prefix for semver tool + CLEAN_VERSION_STRING=${VERSION_STRING#v} + + echo "Found Argo CD version string: $VERSION_STRING (using $CLEAN_VERSION_STRING for check)" + echo "Required version constraint: $REQUIRED_VERSION_CONSTRAINT" + + # --- Semver Check (using semver CLI) --- + echo "Performing semver check using 'semver-cli'..." + if semver-cli satisfies "$CLEAN_VERSION_STRING" "$REQUIRED_VERSION_CONSTRAINT"; then + echo "Argo CD version $VERSION_STRING satisfies range '$REQUIRED_VERSION_CONSTRAINT'." + else + echo "Error: Argo CD version $VERSION_STRING does not satisfy required range '$REQUIRED_VERSION_CONSTRAINT'." + exit 1 + fi + } + + if [ "$ARGOCD_CHECK_VERSION" = "true" ]; then + validate_argocd_version + fi + + # --- Helm Values Validation (cf cli) --- + echo "Argo CD version check passed. Validating helm values using cf cli..." + cf helm validate --values /job_tmp/values.yaml --namespace ${NAMESPACE} --version ${CHART_VERSION} --hook --log-level debug volumeMounts: - name: customized-values mountPath: "/job_tmp" diff --git a/charts/gitops-runtime/values.yaml b/charts/gitops-runtime/values.yaml index 2378c623..517e9b1c 100644 --- a/charts/gitops-runtime/values.yaml +++ b/charts/gitops-runtime/values.yaml @@ -182,6 +182,12 @@ installer: tag: "" pullPolicy: IfNotPresent + argoCdVersionCheck: + # Labels to find the Argo CD API server service + argoServerLabels: + app.kubernetes.io/component: server + app.kubernetes.io/part-of: argocd + # ----------------------------------------------------------------------------------------------------------------------- # Sealed secrets # ----------------------------------------------------------------------------------------------------------------------- diff --git a/installer-image/Dockerfile b/installer-image/Dockerfile index ef59d98e..f164c094 100644 --- a/installer-image/Dockerfile +++ b/installer-image/Dockerfile @@ -1,3 +1,8 @@ +FROM golang:1.24.2 AS go-build + +RUN go install github.com/davidrjonas/semver-cli@latest \ + && cp $GOPATH/bin/semver-cli /usr/local/bin/ + #bookworm-slim FROM debian:12.10-slim @@ -6,8 +11,9 @@ RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selectio ARG CF_CLI_VERSION=v0.2.6 ARG TARGETARCH -RUN apt-get update && apt-get install curl -y +RUN apt-get update && apt-get install curl jq -y RUN curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/${CF_CLI_VERSION}/cf-linux-${TARGETARCH}.tar.gz | tar zx && mv ./cf-linux-${TARGETARCH} /usr/local/bin/cf +COPY --from=go-build /usr/local/bin/semver-cli /usr/local/bin/semver-cli COPY --from=bitnami/kubectl:1.32.3 /opt/bitnami/kubectl/bin/kubectl /usr/local/bin/ RUN adduser --shell /bin/bash codefresh