#!/usr/bin/env bash set -euo pipefail OUTPUT_DIR="./generated-helmops" API_VERSION="fleet.cattle.io/v1alpha1" RESOURCE_KIND="HelmOp" VALUES_ARGS=() WORKSPACE_NAMESPACE="fleet-local" usage() { cat <<'EOF' Usage: ./generate-fleet-helmops-from-helm.sh [options] Description: Legge le release presenti con `helm list -A`, recupera metadata e values per ogni release e genera un file YAML per release in formato HelmOp-style. Options: -o, --output-dir Directory di output (default: ./generated-helmops) -w, --workspace-namespace Namespace dove creare la risorsa HelmOp (default: fleet-local) --api-version apiVersion del manifest generato (default: fleet.cattle.io/v1alpha1) --kind kind del manifest generato (default: HelmOp) -a, --all-values Usa `helm get values -a -o yaml` -h, --help Mostra questo help Examples: ./generate-fleet-helmops-from-helm.sh ./generate-fleet-helmops-from-helm.sh -o ./clusters/idc/imported ./generate-fleet-helmops-from-helm.sh -w fleet-default ./generate-fleet-helmops-from-helm.sh --kind HelmChart --api-version helm.cattle.io/v1 EOF } require_cmd() { local cmd="$1" if ! command -v "$cmd" >/dev/null 2>&1; then echo "Errore: comando richiesto non trovato: $cmd" >&2 exit 1 fi } trim() { local value="$1" value="${value#"${value%%[![:space:]]*}"}" value="${value%"${value##*[![:space:]]}"}" printf '%s' "$value" } safe_file_name() { printf '%s' "$1" | tr '/\\:' '---' } extract_yaml_scalar() { local key="$1" local content="$2" printf '%s\n' "$content" \ | sed -n "s/^[[:space:]]*${key}:[[:space:]]*//p" \ | head -n 1 \ | sed 's/^"//; s/"$//; s/^\x27//; s/\x27$//' } extract_first_source() { local content="$1" awk ' /^sources:/ { in_sources=1; next } in_sources && /^[^[:space:]-]/ { exit } in_sources && /^[[:space:]]*-[[:space:]]*/ { sub(/^[[:space:]]*-[[:space:]]*/, "") print exit } ' <<< "$content" } extract_chart_name_from_ref() { local chart_ref="$1" if [[ "$chart_ref" =~ ^(.+)-([0-9][A-Za-z0-9.+_-]*)$ ]]; then printf '%s' "${BASH_REMATCH[1]}" else printf '%s' "$chart_ref" fi } extract_chart_version_from_ref() { local chart_ref="$1" if [[ "$chart_ref" =~ ^(.+)-([0-9][A-Za-z0-9.+_-]*)$ ]]; then printf '%s' "${BASH_REMATCH[2]}" fi } find_repo_url_from_local_cache() { local chart_name="$1" local chart_version="$2" local search_json="" local repo_alias="" [[ -z "$chart_name" ]] && return 0 search_json="$(helm search repo "$chart_name" --versions -o json 2>/dev/null || true)" [[ -z "$search_json" || "$search_json" == "[]" ]] && return 0 if [[ -n "$chart_version" ]]; then repo_alias="$(jq -r --arg chart "$chart_name" --arg version "$chart_version" ' map(select((.name | split("/") | last) == $chart and .version == $version)) | .[0].name // empty ' <<< "$search_json")" fi if [[ -z "$repo_alias" ]]; then repo_alias="$(jq -r --arg chart "$chart_name" ' map(select((.name | split("/") | last) == $chart)) | .[0].name // empty ' <<< "$search_json")" fi [[ -z "$repo_alias" ]] && return 0 repo_alias="${repo_alias%%/*}" helm repo list -o json 2>/dev/null \ | jq -r --arg alias "$repo_alias" 'map(select(.name == $alias)) | .[0].url // empty' } indent_file() { local file_path="$1" sed 's/^/ /' "$file_path" } while [[ $# -gt 0 ]]; do case "$1" in -o|--output-dir) OUTPUT_DIR="$2" shift 2 ;; -w|--workspace-namespace) WORKSPACE_NAMESPACE="$2" shift 2 ;; --api-version) API_VERSION="$2" shift 2 ;; --kind) RESOURCE_KIND="$2" shift 2 ;; -a|--all-values) VALUES_ARGS=(-a) shift ;; -h|--help) usage exit 0 ;; *) echo "Opzione sconosciuta: $1" >&2 usage >&2 exit 1 ;; esac done require_cmd helm require_cmd jq mkdir -p "$OUTPUT_DIR" releases_json="$(helm list -A -o json)" release_count="$(jq 'length' <<< "$releases_json")" if [[ "$release_count" -eq 0 ]]; then echo "Nessuna release Helm trovata." >&2 exit 0 fi echo "==> Release trovate: $release_count" echo "==> Directory output: $OUTPUT_DIR" generated_count=0 warning_count=0 while IFS= read -r release; do name="$(jq -r '.name' <<< "$release")" namespace="$(jq -r '.namespace' <<< "$release")" chart_ref="$(jq -r '.chart // ""' <<< "$release")" metadata_yaml="$(helm get metadata "$name" -n "$namespace" -o yaml 2>/dev/null || true)" if [[ -z "$metadata_yaml" ]]; then echo "! Impossibile leggere metadata per ${namespace}/${name}, salto." >&2 warning_count=$((warning_count + 1)) continue fi repo_url="$(trim "$(extract_yaml_scalar repo "$metadata_yaml")")" if [[ -z "$repo_url" ]]; then repo_url="$(trim "$(extract_yaml_scalar repository "$metadata_yaml")")" fi if [[ -z "$repo_url" ]]; then repo_url="$(trim "$(extract_yaml_scalar repoURL "$metadata_yaml")")" fi if [[ -z "$repo_url" ]]; then repo_url="$(trim "$(extract_first_source "$metadata_yaml")")" fi chart_name="$(trim "$(extract_yaml_scalar chart "$metadata_yaml")")" chart_version="$(trim "$(extract_yaml_scalar version "$metadata_yaml")")" if [[ -z "$chart_name" ]]; then chart_name="$(extract_chart_name_from_ref "$chart_ref")" fi if [[ -z "$chart_version" ]]; then chart_version="$(extract_chart_version_from_ref "$chart_ref")" fi if [[ -z "$repo_url" ]]; then repo_url="$(trim "$(find_repo_url_from_local_cache "$chart_name" "$chart_version")")" fi if [[ -z "$repo_url" ]]; then repo_url="REPO_URL_NOT_FOUND" echo "! Repo URL non trovato in metadata o cache locale per ${namespace}/${name}" >&2 warning_count=$((warning_count + 1)) fi if [[ -z "$chart_name" ]]; then chart_name="CHART_NAME_NOT_FOUND" echo "! Chart name non trovato per ${namespace}/${name}" >&2 warning_count=$((warning_count + 1)) fi values_file="$(mktemp)" if ! helm get values "$name" -n "$namespace" "${VALUES_ARGS[@]}" -o yaml > "$values_file" 2>/dev/null; then printf '{}\n' > "$values_file" echo "! Values non disponibili per ${namespace}/${name}, uso {}" >&2 warning_count=$((warning_count + 1)) fi if [[ ! -s "$values_file" ]]; then printf '{}\n' > "$values_file" fi values_compact="$(tr -d '[:space:]' < "$values_file")" ns_dir="${OUTPUT_DIR}/${namespace}" mkdir -p "$ns_dir" safe_name="$(safe_file_name "$name")" out_file="${ns_dir}/${safe_name}-helmop.yaml" cat > "$out_file" <> "$out_file" <> "$out_file" <> "$out_file" <