This commit is contained in:
alessandro barucci
2026-09-12 19:21:41 +02:00
parent 58a443a076
commit f8ffa9e065
19 changed files with 1275 additions and 3 deletions
+305
View File
@@ -0,0 +1,305 @@
#!/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 <dir> Directory di output (default: ./generated-helmops)
-w, --workspace-namespace <ns>
Namespace dove creare la risorsa HelmOp
(default: fleet-local)
--api-version <value> apiVersion del manifest generato
(default: fleet.cattle.io/v1alpha1)
--kind <value> 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" <<EOF
apiVersion: ${API_VERSION}
kind: ${RESOURCE_KIND}
metadata:
name: ${safe_name}
namespace: ${WORKSPACE_NAMESPACE}
spec:
namespace: ${namespace}
helm:
releaseName: ${name}
repo: ${repo_url}
chart: ${chart_name}
EOF
if [[ -n "$chart_version" ]]; then
cat >> "$out_file" <<EOF
version: ${chart_version}
EOF
fi
if [[ "$values_compact" == "{}" ]]; then
cat >> "$out_file" <<EOF
values: {}
EOF
else
cat >> "$out_file" <<EOF
values:
$(indent_file "$values_file")
EOF
fi
rm -f "$values_file"
echo "+ Generato ${out_file} da ${namespace}/${name}"
generated_count=$((generated_count + 1))
done < <(jq -c '.[]' <<< "$releases_json")
echo ""
echo "=== Riepilogo ==="
echo "File generati: $generated_count"
echo "Warning: $warning_count"
echo "Output: $OUTPUT_DIR"
echo "Workspace namespace: $WORKSPACE_NAMESPACE"
echo ""
echo "Nota: il repo URL viene letto da 'helm get metadata' e, se assente,"
echo " viene cercato nella cache locale di 'helm repo list/search repo'."
echo "Se vedi REPO_URL_NOT_FOUND, completa il campo repo manualmente."