Skip to content

feat: add Argo CD version check and validation in pre-install hook #462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions charts/gitops-runtime/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
# -----------------------------------------------------------------------------------------------------------------------
Expand Down
8 changes: 7 additions & 1 deletion installer-image/Dockerfile
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down