From 4a2cc0f9d0998b104b85fb1d65e24cef3988c1e0 Mon Sep 17 00:00:00 2001 From: Angel Marin Date: Fri, 6 Mar 2026 11:35:34 +0100 Subject: [PATCH] HYPERFLEET-720 - fix: update adapter configs --- deploy-scripts/lib/adapter.sh | 498 +++++++------- .../cl-deployment/adapter-config.yaml | 41 +- .../cl-deployment/adapter-task-config.yaml | 252 ++++--- .../adapter-configs/cl-deployment/values.yaml | 6 +- .../cl-job/adapter-config.yaml | 41 +- .../cl-job/adapter-task-config.yaml | 344 +++++----- testdata/adapter-configs/cl-job/values.yaml | 6 +- .../cl-maestro/adapter-config.yaml | 118 ++-- .../cl-maestro/adapter-task-config.yaml | 634 +++++++++--------- .../adapter-configs/cl-maestro/values.yaml | 6 +- .../cl-namespace/adapter-config.yaml | 41 +- .../cl-namespace/adapter-task-config.yaml | 274 ++++---- .../adapter-configs/cl-namespace/values.yaml | 6 +- .../np-configmap/adapter-config.yaml | 41 +- .../np-configmap/adapter-task-config.yaml | 252 ++++--- .../adapter-configs/np-configmap/values.yaml | 6 +- 16 files changed, 1258 insertions(+), 1308 deletions(-) diff --git a/deploy-scripts/lib/adapter.sh b/deploy-scripts/lib/adapter.sh index b2158ac..bb4dfa4 100755 --- a/deploy-scripts/lib/adapter.sh +++ b/deploy-scripts/lib/adapter.sh @@ -10,79 +10,79 @@ # ============================================================================ discover_adapters() { - # Use ADAPTERS_FILE_DIR env var, fallback to default - local adapter_configs_dir="${ADAPTERS_FILE_DIR:-${TESTDATA_DIR}/adapter-configs}" + # Use ADAPTERS_FILE_DIR env var, fallback to default + local adapter_configs_dir="${ADAPTERS_FILE_DIR:-${TESTDATA_DIR}/adapter-configs}" - if [[ ! -d "${adapter_configs_dir}" ]]; then - log_verbose "Adapter configs directory not found: ${adapter_configs_dir}" >&2 - return 1 - fi + if [[ ! -d "${adapter_configs_dir}" ]]; then + log_verbose "Adapter configs directory not found: ${adapter_configs_dir}" >&2 + return 1 + fi - # Read adapter names from environment variables - local cluster_adapters="${CLUSTER_TIER0_ADAPTERS_DEPLOYMENT:-}" - local nodepool_adapters="${NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT:-}" + # Read adapter names from environment variables + local cluster_adapters="${CLUSTER_TIER0_ADAPTERS_DEPLOYMENT:-}" + local nodepool_adapters="${NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT:-}" - if [[ -z "${cluster_adapters}" && -z "${nodepool_adapters}" ]]; then - log_error "No adapters specified. Set CLUSTER_TIER0_ADAPTERS_DEPLOYMENT and/or NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT" >&2 - return 1 - fi + if [[ -z "${cluster_adapters}" && -z "${nodepool_adapters}" ]]; then + log_error "No adapters specified. Set CLUSTER_TIER0_ADAPTERS_DEPLOYMENT and/or NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT" >&2 + return 1 + fi - # Build list of adapter directories from environment variables - local adapter_dirs=() + # Build list of adapter directories from environment variables + local adapter_dirs=() - # Add cluster adapters - if [[ -n "${cluster_adapters}" ]]; then + # Add cluster adapters + if [[ -n "${cluster_adapters}" ]]; then IFS=',' read -ra cluster_adapter_array <<< "${cluster_adapters}" - for adapter_name in "${cluster_adapter_array[@]}"; do - # Trim whitespace - adapter_name=$(echo "${adapter_name}" | xargs) - # Validate adapter name is not empty (prevents issues from trailing commas) - if [[ -z "${adapter_name}" ]]; then - log_error "Empty adapter name in CLUSTER_TIER0_ADAPTERS_DEPLOYMENT (check for trailing commas)" >&2 - return 1 - fi - if [[ -d "${adapter_configs_dir}/${adapter_name}" ]]; then - adapter_dirs+=("clusters|${adapter_name}") - else - log_error "Cluster adapter directory not found: ${adapter_configs_dir}/${adapter_name}" >&2 - return 1 - fi - done - fi + for adapter_name in "${cluster_adapter_array[@]}"; do + # Trim whitespace + adapter_name=$(echo "${adapter_name}" | xargs) + # Validate adapter name is not empty (prevents issues from trailing commas) + if [[ -z "${adapter_name}" ]]; then + log_error "Empty adapter name in CLUSTER_TIER0_ADAPTERS_DEPLOYMENT (check for trailing commas)" >&2 + return 1 + fi + if [[ -d "${adapter_configs_dir}/${adapter_name}" ]]; then + adapter_dirs+=("clusters|${adapter_name}") + else + log_error "Cluster adapter directory not found: ${adapter_configs_dir}/${adapter_name}" >&2 + return 1 + fi + done + fi - # Add nodepool adapters - if [[ -n "${nodepool_adapters}" ]]; then + # Add nodepool adapters + if [[ -n "${nodepool_adapters}" ]]; then IFS=',' read -ra nodepool_adapter_array <<< "${nodepool_adapters}" - for adapter_name in "${nodepool_adapter_array[@]}"; do - # Trim whitespace - adapter_name=$(echo "${adapter_name}" | xargs) - # Validate adapter name is not empty (prevents issues from trailing commas) - if [[ -z "${adapter_name}" ]]; then - log_error "Empty adapter name in NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT (check for trailing commas)" >&2 - return 1 - fi - if [[ -d "${adapter_configs_dir}/${adapter_name}" ]]; then - adapter_dirs+=("nodepools|${adapter_name}") - else - log_error "NodePool adapter directory not found: ${adapter_configs_dir}/${adapter_name}" >&2 - return 1 - fi - done - fi - - if [[ ${#adapter_dirs[@]} -eq 0 ]]; then - log_verbose "No adapter configurations found" >&2 + for adapter_name in "${nodepool_adapter_array[@]}"; do + # Trim whitespace + adapter_name=$(echo "${adapter_name}" | xargs) + # Validate adapter name is not empty (prevents issues from trailing commas) + if [[ -z "${adapter_name}" ]]; then + log_error "Empty adapter name in NODEPOOL_TIER0_ADAPTERS_DEPLOYMENT (check for trailing commas)" >&2 return 1 - fi - - log_info "Found ${#adapter_dirs[@]} adapter(s) to deploy:" >&2 - for dir in "${adapter_dirs[@]}"; do - log_info " - ${dir}" >&2 + fi + if [[ -d "${adapter_configs_dir}/${adapter_name}" ]]; then + adapter_dirs+=("nodepools|${adapter_name}") + else + log_error "NodePool adapter directory not found: ${adapter_configs_dir}/${adapter_name}" >&2 + return 1 + fi done + fi - # Export for use in other functions - # Format: resource_type|adapter_name (e.g., "clusters|cl-namespace") - printf '%s\n' "${adapter_dirs[@]}" + if [[ ${#adapter_dirs[@]} -eq 0 ]]; then + log_verbose "No adapter configurations found" >&2 + return 1 + fi + + log_info "Found ${#adapter_dirs[@]} adapter(s) to deploy:" >&2 + for dir in "${adapter_dirs[@]}"; do + log_info " - ${dir}" >&2 + done + + # Export for use in other functions + # Format: resource_type|adapter_name (e.g., "clusters|cl-namespace") + printf '%s\n' "${adapter_dirs[@]}" } # ============================================================================ @@ -90,151 +90,151 @@ discover_adapters() { # ============================================================================ install_adapter_instance() { - local dir_name="$1" + local dir_name="$1" - log_section "Installing Adapter: ${dir_name}" + log_section "Installing Adapter: ${dir_name}" - # Extract resource_type and adapter_name from format: resource_type|adapter_name - local resource_type="${dir_name%%|*}" - local adapter_name="${dir_name##*|}" + # Extract resource_type and adapter_name from format: resource_type|adapter_name + local resource_type="${dir_name%%|*}" + local adapter_name="${dir_name##*|}" - # Validate the descriptor format and ensure both parts are non-empty - if [[ -z "${resource_type}" || -z "${adapter_name}" || "${dir_name}" != *"|"* ]]; then - log_error "Invalid adapter descriptor '${dir_name}'. Expected format: resource_type|adapter_name" - return 1 - fi + # Validate the descriptor format and ensure both parts are non-empty + if [[ -z "${resource_type}" || -z "${adapter_name}" || "${dir_name}" != *"|"* ]]; then + log_error "Invalid adapter descriptor '${dir_name}'. Expected format: resource_type|adapter_name" + return 1 + fi - log_info "Resource type: ${resource_type}" - log_info "Adapter name: ${adapter_name}" + log_info "Resource type: ${resource_type}" + log_info "Adapter name: ${adapter_name}" - # Construct release name - local release_name="adapter-${resource_type}-${adapter_name}" + # Construct release name + local release_name="adapter-${resource_type}-${adapter_name}" - # Source adapter config directory (using ADAPTERS_FILE_DIR env var) - local adapter_configs_dir="${ADAPTERS_FILE_DIR:-${TESTDATA_DIR}/adapter-configs}" - local source_adapter_dir="${adapter_configs_dir}/${adapter_name}" + # Source adapter config directory (using ADAPTERS_FILE_DIR env var) + local adapter_configs_dir="${ADAPTERS_FILE_DIR:-${TESTDATA_DIR}/adapter-configs}" + local source_adapter_dir="${adapter_configs_dir}/${adapter_name}" - if [[ ! -d "${source_adapter_dir}" ]]; then - log_error "Adapter config directory not found: ${source_adapter_dir}" - return 1 - fi + if [[ ! -d "${source_adapter_dir}" ]]; then + log_error "Adapter config directory not found: ${source_adapter_dir}" + return 1 + fi - # Chart path - local full_chart_path="${WORK_DIR}/adapter/${ADAPTER_CHART_PATH}" - - # Copy adapter config folder to chart directory - local dest_adapter_dir="${full_chart_path}/${adapter_name}" - log_info "Copying adapter config from ${source_adapter_dir} to ${dest_adapter_dir}" - - if [[ -d "${dest_adapter_dir}" ]]; then - # Safety check: ensure dest_adapter_dir contains adapter_name to prevent accidental deletion - if [[ "${dest_adapter_dir}" != *"${adapter_name}" || "${dest_adapter_dir}" == "/" || "${dest_adapter_dir}" == "${full_chart_path}" ]]; then - log_error "Safety check failed: refusing to delete suspicious path: ${dest_adapter_dir}" - return 1 - fi - log_verbose "Removing existing adapter config directory: ${dest_adapter_dir}" - rm -rf "${dest_adapter_dir}" - fi + # Chart path + local full_chart_path="${WORK_DIR}/adapter/${ADAPTER_CHART_PATH}" - cp -r "${source_adapter_dir}" "${dest_adapter_dir}" + # Copy adapter config folder to chart directory + local dest_adapter_dir="${full_chart_path}/${adapter_name}" + log_info "Copying adapter config from ${source_adapter_dir} to ${dest_adapter_dir}" - # Values file path (now in the chart directory) - local values_file="${dest_adapter_dir}/values.yaml" - if [[ ! -f "${values_file}" ]]; then - log_error "Values file not found: ${values_file}" - return 1 - fi - - # Construct subscription ID and topic names - # Allow override from environment variables, otherwise use auto-generated defaults - local subscription_id="${ADAPTER_SUBSCRIPTION_ID:-${NAMESPACE}-${resource_type}-${adapter_name}}" - local topic="${ADAPTER_TOPIC:-${NAMESPACE}-${resource_type}}" - local dead_letter_topic="${ADAPTER_DEAD_LETTER_TOPIC:-${NAMESPACE}-${resource_type}-dlq}" - - if [[ "${DRY_RUN}" == "true" ]]; then - log_info "[DRY-RUN] Would install adapter with:" - log_info " Release name: ${release_name}" - log_info " Namespace: ${NAMESPACE}" - log_info " Chart path: ${full_chart_path}" - log_info " Values file: ${values_file}" - log_info " Image: ${IMAGE_REGISTRY}/${ADAPTER_IMAGE_REPO}:${ADAPTER_IMAGE_TAG}" - log_info " Subscription ID: ${subscription_id}" - log_info " Topic: ${topic}" - log_info " Dead Letter Topic: ${dead_letter_topic}" - return 0 + if [[ -d "${dest_adapter_dir}" ]]; then + # Safety check: ensure dest_adapter_dir contains adapter_name to prevent accidental deletion + if [[ "${dest_adapter_dir}" != *"${adapter_name}" || "${dest_adapter_dir}" == "/" || "${dest_adapter_dir}" == "${full_chart_path}" ]]; then + log_error "Safety check failed: refusing to delete suspicious path: ${dest_adapter_dir}" + return 1 fi - - - # Build helm command - local helm_cmd=( - helm upgrade --install - "${release_name}" - "${full_chart_path}" - --namespace "${NAMESPACE}" - --create-namespace - --wait - --timeout 5m - -f "${values_file}" - --set "fullnameOverride=${release_name}" - --set "image.registry=${IMAGE_REGISTRY}" - --set "image.repository=${ADAPTER_IMAGE_REPO}" - --set "image.tag=${ADAPTER_IMAGE_TAG}" - --set "broker.googlepubsub.projectId=${GCP_PROJECT_ID}" - --set "broker.googlepubsub.createTopicIfMissing=${ADAPTER_GOOGLEPUBSUB_CREATE_TOPIC_IF_MISSING}" - --set "broker.googlepubsub.createSubscriptionIfMissing=${ADAPTER_GOOGLEPUBSUB_CREATE_SUBSCRIPTION_IF_MISSING}" - --set "broker.googlepubsub.subscriptionId=${subscription_id}" - --set "broker.googlepubsub.topic=${topic}" - --set "broker.googlepubsub.deadLetterTopic=${dead_letter_topic}" - ) - - log_info "Executing Helm command:" - log_info "${helm_cmd[*]}" - echo - - if "${helm_cmd[@]}"; then - log_success "Adapter ${adapter_name} for ${resource_type} Helm release created successfully" - - # Verify pod health - log_info "Verifying pod health..." - if verify_pod_health "${NAMESPACE}" "app.kubernetes.io/instance=${release_name}" "${adapter_name}" 120 5; then - log_success "Adapter ${adapter_name} for ${resource_type} is running and healthy" - else - log_error "Adapter ${adapter_name} for ${resource_type} deployment failed health check" - log_info "Checking pod logs for troubleshooting:" - kubectl logs -n "${NAMESPACE}" -l "app.kubernetes.io/instance=${release_name}" --tail=50 2>/dev/null || true - return 1 - fi + log_verbose "Removing existing adapter config directory: ${dest_adapter_dir}" + rm -rf "${dest_adapter_dir}" + fi + + cp -r "${source_adapter_dir}" "${dest_adapter_dir}" + + # Values file path (now in the chart directory) + local values_file="${dest_adapter_dir}/values.yaml" + if [[ ! -f "${values_file}" ]]; then + log_error "Values file not found: ${values_file}" + return 1 + fi + + # Construct subscription ID and topic names + # Allow override from environment variables, otherwise use auto-generated defaults + local subscription_id="${ADAPTER_SUBSCRIPTION_ID:-${NAMESPACE}-${resource_type}-${adapter_name}}" + local topic="${ADAPTER_TOPIC:-${NAMESPACE}-${resource_type}}" + local dead_letter_topic="${ADAPTER_DEAD_LETTER_TOPIC:-${NAMESPACE}-${resource_type}-dlq}" + + if [[ "${DRY_RUN}" == "true" ]]; then + log_info "[DRY-RUN] Would install adapter with:" + log_info " Release name: ${release_name}" + log_info " Namespace: ${NAMESPACE}" + log_info " Chart path: ${full_chart_path}" + log_info " Values file: ${values_file}" + log_info " Image: ${IMAGE_REGISTRY}/${ADAPTER_IMAGE_REPO}:${ADAPTER_IMAGE_TAG}" + log_info " Subscription ID: ${subscription_id}" + log_info " Topic: ${topic}" + log_info " Dead Letter Topic: ${dead_letter_topic}" + return 0 + fi + + + # Build helm command + local helm_cmd=( + helm upgrade --install + "${release_name}" + "${full_chart_path}" + --namespace "${NAMESPACE}" + --create-namespace + --wait + --timeout 5m + -f "${values_file}" + --set "fullnameOverride=${release_name}" + --set "image.registry=${IMAGE_REGISTRY}" + --set "image.repository=${ADAPTER_IMAGE_REPO}" + --set "image.tag=${ADAPTER_IMAGE_TAG}" + --set "broker.googlepubsub.project_id=${GCP_PROJECT_ID}" + --set "broker.googlepubsub.create_topic_if_missing=${ADAPTER_GOOGLEPUBSUB_CREATE_TOPIC_IF_MISSING}" + --set "broker.googlepubsub.create_subscription_if_missing=${ADAPTER_GOOGLEPUBSUB_CREATE_SUBSCRIPTION_IF_MISSING}" + --set "broker.googlepubsub.subscription_id=${subscription_id}" + --set "broker.googlepubsub.topic=${topic}" + --set "broker.googlepubsub.dead_letter_topic=${dead_letter_topic}" + ) + + log_info "Executing Helm command:" + log_info "${helm_cmd[*]}" + echo + + if "${helm_cmd[@]}"; then + log_success "Adapter ${adapter_name} for ${resource_type} Helm release created successfully" + + # Verify pod health + log_info "Verifying pod health..." + if verify_pod_health "${NAMESPACE}" "app.kubernetes.io/instance=${release_name}" "${adapter_name}" 120 5; then + log_success "Adapter ${adapter_name} for ${resource_type} is running and healthy" else - log_error "Failed to install adapter ${adapter_name} for ${resource_type}" - return 1 + log_error "Adapter ${adapter_name} for ${resource_type} deployment failed health check" + log_info "Checking pod logs for troubleshooting:" + kubectl logs -n "${NAMESPACE}" -l "app.kubernetes.io/instance=${release_name}" --tail=50 2>/dev/null || true + return 1 fi + else + log_error "Failed to install adapter ${adapter_name} for ${resource_type}" + return 1 + fi } install_adapters() { - log_section "Deploying All Adapters" - - # Discover adapters - local adapters - if ! adapters=$(discover_adapters); then - log_warning "No adapters found to deploy" - return 0 + log_section "Deploying All Adapters" + + # Discover adapters + local adapters + if ! adapters=$(discover_adapters); then + log_warning "No adapters found to deploy" + return 0 + fi + + # Install each adapter + local failed=0 + while IFS= read -r adapter_dir; do + if ! install_adapter_instance "${adapter_dir}"; then + log_warning "Failed to install adapter: ${adapter_dir}" + ((failed++)) fi - - # Install each adapter - local failed=0 - while IFS= read -r adapter_dir; do - if ! install_adapter_instance "${adapter_dir}"; then - log_warning "Failed to install adapter: ${adapter_dir}" - ((failed++)) - fi done <<< "${adapters}" - if [[ ${failed} -gt 0 ]]; then - log_error "${failed} adapter(s) failed to install" - return 1 - else - log_success "All adapters deployed successfully" - fi + if [[ ${failed} -gt 0 ]]; then + log_error "${failed} adapter(s) failed to install" + return 1 + else + log_success "All adapters deployed successfully" + fi } # ============================================================================ @@ -242,71 +242,71 @@ install_adapters() { # ============================================================================ uninstall_adapter_instance() { - local dir_name="$1" - - log_section "Uninstalling Adapter: ${dir_name}" - - # Extract resource_type and adapter_name from format: resource_type|adapter_name - local resource_type="${dir_name%%|*}" - local adapter_name="${dir_name##*|}" - - # Validate the descriptor format and ensure both parts are non-empty - if [[ -z "${resource_type}" || -z "${adapter_name}" || "${dir_name}" != *"|"* ]]; then - log_error "Invalid adapter descriptor '${dir_name}'. Expected format: resource_type|adapter_name" - return 1 - fi - - log_info "Resource type: ${resource_type}" - log_info "Adapter name: ${adapter_name}" - - # Construct release name - local release_name="adapter-${resource_type}-${adapter_name}" - - # Check if release exists - if ! helm list -n "${NAMESPACE}" 2>/dev/null | grep -q "^${release_name}"; then - log_warning "Release '${release_name}' not found in namespace '${NAMESPACE}'" - return 0 - fi - - if [[ "${DRY_RUN}" == "true" ]]; then - log_info "[DRY-RUN] Would uninstall adapter (release: ${release_name})" - return 0 - fi - - log_info "Uninstalling adapter ${adapter_name} for ${resource_type}..." - log_info "Executing: helm uninstall ${release_name} -n ${NAMESPACE} --wait --timeout 5m" - - if helm uninstall "${release_name}" -n "${NAMESPACE}" --wait --timeout 5m; then - log_success "Adapter ${adapter_name} for ${resource_type} uninstalled successfully" - else - log_error "Failed to uninstall adapter ${adapter_name} for ${resource_type}" - return 1 - fi + local dir_name="$1" + + log_section "Uninstalling Adapter: ${dir_name}" + + # Extract resource_type and adapter_name from format: resource_type|adapter_name + local resource_type="${dir_name%%|*}" + local adapter_name="${dir_name##*|}" + + # Validate the descriptor format and ensure both parts are non-empty + if [[ -z "${resource_type}" || -z "${adapter_name}" || "${dir_name}" != *"|"* ]]; then + log_error "Invalid adapter descriptor '${dir_name}'. Expected format: resource_type|adapter_name" + return 1 + fi + + log_info "Resource type: ${resource_type}" + log_info "Adapter name: ${adapter_name}" + + # Construct release name + local release_name="adapter-${resource_type}-${adapter_name}" + + # Check if release exists + if ! helm list -n "${NAMESPACE}" 2>/dev/null | grep -q "^${release_name}"; then + log_warning "Release '${release_name}' not found in namespace '${NAMESPACE}'" + return 0 + fi + + if [[ "${DRY_RUN}" == "true" ]]; then + log_info "[DRY-RUN] Would uninstall adapter (release: ${release_name})" + return 0 + fi + + log_info "Uninstalling adapter ${adapter_name} for ${resource_type}..." + log_info "Executing: helm uninstall ${release_name} -n ${NAMESPACE} --wait --timeout 5m" + + if helm uninstall "${release_name}" -n "${NAMESPACE}" --wait --timeout 5m; then + log_success "Adapter ${adapter_name} for ${resource_type} uninstalled successfully" + else + log_error "Failed to uninstall adapter ${adapter_name} for ${resource_type}" + return 1 + fi } uninstall_adapters() { - log_section "Uninstalling All Adapters" - - # Discover adapters - local adapters - if ! adapters=$(discover_adapters); then - log_warning "No adapters found to uninstall" - return 0 + log_section "Uninstalling All Adapters" + + # Discover adapters + local adapters + if ! adapters=$(discover_adapters); then + log_warning "No adapters found to uninstall" + return 0 + fi + + # Uninstall each adapter + local failed=0 + while IFS= read -r adapter_dir; do + if ! uninstall_adapter_instance "${adapter_dir}"; then + log_warning "Failed to uninstall adapter: ${adapter_dir}" + ((failed++)) fi - - # Uninstall each adapter - local failed=0 - while IFS= read -r adapter_dir; do - if ! uninstall_adapter_instance "${adapter_dir}"; then - log_warning "Failed to uninstall adapter: ${adapter_dir}" - ((failed++)) - fi done <<< "${adapters}" - if [[ ${failed} -gt 0 ]]; then - log_error "${failed} adapter(s) failed to uninstall" - return 1 - else - log_success "All adapters uninstalled successfully" - fi + if [[ ${failed} -gt 0 ]]; then + log_error "${failed} adapter(s) failed to uninstall" + return 1 + else + log_success "All adapters uninstalled successfully" + fi } diff --git a/testdata/adapter-configs/cl-deployment/adapter-config.yaml b/testdata/adapter-configs/cl-deployment/adapter-config.yaml index 2f9302c..d20254b 100644 --- a/testdata/adapter-configs/cl-deployment/adapter-config.yaml +++ b/testdata/adapter-configs/cl-deployment/adapter-config.yaml @@ -1,28 +1,23 @@ -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterConfig -metadata: +adapter: name: cl-deployment - labels: - hyperfleet.io/adapter-type: cl-deployment - hyperfleet.io/component: adapter -spec: - adapter: - version: "0.1.0" + #version: "0.1.0" - # Log the full merged configuration after load (default: false) - debugConfig: false +# Log the full merged configuration after load (default: false) +debug_config: false +log: + level: debug - clients: - hyperfleetApi: - baseUrl: CHANGE_ME - version: v1 - timeout: 2s - retryAttempts: 3 - retryBackoff: exponential +clients: + hyperfleet_api: + base_url: CHANGE_ME + version: v1 + timeout: 2s + retry_attempts: 3 + retry_backoff: exponential - broker: - subscriptionId: CHANGE_ME - topic: CHANGE_ME + broker: + subscription_id: CHANGE_ME + topic: CHANGE_ME - kubernetes: - apiVersion: "v1" + kubernetes: + api_version: "v1" diff --git a/testdata/adapter-configs/cl-deployment/adapter-task-config.yaml b/testdata/adapter-configs/cl-deployment/adapter-task-config.yaml index 87a4af4..67ba124 100644 --- a/testdata/adapter-configs/cl-deployment/adapter-task-config.yaml +++ b/testdata/adapter-configs/cl-deployment/adapter-task-config.yaml @@ -1,136 +1,130 @@ # Simple valid HyperFleet Adapter Configuration for testing -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterTaskConfig -metadata: - name: cl-deployment - labels: - hyperfleet.io/adapter-type: cl-deployment - hyperfleet.io/component: adapter -spec: - # Parameters with all required variables - params: - - name: "clusterId" - source: "event.id" - type: "string" - required: true +# Parameters with all required variables +params: + - name: "clusterId" + source: "event.id" + type: "string" + required: true - # Preconditions with valid operators and CEL expressions - preconditions: - - name: "clusterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterName" - field: "name" - - name: "generationSpec" - field: "generation" - - name: "readyConditionStatus" - expression: | - status.conditions.filter(c, c.type == "Ready").size() > 0 - ? status.conditions.filter(c, c.type == "Ready")[0].status - : "False" - # Structured conditions with valid operators - conditions: - - field: "readyConditionStatus" - operator: "equals" - value: "False" +# Preconditions with valid operators and CEL expressions +preconditions: + - name: "clusterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterName" + field: "name" + - name: "generationSpec" + field: "generation" + - name: "readyConditionStatus" + expression: | + status.conditions.filter(c, c.type == "Ready").size() > 0 + ? status.conditions.filter(c, c.type == "Ready")[0].status + : "False" + # Structured conditions with valid operators + conditions: + - field: "readyConditionStatus" + operator: "equals" + value: "False" - - name: "clusterAdapterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}/statuses" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterJobStatus" - field: "{.items[?(@.adapter=='cl-job')].conditions[?(@.type=='Available')].status}" - conditions: - - field: "clusterJobStatus" - operator: "equals" - value: "True" + - name: "clusterAdapterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}/statuses" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterJobStatus" + field: "{.items[?(@.adapter=='cl-job')].conditions[?(@.type=='Available')].status}" + conditions: + - field: "clusterJobStatus" + operator: "equals" + value: "True" - # Resources with valid K8s manifests - resources: - - name: "testDeployment" - manifest: - ref: "/etc/adapter/deployment.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/resource-type: "deployment" - hyperfleet.io/cluster-id: "{{ .clusterId }}" +# Resources with valid K8s manifests +resources: + - name: "testDeployment" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/deployment.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/resource-type: "deployment" + hyperfleet.io/cluster-id: "{{ .clusterId }}" - # Post-processing with valid CEL expressions - post: - payloads: - - name: "clusterStatusPayload" - build: - adapter: "{{ .metadata.name }}" - conditions: - # Applied: Deployment successfully created - - type: "Applied" - status: - expression: | - has(resources.testDeployment) ? "True" : "False" - reason: - expression: | - has(resources.testDeployment) - ? "DeploymentApplied" - : "DeploymentPending" - message: - expression: | - has(resources.testDeployment) - ? "testDeployment manifest applied successfully" - : "testDeployment is pending to be applied" - # Available: Check deployment status conditions - - type: "Available" - status: - expression: | - has(resources.testDeployment) ? - ( resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].status : "Unknown") - : "Unknown" - reason: - expression: | - resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].reason - : resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Progressing" && c.status == "False") ? "DeploymentFailed" - : resources.?testDeployment.?status.hasValue() ? "DeploymentInProgress" : "DeploymentPending" - message: - expression: | - resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].message - : resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Progressing" && c.status == "False") ? "Deployment failed" - : resources.?testDeployment.?status.hasValue() ? "Deployment in progress" : "Deployment is pending" - # Health: Adapter execution status (runtime) - - type: "Health" - status: - expression: | - adapter.?executionStatus.orValue("") == "success" ? "True" : "False" - reason: - expression: | - adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" - message: - expression: | - adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" - # Event generation ID metadata field needs to use expression to avoid interpolation issues - observed_generation: - expression: "generationSpec" - observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" +# Post-processing with valid CEL expressions +post: + payloads: + - name: "clusterStatusPayload" + build: + adapter: "{{ .adapter.name }}" + conditions: + # Applied: Deployment successfully created + - type: "Applied" + status: + expression: | + has(resources.testDeployment) ? "True" : "False" + reason: + expression: | + has(resources.testDeployment) + ? "DeploymentApplied" + : "DeploymentPending" + message: + expression: | + has(resources.testDeployment) + ? "testDeployment manifest applied successfully" + : "testDeployment is pending to be applied" + # Available: Check deployment status conditions + - type: "Available" + status: + expression: | + has(resources.testDeployment) ? + ( resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].status : "Unknown") + : "Unknown" + reason: + expression: | + resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].reason + : resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Progressing" && c.status == "False") ? "DeploymentFailed" + : resources.?testDeployment.?status.hasValue() ? "DeploymentInProgress" : "DeploymentPending" + message: + expression: | + resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testDeployment.status.conditions.filter(c, c.type == "Available")[0].message + : resources.?testDeployment.?status.?conditions.orValue([]).exists(c, c.type == "Progressing" && c.status == "False") ? "Deployment failed" + : resources.?testDeployment.?status.hasValue() ? "Deployment in progress" : "Deployment is pending" + # Health: Adapter execution status (runtime) + - type: "Health" + status: + expression: | + adapter.?executionStatus.orValue("") == "success" ? "True" : "False" + reason: + expression: | + adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" + message: + expression: | + adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" + # Event generation ID metadata field needs to use expression to avoid interpolation issues + observed_generation: + expression: "generationSpec" + observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" - postActions: - - name: "reportClusterStatus" - apiCall: - method: "POST" - url: "/clusters/{{ .clusterId }}/statuses" - headers: - - name: "Content-Type" - value: "application/json" - body: "{{ .clusterStatusPayload }}" + post_actions: + - name: "reportClusterStatus" + api_call: + method: "POST" + url: "/clusters/{{ .clusterId }}/statuses" + headers: + - name: "Content-Type" + value: "application/json" + body: "{{ .clusterStatusPayload }}" diff --git a/testdata/adapter-configs/cl-deployment/values.yaml b/testdata/adapter-configs/cl-deployment/values.yaml index 2eeb94f..271d567 100644 --- a/testdata/adapter-configs/cl-deployment/values.yaml +++ b/testdata/adapter-configs/cl-deployment/values.yaml @@ -14,10 +14,10 @@ adapterTaskConfig: broker: create: true googlepubsub: - projectId: CHANGE_ME - subscriptionId: CHANGE_ME + project_id: CHANGE_ME + subscription_id: CHANGE_ME topic: CHANGE_ME - deadLetterTopic: CHANGE_ME + dead_letter_topic: CHANGE_ME image: registry: CHANGE_ME diff --git a/testdata/adapter-configs/cl-job/adapter-config.yaml b/testdata/adapter-configs/cl-job/adapter-config.yaml index 6e55673..88098e0 100644 --- a/testdata/adapter-configs/cl-job/adapter-config.yaml +++ b/testdata/adapter-configs/cl-job/adapter-config.yaml @@ -1,28 +1,23 @@ -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterConfig -metadata: +adapter: name: cl-job - labels: - hyperfleet.io/adapter-type: cl-job - hyperfleet.io/component: adapter -spec: - adapter: - version: "0.1.0" + #version: "0.1.0" - # Log the full merged configuration after load (default: false) - debugConfig: false +# Log the full merged configuration after load (default: false) +debug_config: false +log: + level: debug - clients: - hyperfleetApi: - baseUrl: CHANGE_ME - version: v1 - timeout: 2s - retryAttempts: 3 - retryBackoff: exponential +clients: + hyperfleet_api: + base_url: CHANGE_ME + version: v1 + timeout: 2s + retry_attempts: 3 + retry_backoff: exponential - broker: - subscriptionId: CHANGE_ME - topic: CHANGE_ME + broker: + subscription_id: CHANGE_ME + topic: CHANGE_ME - kubernetes: - apiVersion: "v1" + kubernetes: + api_version: "v1" diff --git a/testdata/adapter-configs/cl-job/adapter-task-config.yaml b/testdata/adapter-configs/cl-job/adapter-task-config.yaml index a61db32..d13b724 100644 --- a/testdata/adapter-configs/cl-job/adapter-task-config.yaml +++ b/testdata/adapter-configs/cl-job/adapter-task-config.yaml @@ -1,182 +1,182 @@ # Simple valid HyperFleet Adapter Configuration for testing -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterTaskConfig -metadata: - name: cl-job - labels: - hyperfleet.io/adapter-type: cl-job - hyperfleet.io/component: adapter -spec: - # Parameters with all required variables - params: - - name: "clusterId" - source: "event.id" - type: "string" - required: true - - name: "jobSleepDuration" - source: "env.JOB_SLEEP_DURATION" - type: "string" - default: "15" +# Parameters with all required variables +params: + - name: "clusterId" + source: "event.id" + type: "string" + required: true + - name: "jobSleepDuration" + source: "env.JOB_SLEEP_DURATION" + type: "string" + default: "15" - # Preconditions with valid operators and CEL expressions - preconditions: - - name: "clusterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterName" - field: "name" - - name: "generationSpec" - field: "generation" - - name: "simulateResult" # possible values: success (default), failure, hang, crash, invalid-json, missing-status - field: "simulateResult" - default: "success" - - name: "readyConditionStatus" - expression: | - status.conditions.filter(c, c.type == "Ready").size() > 0 - ? status.conditions.filter(c, c.type == "Ready")[0].status - : "False" - # Structured conditions with valid operators - conditions: - - field: "readyConditionStatus" - operator: "equals" - value: "False" +# Preconditions with valid operators and CEL expressions +preconditions: + - name: "clusterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterName" + field: "name" + - name: "generationSpec" + field: "generation" + - name: "simulateResult" # possible values: success (default), failure, hang, crash, invalid-json, missing-status + field: "simulateResult" + default: "success" + - name: "readyConditionStatus" + expression: | + status.conditions.filter(c, c.type == "Ready").size() > 0 + ? status.conditions.filter(c, c.type == "Ready")[0].status + : "False" + # Structured conditions with valid operators + conditions: + - field: "readyConditionStatus" + operator: "equals" + value: "False" - - name: "validationCheck" - # Valid CEL expression - expression: | - readyConditionStatus == "False" + - name: "validationCheck" + # Valid CEL expression + expression: | + readyConditionStatus == "False" - - name: "clusterAdapterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}/statuses" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterNamespaceStatus" - field: "{.items[?(@.adapter=='cl-namespace')].data.namespace.status}" - conditions: - - field: "clusterNamespaceStatus" - operator: "equals" - value: "Active" + - name: "clusterAdapterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}/statuses" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterNamespaceStatus" + field: "{.items[?(@.adapter=='cl-namespace')].data.namespace.status}" + conditions: + - field: "clusterNamespaceStatus" + operator: "equals" + value: "Active" + +# Resources with valid K8s manifests +resources: + # the following configuration is for a job that will be created in the cluster + # in the namespace created above + # it will require a service account to be created in that namespace as well as a role and rolebinding + - name: "jobServiceAccount" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/job-serviceaccount.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/resource-type: "service-account" + hyperfleet.io/cluster-id: "{{ .clusterId }}" - # Resources with valid K8s manifests - resources: - # the following configuration is for a job that will be created in the cluster - # in the namespace created above - # it will require a service account to be created in that namespace as well as a role and rolebinding - - name: "jobServiceAccount" - manifest: - ref: "/etc/adapter/job-serviceaccount.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/resource-type: "service-account" - hyperfleet.io/cluster-id: "{{ .clusterId }}" - - - name: "jobRole" - manifest: - ref: "/etc/adapter/job-role.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/resource-type: "role" + - name: "jobRole" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/job-role.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/resource-type: "role" - - name: "jobRolebinding" - manifest: - ref: "/etc/adapter/job-rolebinding.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/resource-type: "role-binding" + - name: "jobRolebinding" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/job-rolebinding.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/resource-type: "role-binding" - - name: "testJob" - manifest: - ref: "/etc/adapter/job.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/resource-type: "job" + - name: "testJob" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/job.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/resource-type: "job" - # Post-processing with valid CEL expressions - # This example contains multiple resources, we will only report on the conditions of the jobNamespace not to overcomplicate the example - post: - payloads: - - name: "clusterStatusPayload" - build: - adapter: "{{ .metadata.name }}" - conditions: - # Applied: Job successfully created - - type: "Applied" - status: - expression: | - has(resources.testJob) ? "True" : "False" - reason: - expression: | - has(resources.testJob) - ? "JobApplied" - : "JobPending" - message: - expression: | - has(resources.testJob) - ? "testJob manifest applied successfully" - : "testJob is pending to be applied" - # Available: Check job status conditions - - type: "Available" - status: - expression: | - has(resources.testJob) ? - ( resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].status : "Unknown") - : "Unknown" - reason: - expression: | - resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].reason - : resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "testJobFailed" - : resources.?testJob.?status.hasValue() ? "testJobInProgress" : "testJobPending" - message: - expression: | - resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") - ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].message - : resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "testJob failed" - : resources.?testJob.?status.hasValue() ? "testJob in progress" : "testJob is pending" - # Health: Adapter execution status (runtime) - - type: "Health" - status: - expression: | - adapter.?executionStatus.orValue("") == "success" ? "True" : "False" - reason: - expression: | - adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" - message: - expression: | - adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" - # Event generation ID metadata field needs to use expression to avoid interpolation issues - observed_generation: - expression: "generationSpec" - observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" +# Post-processing with valid CEL expressions +# This example contains multiple resources, we will only report on the conditions of the jobNamespace not to overcomplicate the example +post: + payloads: + - name: "clusterStatusPayload" + build: + adapter: "{{ .adapter.name }}" + conditions: + # Applied: Job successfully created + - type: "Applied" + status: + expression: | + has(resources.testJob) ? "True" : "False" + reason: + expression: | + has(resources.testJob) + ? "JobApplied" + : "JobPending" + message: + expression: | + has(resources.testJob) + ? "testJob manifest applied successfully" + : "testJob is pending to be applied" + # Available: Check job status conditions + - type: "Available" + status: + expression: | + has(resources.testJob) ? + ( resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].status : "Unknown") + : "Unknown" + reason: + expression: | + resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].reason + : resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "testJobFailed" + : resources.?testJob.?status.hasValue() ? "testJobInProgress" : "testJobPending" + message: + expression: | + resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Available") + ? resources.testJob.status.conditions.filter(c, c.type == "Available")[0].message + : resources.?testJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "testJob failed" + : resources.?testJob.?status.hasValue() ? "testJob in progress" : "testJob is pending" + # Health: Adapter execution status (runtime) + - type: "Health" + status: + expression: | + adapter.?executionStatus.orValue("") == "success" ? "True" : "False" + reason: + expression: | + adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" + message: + expression: | + adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" + # Event generation ID metadata field needs to use expression to avoid interpolation issues + observed_generation: + expression: "generationSpec" + observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" - postActions: - - name: "reportClusterStatus" - apiCall: - method: "POST" - url: "/clusters/{{ .clusterId }}/statuses" - headers: - - name: "Content-Type" - value: "application/json" - body: "{{ .clusterStatusPayload }}" + post_actions: + - name: "reportClusterStatus" + api_call: + method: "POST" + url: "/clusters/{{ .clusterId }}/statuses" + headers: + - name: "Content-Type" + value: "application/json" + body: "{{ .clusterStatusPayload }}" diff --git a/testdata/adapter-configs/cl-job/values.yaml b/testdata/adapter-configs/cl-job/values.yaml index 21fa14f..2ba63f2 100644 --- a/testdata/adapter-configs/cl-job/values.yaml +++ b/testdata/adapter-configs/cl-job/values.yaml @@ -17,10 +17,10 @@ adapterTaskConfig: broker: create: true googlepubsub: - projectId: CHANGE_ME - subscriptionId: CHANGE_ME + project_id: CHANGE_ME + subscription_id: CHANGE_ME topic: CHANGE_ME - deadLetterTopic: CHANGE_ME + dead_letter_topic: CHANGE_ME image: registry: CHANGE_ME diff --git a/testdata/adapter-configs/cl-maestro/adapter-config.yaml b/testdata/adapter-configs/cl-maestro/adapter-config.yaml index 8c6c004..dbe3a4e 100644 --- a/testdata/adapter-configs/cl-maestro/adapter-config.yaml +++ b/testdata/adapter-configs/cl-maestro/adapter-config.yaml @@ -1,69 +1,63 @@ # Example HyperFleet Adapter deployment configuration -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterConfig -metadata: +adapter: name: cl-maestro - labels: - hyperfleet.io/adapter-type: cl-maestro - hyperfleet.io/component: adapter -spec: - adapter: - version: "0.1.0" + #version: "0.1.0" - # Log the full merged configuration after load (default: false) - debugConfig: true +# Log the full merged configuration after load (default: false) +debug_config: true +log: + level: debug - clients: - hyperfleetApi: - baseUrl: CHANGE_ME - version: v1 - timeout: 2s - retryAttempts: 3 - retryBackoff: exponential +clients: + hyperfleet_api: + base_url: CHANGE_ME + version: v1 + timeout: 2s + retry_attempts: 3 + retry_backoff: exponential - broker: - # These values are overridden at deploy time via env vars from Helm values - subscriptionId: CHANGE_ME - topic: CHANGE_ME + broker: + # These values are overridden at deploy time via env vars from Helm values + subscription_id: CHANGE_ME + topic: CHANGE_ME - maestro: - grpcServerAddress: "maestro-grpc.maestro.svc.cluster.local:8090" - - # HTTPS server address for REST API operations (optional) - # Environment variable: HYPERFLEET_MAESTRO_HTTP_SERVER_ADDRESS - httpServerAddress: "http://maestro.maestro.svc.cluster.local:8000" - - # Source identifier for CloudEvents routing (must be unique across adapters) - # Environment variable: HYPERFLEET_MAESTRO_SOURCE_ID - sourceId: "cl-maestro" - - # Client identifier (defaults to sourceId if not specified) - # Environment variable: HYPERFLEET_MAESTRO_CLIENT_ID - clientId: "cl-maestro-client" - insecure: true - - # Authentication configuration - #auth: - # type: "tls" # TLS certificate-based mTLS - # - # tlsConfig: - # # gRPC TLS configuration - # # Certificate paths (mounted from Kubernetes secrets) - # # Environment variable: HYPERFLEET_MAESTRO_CA_FILE - # caFile: "/etc/maestro/certs/grpc/ca.crt" - # - # # Environment variable: HYPERFLEET_MAESTRO_CERT_FILE - # certFile: "/etc/maestro/certs/grpc/client.crt" - # - # # Environment variable: HYPERFLEET_MAESTRO_KEY_FILE - # keyFile: "/etc/maestro/certs/grpc/client.key" - # - # # Server name for TLS verification - # # Environment variable: HYPERFLEET_MAESTRO_SERVER_NAME - # serverName: "maestro-grpc.maestro.svc.cluster.local" - # - # # HTTP API TLS configuration (may use different CA than gRPC) - # # If not set, falls back to caFile for backwards compatibility - # # Environment variable: HYPERFLEET_MAESTRO_HTTP_CA_FILE - # httpCaFile: "/etc/maestro/certs/https/ca.crt" + maestro: + grpc_server_address: "maestro-grpc.maestro.svc.cluster.local:8090" + # HTTPS server address for REST API operations (optional) + # Environment variable: HYPERFLEET_MAESTRO_HTTP_SERVER_ADDRESS + http_server_address: "http://maestro.maestro.svc.cluster.local:8000" + + # Source identifier for CloudEvents routing (must be unique across adapters) + # Environment variable: HYPERFLEET_MAESTRO_SOURCE_ID + source_id: "cl-maestro" + + # Client identifier (defaults to source_id if not specified) + # Environment variable: HYPERFLEET_MAESTRO_CLIENT_ID + client_id: "cl-maestro-client" + insecure: true + + # Authentication configuration + #auth: + # type: "tls" # TLS certificate-based mTLS + # + # tls_config: + # # gRPC TLS configuration + # # Certificate paths (mounted from Kubernetes secrets) + # # Environment variable: HYPERFLEET_MAESTRO_CA_FILE + # ca_file: "/etc/maestro/certs/grpc/ca.crt" + # + # # Environment variable: HYPERFLEET_MAESTRO_CERT_FILE + # cert_file: "/etc/maestro/certs/grpc/client.crt" + # + # # Environment variable: HYPERFLEET_MAESTRO_KEY_FILE + # key_file: "/etc/maestro/certs/grpc/client.key" + # + # # Server name for TLS verification + # # Environment variable: HYPERFLEET_MAESTRO_SERVER_NAME + # server_name: "maestro-grpc.maestro.svc.cluster.local" + # + # # HTTP API TLS configuration (may use different CA than gRPC) + # # If not set, falls back to ca_file for backwards compatibility + # # Environment variable: HYPERFLEET_MAESTRO_HTTP_CA_FILE + # http_ca_file: "/etc/maestro/certs/https/ca.crt" diff --git a/testdata/adapter-configs/cl-maestro/adapter-task-config.yaml b/testdata/adapter-configs/cl-maestro/adapter-task-config.yaml index 2740c08..317a627 100644 --- a/testdata/adapter-configs/cl-maestro/adapter-task-config.yaml +++ b/testdata/adapter-configs/cl-maestro/adapter-task-config.yaml @@ -1,347 +1,341 @@ # Example HyperFleet Adapter task configuration -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterTaskConfig -metadata: - name: cl-maestro - labels: - hyperfleet.io/adapter-type: cl-maestro - hyperfleet.io/component: adapter -spec: - # Parameters with all required variables - params: - - name: "clusterId" - source: "event.id" - type: "string" - required: true +# Parameters with all required variables +params: - - name: "generation" - source: "event.generation" - type: "int" - required: true + - name: "clusterId" + source: "event.id" + type: "string" + required: true - - name: "namespace" - source: "env.NAMESPACE" - type: "string" + - name: "generation" + source: "event.generation" + type: "int" + required: true + - name: "namespace" + source: "env.NAMESPACE" + type: "string" - # Preconditions with valid operators and CEL expressions - preconditions: - - name: "clusterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterName" - field: "name" - - name: "generation" - field: "generation" - - name: "timestamp" - field: "created_time" - - name: "readyConditionStatus" - expression: | - status.conditions.filter(c, c.type == "Ready").size() > 0 - ? status.conditions.filter(c, c.type == "Ready")[0].status - : "False" - - name: "placementClusterName" - expression: "\"cluster1\"" # TBC coming from placement adapter - description: "Unique identifier for the target maestro" +# Preconditions with valid operators and CEL expressions +preconditions: + - name: "clusterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterName" + field: "name" + - name: "generation" + field: "generation" + - name: "timestamp" + field: "created_time" + - name: "readyConditionStatus" + expression: | + status.conditions.filter(c, c.type == "Ready").size() > 0 + ? status.conditions.filter(c, c.type == "Ready")[0].status + : "False" + - name: "placementClusterName" + expression: "\"cluster1\"" # TBC coming from placement adapter + description: "Unique identifier for the target maestro" - # Structured conditions with valid operators - conditions: - - field: "readyConditionStatus" - operator: "equals" - value: "False" - - name: "validationCheck" - # Valid CEL expression - expression: | - readyConditionStatus == "False" + # Structured conditions with valid operators + conditions: + - field: "readyConditionStatus" + operator: "equals" + value: "False" - # Resources with valid K8s manifests - resources: - - name: "resource0" - transport: - client: "maestro" - maestro: - targetCluster: "{{ .placementClusterName }}" - - # ManifestWork is a kind of manifest that can be used to create resources on the cluster. - # It is a collection of resources that are created together. - manifest: - apiVersion: work.open-cluster-management.io/v1 - kind: ManifestWork - metadata: - # ManifestWork name - must be unique within consumer namespace - name: "{{ .clusterId }}-{{ .metadata.name }}" + - name: "validationCheck" + # Valid CEL expression + expression: | + readyConditionStatus == "False" - # Labels for identification, filtering, and management - labels: - # HyperFleet tracking labels - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/adapter: "{{ .metadata.name }}" - hyperfleet.io/component: "infrastructure" - hyperfleet.io/generation: "{{ .generation }}" - hyperfleet.io/resource-group: "cluster-setup" +# Resources with valid K8s manifests +resources: + - name: "resource0" + transport: + client: "maestro" + maestro: + target_cluster: "{{ .placementClusterName }}" - # Maestro-specific labels - maestro.io/source-id: "{{ .metadata.name }}" - maestro.io/resource-type: "manifestwork" - maestro.io/priority: "normal" + # ManifestWork is a kind of manifest that can be used to create resources on the cluster. + # It is a collection of resources that are created together. + manifest: + apiVersion: work.open-cluster-management.io/v1 + kind: ManifestWork + metadata: + # ManifestWork name - must be unique within consumer namespace + name: "{{ .clusterId }}-{{ .adapter.name }}" - # Standard Kubernetes application labels - app.kubernetes.io/name: "aro-hcp-cluster" - app.kubernetes.io/instance: "{{ .clusterId }}" - app.kubernetes.io/version: "v1.0.0" - app.kubernetes.io/component: "infrastructure" - app.kubernetes.io/part-of: "hyperfleet" - app.kubernetes.io/managed-by: "cl-maestro" - app.kubernetes.io/created-by: "{{ .metadata.name }}" + # Labels for identification, filtering, and management + labels: + # HyperFleet tracking labels + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/adapter: "{{ .adapter.name }}" + hyperfleet.io/component: "infrastructure" + hyperfleet.io/generation: "{{ .generation }}" + hyperfleet.io/resource-group: "cluster-setup" - # Annotations for metadata and operational information - annotations: - # Tracking and lifecycle - hyperfleet.io/created-by: "cl-maestro-framework" - hyperfleet.io/managed-by: "{{ .metadata.name }}" - hyperfleet.io/generation: "{{ .generation }}" - hyperfleet.io/cluster-name: "{{ .clusterId }}" - hyperfleet.io/deployment-time: "{{ .timestamp }}" + # Maestro-specific labels + maestro.io/source-id: "{{ .adapter.name }}" + maestro.io/resource-type: "manifestwork" + maestro.io/priority: "normal" - # Maestro-specific annotations - maestro.io/applied-time: "{{ .timestamp }}" - maestro.io/source-adapter: "{{ .metadata.name }}" + # Standard Kubernetes application labels + app.kubernetes.io/name: "aro-hcp-cluster" + app.kubernetes.io/instance: "{{ .clusterId }}" + app.kubernetes.io/version: "v1.0.0" + app.kubernetes.io/component: "infrastructure" + app.kubernetes.io/part-of: "hyperfleet" + app.kubernetes.io/managed-by: "cl-maestro" + app.kubernetes.io/created-by: "{{ .adapter.name }}" - # Documentation - description: "Complete cluster setup including namespace, configuration, and RBAC" + # Annotations for metadata and operational information + annotations: + # Tracking and lifecycle + hyperfleet.io/created-by: "cl-maestro-framework" + hyperfleet.io/managed-by: "{{ .adapter.name }}" + hyperfleet.io/generation: "{{ .generation }}" + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/cluster-name: "{{ .clusterName }}" + hyperfleet.io/deployment-time: "{{ .timestamp }}" - # ManifestWork specification - spec: - # ============================================================================ - # Workload - Contains the Kubernetes manifests to deploy - # ============================================================================ - workload: - # Kubernetes manifests array - injected by framework from business logic config - manifests: - - apiVersion: v1 - kind: Namespace - metadata: - name: "{{ .clusterId | lower }}-{{ .metadata.name }}-namespace" - labels: - app.kubernetes.io/component: adapter-task-config - app.kubernetes.io/instance: "{{ .metadata.name }}" - app.kubernetes.io/name: cl-maestro - app.kubernetes.io/transport: maestro - annotations: - hyperfleet.io/generation: "{{ .generation }}" - - apiVersion: v1 - kind: ConfigMap - data: - cluster_id: "{{ .clusterId }}" - cluster_name: "{{ .clusterName }}" - metadata: - name: "{{ .clusterId | lower }}-{{ .metadata.name }}-configmap" - namespace: "{{ .clusterId | lower }}-{{ .metadata.name }}-namespace" - labels: - app.kubernetes.io/component: adapter-task-config - app.kubernetes.io/instance: "{{ .metadata.name }}" - app.kubernetes.io/name: cl-maestro - app.kubernetes.io/version: 1.0.0 - app.kubernetes.io/transport: maestro - annotations: - hyperfleet.io/generation: "{{ .generation }}" + # Maestro-specific annotations + maestro.io/applied-time: "{{ .timestamp }}" + maestro.io/source-adapter: "{{ .adapter.name }}" - # ============================================================================ - # Delete Options - How resources should be removed - # ============================================================================ - deleteOption: - # Propagation policy for resource deletion - # - "Foreground": Wait for dependent resources to be deleted first - # - "Background": Delete immediately, let cluster handle dependents - # - "Orphan": Leave resources on cluster when ManifestWork is deleted - propagationPolicy: "Foreground" + # Documentation + description: "Complete cluster setup including namespace, configuration, and RBAC" - # Grace period for graceful deletion (seconds) - gracePeriodSeconds: 30 + # ManifestWork specification + spec: + # ============================================================================ + # Workload - Contains the Kubernetes manifests to deploy + # ============================================================================ + workload: + # Kubernetes manifests array - injected by framework from business logic config + manifests: + - apiVersion: v1 + kind: Namespace + metadata: + name: "{{ .clusterId | lower }}-{{ .adapter.name }}-namespace" + labels: + app.kubernetes.io/component: adapter-task-config + app.kubernetes.io/instance: "{{ .adapter.name }}" + app.kubernetes.io/name: cl-maestro + app.kubernetes.io/transport: maestro + annotations: + hyperfleet.io/generation: "{{ .generation }}" + - apiVersion: v1 + kind: ConfigMap + data: + cluster_id: "{{ .clusterId }}" + cluster_name: "{{ .clusterName }}" + metadata: + name: "{{ .clusterId | lower }}-{{ .adapter.name }}-configmap" + namespace: "{{ .clusterId | lower }}-{{ .adapter.name }}-namespace" + labels: + app.kubernetes.io/component: adapter-task-config + app.kubernetes.io/instance: "{{ .adapter.name }}" + app.kubernetes.io/name: cl-maestro + app.kubernetes.io/version: 1.0.0 + app.kubernetes.io/transport: maestro + annotations: + hyperfleet.io/generation: "{{ .generation }}" - # ============================================================================ - # Manifest Configurations - Per-resource settings for update and feedback - # ============================================================================ - manifestConfigs: - - resourceIdentifier: - group: "" # Core API group (empty for v1 resources) - resource: "namespaces" # Resource type - name: "{{ .clusterId | lower }}-{{ .metadata.name }}-namespace" # Specific resource name - updateStrategy: - type: "ServerSideApply" # Use server-side apply for namespaces - feedbackRules: - - type: "JSONPaths" # Use JSON path expressions for status feedback - jsonPaths: - - name: "phase" - path: ".status.phase" - # ======================================================================== - # Configuration for Namespace resources - # ======================================================================== - - resourceIdentifier: - group: "" # Core API group (empty for v1 resources) - resource: "configmaps" # Resource type - name: "{{ .clusterId | lower }}-{{ .metadata.name }}-configmap" # Specific resource name - namespace: "{{ .clusterId | lower }}-{{ .metadata.name }}-namespace" - updateStrategy: - type: "ServerSideApply" # Use server-side apply for namespaces - serverSideApply: - fieldManager: "cl-maestro" # Field manager name for conflict resolution - force: false # Don't force conflicts (fail on conflicts) - feedbackRules: - - type: "JSONPaths" # Use JSON path expressions for status feedback - jsonPaths: - - name: "data" - path: ".data" - - name: "resourceVersion" - path: ".metadata.resourceVersion" - # Discover the ResourceBundle (ManifestWork) by name from Maestro - discovery: - byName: "{{ .clusterId }}-{{ .metadata.name }}" + # ============================================================================ + # Delete Options - How resources should be removed + # ============================================================================ + deleteOption: + # Propagation policy for resource deletion + # - "Foreground": Wait for dependent resources to be deleted first + # - "Background": Delete immediately, let cluster handle dependents + # - "Orphan": Leave resources on cluster when ManifestWork is deleted + propagationPolicy: "Foreground" - # Discover nested resources deployed by the ManifestWork - nestedDiscoveries: - - name: "namespace0" - discovery: - byName: "{{ .clusterId | lower }}-{{ .metadata.name }}-namespace" - - name: "configmap0" - discovery: - byName: "{{ .clusterId | lower }}-{{ .metadata.name }}-configmap" + # Grace period for graceful deletion (seconds) + gracePeriodSeconds: 30 - post: - payloads: - - name: "statusPayload" - build: - adapter: "{{ .metadata.name }}" - conditions: - # Applied: Check if ManifestWork exists and has type="Applied", status="True" - - type: "Applied" - status: - expression: | - has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].status : "False" - reason: - expression: | - has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].reason : "ManifestWorkNotDiscovered" - message: - expression: | - has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].message : "ManifestWork not discovered from Maestro or no Applied condition" + # ============================================================================ + # Manifest Configurations - Per-resource settings for update and feedback + # ============================================================================ + manifestConfigs: + - resourceIdentifier: + group: "" # Core API group (empty for v1 resources) + resource: "namespaces" # Resource type + name: "{{ .clusterId | lower }}-{{ .adapter.name }}-namespace" # Specific resource name + updateStrategy: + type: "ServerSideApply" # Use server-side apply for namespaces + feedbackRules: + - type: "JSONPaths" # Use JSON path expressions for status feedback + jsonPaths: + - name: "phase" + path: ".status.phase" + # ======================================================================== + # Configuration for Namespace resources + # ======================================================================== + - resourceIdentifier: + group: "" # Core API group (empty for v1 resources) + resource: "configmaps" # Resource type + name: "{{ .clusterId | lower }}-{{ .adapter.name }}-configmap" # Specific resource name + namespace: "{{ .clusterId | lower }}-{{ .adapter.name }}-namespace" + updateStrategy: + type: "ServerSideApply" # Use server-side apply for namespaces + serverSideApply: + fieldManager: "cl-maestro" # Field manager name for conflict resolution + force: false # Don't force conflicts (fail on conflicts) + feedbackRules: + - type: "JSONPaths" # Use JSON path expressions for status feedback + jsonPaths: + - name: "data" + path: ".data" + - name: "resourceVersion" + path: ".metadata.resourceVersion" + # Discover the ResourceBundle (ManifestWork) by name from Maestro + discovery: + by_name: "{{ .clusterId }}-{{ .adapter.name }}" - # Available: Check if nested discovered manifests are available on the spoke cluster - # Each nested discovery is enriched with top-level "conditions" from status.resourceStatus.manifests[] - - type: "Available" - status: - expression: | - has(resources.namespace0) && has(resources.namespace0.conditions) - && resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") - && has(resources.configmap0) && has(resources.configmap0.conditions) - && resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") - ? "True" - : "False" - reason: - expression: | - !(has(resources.namespace0) && has(resources.namespace0.conditions)) - ? "NamespaceNotDiscovered" - : !resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") - ? "NamespaceNotAvailable" - : !(has(resources.configmap0) && has(resources.configmap0.conditions)) - ? "ConfigMapNotDiscovered" - : !resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") - ? "ConfigMapNotAvailable" - : "AllResourcesAvailable" - message: - expression: | - !(has(resources.namespace0) && has(resources.namespace0.conditions)) - ? "Namespace not discovered from ManifestWork" - : !resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") - ? "Namespace not yet available on spoke cluster" - : !(has(resources.configmap0) && has(resources.configmap0.conditions)) - ? "ConfigMap not discovered from ManifestWork" - : !resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") - ? "ConfigMap not yet available on spoke cluster" - : "All manifests (namespace, configmap) are available on spoke cluster" + # Discover nested resources deployed by the ManifestWork + nested_discoveries: + - name: "namespace0" + discovery: + by_name: "{{ .clusterId | lower }}-{{ .adapter.name }}-namespace" + - name: "configmap0" + discovery: + by_name: "{{ .clusterId | lower }}-{{ .adapter.name }}-configmap" - # Health: Adapter execution status — surfaces errors from any phase - - type: "Health" - status: - expression: | - adapter.?executionStatus.orValue("") == "success" - && !adapter.?resourcesSkipped.orValue(false) - ? "True" - : "False" - reason: - expression: | - adapter.?executionStatus.orValue("") != "success" - ? "ExecutionFailed:" + adapter.?executionError.?phase.orValue("unknown") - : adapter.?resourcesSkipped.orValue(false) - ? "ResourcesSkipped" - : "Healthy" - message: - expression: | - adapter.?executionStatus.orValue("") != "success" - ? "Adapter failed at phase [" - + adapter.?executionError.?phase.orValue("unknown") - + "] step [" - + adapter.?executionError.?step.orValue("unknown") - + "]: " - + adapter.?executionError.?message.orValue(adapter.?errorMessage.orValue("no details")) - : adapter.?resourcesSkipped.orValue(false) - ? "Resources skipped: " + adapter.?skipReason.orValue("unknown reason") - : "Adapter execution completed successfully" +post: + payloads: + - name: "statusPayload" + build: + adapter: "{{ .adapter.name }}" + conditions: + # Applied: Check if ManifestWork exists and has type="Applied", status="True" + - type: "Applied" + status: + expression: | + has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].status : "False" + reason: + expression: | + has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].reason : "ManifestWorkNotDiscovered" + message: + expression: | + has(resources.resource0) && has(resources.resource0.status) && has(resources.resource0.status.conditions) && resources.resource0.status.conditions.filter(c, has(c.type) && c.type == "Applied").size() > 0 ? resources.resource0.status.conditions.filter(c, c.type == "Applied")[0].message : "ManifestWork not discovered from Maestro or no Applied condition" - observed_generation: - expression: "generation" - observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" + # Available: Check if nested discovered manifests are available on the spoke cluster + # Each nested discovery is enriched with top-level "conditions" from status.resourceStatus.manifests[] + - type: "Available" + status: + expression: | + has(resources.namespace0) && has(resources.namespace0.conditions) + && resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") + && has(resources.configmap0) && has(resources.configmap0.conditions) + && resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") + ? "True" + : "False" + reason: + expression: | + !(has(resources.namespace0) && has(resources.namespace0.conditions)) + ? "NamespaceNotDiscovered" + : !resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") + ? "NamespaceNotAvailable" + : !(has(resources.configmap0) && has(resources.configmap0.conditions)) + ? "ConfigMapNotDiscovered" + : !resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") + ? "ConfigMapNotAvailable" + : "AllResourcesAvailable" + message: + expression: | + !(has(resources.namespace0) && has(resources.namespace0.conditions)) + ? "Namespace not discovered from ManifestWork" + : !resources.namespace0.conditions.exists(c, has(c.type) && c.type == "Available" && has(c.status) && c.status == "True") + ? "Namespace not yet available on spoke cluster" + : !(has(resources.configmap0) && has(resources.configmap0.conditions)) + ? "ConfigMap not discovered from ManifestWork" + : !resources.configmap0.conditions.exists(c, c.type == "Available" && has(c.status) && c.status == "True") + ? "ConfigMap not yet available on spoke cluster" + : "All manifests (namespace, configmap) are available on spoke cluster" - # Extract data from discovered ManifestWork from Maestro - data: - manifestwork: - name: - expression: | - has(resources.resource0) && has(resources.resource0.metadata) - ? resources.resource0.metadata.name - : "" - consumer: - expression: | - has(resources.resource0) && has(resources.resource0.metadata) - ? resources.resource0.metadata.namespace - : placementClusterName - configmap: - name: - expression: | - has(resources.configmap0) && has(resources.configmap0.metadata) - ? resources.configmap0.metadata.name - : "" - clusterId: - expression: | - has(resources.configmap0) && has(resources.configmap0.data) && has(resources.configmap0.data.cluster_id) - ? resources.configmap0.data.cluster_id - : clusterId - namespace: - name: - expression: | - has(resources.namespace0) && has(resources.namespace0.metadata) - ? resources.namespace0.metadata.name - : "" - phase: - expression: | - has(resources.namespace0) && has(resources.namespace0.statusFeedback) && has(resources.namespace0.statusFeedback.values) - && resources.namespace0.statusFeedback.values.exists(v, has(v.name) && v.name == "phase" && has(v.fieldValue)) - ? resources.namespace0.statusFeedback.values.filter(v, v.name == "phase")[0].fieldValue.string - : "Unknown" + # Health: Adapter execution status — surfaces errors from any phase + - type: "Health" + status: + expression: | + adapter.?executionStatus.orValue("") == "success" + && !adapter.?resourcesSkipped.orValue(false) + ? "True" + : "False" + reason: + expression: | + adapter.?executionStatus.orValue("") != "success" + ? "ExecutionFailed:" + adapter.?executionError.?phase.orValue("unknown") + : adapter.?resourcesSkipped.orValue(false) + ? "ResourcesSkipped" + : "Healthy" + message: + expression: | + adapter.?executionStatus.orValue("") != "success" + ? "Adapter failed at phase [" + + adapter.?executionError.?phase.orValue("unknown") + + "] step [" + + adapter.?executionError.?step.orValue("unknown") + + "]: " + + adapter.?executionError.?message.orValue(adapter.?errorMessage.orValue("no details")) + : adapter.?resourcesSkipped.orValue(false) + ? "Resources skipped: " + adapter.?skipReason.orValue("unknown reason") + : "Adapter execution completed successfully" - postActions: - - name: "reportClusterStatus" - apiCall: - method: "POST" - url: "/clusters/{{ .clusterId }}/statuses" - headers: - - name: "Content-Type" - value: "application/json" - body: "{{ .statusPayload }}" + observed_generation: + expression: "generation" + observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" + + # Extract data from discovered ManifestWork from Maestro + data: + manifestwork: + name: + expression: | + has(resources.resource0) && has(resources.resource0.metadata) + ? resources.resource0.metadata.name + : "" + consumer: + expression: | + has(resources.resource0) && has(resources.resource0.metadata) + ? resources.resource0.metadata.namespace + : placementClusterName + configmap: + name: + expression: | + has(resources.configmap0) && has(resources.configmap0.metadata) + ? resources.configmap0.metadata.name + : "" + clusterId: + expression: | + has(resources.configmap0) && has(resources.configmap0.data) && has(resources.configmap0.data.cluster_id) + ? resources.configmap0.data.cluster_id + : clusterId + namespace: + name: + expression: | + has(resources.namespace0) && has(resources.namespace0.metadata) + ? resources.namespace0.metadata.name + : "" + phase: + expression: | + has(resources.namespace0) && has(resources.namespace0.statusFeedback) && has(resources.namespace0.statusFeedback.values) + && resources.namespace0.statusFeedback.values.exists(v, has(v.name) && v.name == "phase" && has(v.fieldValue)) + ? resources.namespace0.statusFeedback.values.filter(v, v.name == "phase")[0].fieldValue.string + : "Unknown" + + post_actions: + - name: "reportClusterStatus" + api_call: + method: "POST" + url: "/clusters/{{ .clusterId }}/statuses" + headers: + - name: "Content-Type" + value: "application/json" + body: "{{ .statusPayload }}" diff --git a/testdata/adapter-configs/cl-maestro/values.yaml b/testdata/adapter-configs/cl-maestro/values.yaml index 5a5397b..d4e7493 100644 --- a/testdata/adapter-configs/cl-maestro/values.yaml +++ b/testdata/adapter-configs/cl-maestro/values.yaml @@ -13,10 +13,10 @@ adapterTaskConfig: broker: create: true googlepubsub: - projectId: CHANGE_ME - subscriptionId: CHANGE_ME + project_id: CHANGE_ME + subscription_id: CHANGE_ME topic: CHANGE_ME - deadLetterTopic: CHANGE_ME + dead_letter_topic: CHANGE_ME image: registry: CHANGE_ME diff --git a/testdata/adapter-configs/cl-namespace/adapter-config.yaml b/testdata/adapter-configs/cl-namespace/adapter-config.yaml index c7d3d2d..6578f95 100644 --- a/testdata/adapter-configs/cl-namespace/adapter-config.yaml +++ b/testdata/adapter-configs/cl-namespace/adapter-config.yaml @@ -1,28 +1,23 @@ -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterConfig -metadata: +adapter: name: cl-namespace - labels: - hyperfleet.io/adapter-type: cl-namespace - hyperfleet.io/component: adapter -spec: - adapter: - version: "0.1.0" + #version: "0.1.0" - # Log the full merged configuration after load (default: false) - debugConfig: false +# Log the full merged configuration after load (default: false) +debug_config: false +log: + level: debug - clients: - hyperfleetApi: - baseUrl: CHANGE_ME - version: v1 - timeout: 2s - retryAttempts: 3 - retryBackoff: exponential +clients: + hyperfleet_api: + base_url: CHANGE_ME + version: v1 + timeout: 2s + retry_attempts: 3 + retry_backoff: exponential - broker: - subscriptionId: CHANGE_ME - topic: CHANGE_ME + broker: + subscription_id: CHANGE_ME + topic: CHANGE_ME - kubernetes: - apiVersion: "v1" + kubernetes: + api_version: "v1" diff --git a/testdata/adapter-configs/cl-namespace/adapter-task-config.yaml b/testdata/adapter-configs/cl-namespace/adapter-task-config.yaml index 072dbb6..2953b4e 100644 --- a/testdata/adapter-configs/cl-namespace/adapter-task-config.yaml +++ b/testdata/adapter-configs/cl-namespace/adapter-task-config.yaml @@ -1,148 +1,142 @@ # Simple valid HyperFleet Adapter Configuration for testing -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterTaskConfig -metadata: - name: cl-namespace - labels: - hyperfleet.io/adapter-type: cl-namespace - hyperfleet.io/component: adapter -spec: - # Parameters with all required variables - params: - - name: "clusterId" - source: "event.id" - type: "string" - required: true - - name: "testRunId" - source: "env.TEST_RUN_ID" - type: "string" - required: false - default: "TEST_RUN_ID" - - name: "ci" - source: "env.CI" - type: "string" - required: false - default: "false" +# Parameters with all required variables +params: + - name: "clusterId" + source: "event.id" + type: "string" + required: true + - name: "testRunId" + source: "env.TEST_RUN_ID" + type: "string" + required: false + default: "TEST_RUN_ID" + - name: "ci" + source: "env.CI" + type: "string" + required: false + default: "false" - # Preconditions with valid operators and CEL expressions - preconditions: - - name: "clusterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterName" - field: "name" - - name: "generationSpec" - field: "generation" - - name: "readyConditionStatus" - expression: | - status.conditions.filter(c, c.type == "Ready").size() > 0 - ? status.conditions.filter(c, c.type == "Ready")[0].status - : "False" - # Structured conditions with valid operators - conditions: - - field: "readyConditionStatus" - operator: "equals" - value: "False" +# Preconditions with valid operators and CEL expressions +preconditions: + - name: "clusterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterName" + field: "name" + - name: "generationSpec" + field: "generation" + - name: "readyConditionStatus" + expression: | + status.conditions.filter(c, c.type == "Ready").size() > 0 + ? status.conditions.filter(c, c.type == "Ready")[0].status + : "False" + # Structured conditions with valid operators + conditions: + - field: "readyConditionStatus" + operator: "equals" + value: "False" - - name: "validationCheck" - # Valid CEL expression - expression: | - readyConditionStatus == "False" + - name: "validationCheck" + # Valid CEL expression + expression: | + readyConditionStatus == "False" - # Resources with valid K8s manifests - resources: - - name: "clusterNamespace" - manifest: - apiVersion: v1 - kind: Namespace - metadata: - name: "{{ .clusterId }}" - labels: - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/cluster-name: "{{ .clusterName }}" - e2e.hyperfleet.io/test-run-id: "{{ .testRunId }}" - e2e.hyperfleet.io/ci: "{{ .ci }}" - e2e.hyperfleet.io/managed-by: "test-framework" - annotations: - hyperfleet.io/generation: "{{ .generationSpec }}" - discovery: - namespace: "*" # Cluster-scoped resource (Namespace) - bySelectors: - labelSelector: - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/cluster-name: "{{ .clusterName }}" +# Resources with valid K8s manifests +resources: + - name: "clusterNamespace" + transport: + client: "kubernetes" + manifest: + apiVersion: v1 + kind: Namespace + metadata: + name: "{{ .clusterId }}" + labels: + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/cluster-name: "{{ .clusterName }}" + e2e.hyperfleet.io/test-run-id: "{{ .testRunId }}" + e2e.hyperfleet.io/ci: "{{ .ci }}" + e2e.hyperfleet.io/managed-by: "test-framework" + annotations: + hyperfleet.io/generation: "{{ .generationSpec }}" + discovery: + namespace: "*" # Cluster-scoped resource (Namespace) + by_selectors: + label_selector: + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/cluster-name: "{{ .clusterName }}" - # Post-processing with valid CEL expressions - # This example contains multiple resources, we will only report on the conditions of the jobNamespace not to overcomplicate the example - post: - payloads: - - name: "clusterStatusPayload" - build: - adapter: "{{ .metadata.name }}" - conditions: - # Applied: Job successfully created - - type: "Applied" - status: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "True" : "False" - reason: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" - ? "NamespaceCreated" - : "NamespacePending" - message: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" - ? "Namespace created successfully" - : "Namespace creation in progress" - # Available: Check job status conditions - - type: "Available" - status: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "True" : "False" - reason: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "NamespaceReady" : "NamespaceNotReady" - message: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "Namespace is active and ready" : "Namespace is not active and ready" - # Health: Adapter execution status (runtime) - - type: "Health" - status: - expression: | - adapter.?executionStatus.orValue("") == "success" ? "True" : "False" - reason: - expression: | - adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" - message: - expression: | - adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" - # Event generation ID metadata field needs to use expression to avoid interpolation issues - observed_generation: - expression: "generationSpec" - observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" +# Post-processing with valid CEL expressions +# This example contains multiple resources, we will only report on the conditions of the jobNamespace not to overcomplicate the example +post: + payloads: + - name: "clusterStatusPayload" + build: + adapter: "{{ .adapter.name }}" + conditions: + # Applied: Job successfully created + - type: "Applied" + status: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "True" : "False" + reason: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" + ? "NamespaceCreated" + : "NamespacePending" + message: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" + ? "Namespace created successfully" + : "Namespace creation in progress" + # Available: Check job status conditions + - type: "Available" + status: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "True" : "False" + reason: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "NamespaceReady" : "NamespaceNotReady" + message: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") == "Active" ? "Namespace is active and ready" : "Namespace is not active and ready" + # Health: Adapter execution status (runtime) + - type: "Health" + status: + expression: | + adapter.?executionStatus.orValue("") == "success" ? "True" : "False" + reason: + expression: | + adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" + message: + expression: | + adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" + # Event generation ID metadata field needs to use expression to avoid interpolation issues + observed_generation: + expression: "generationSpec" + observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" - data: - namespace: - name: - expression: | - resources.?clusterNamespace.?metadata.?name.orValue("") - status: - expression: | - resources.?clusterNamespace.?status.?phase.orValue("") + data: + namespace: + name: + expression: | + resources.?clusterNamespace.?metadata.?name.orValue("") + status: + expression: | + resources.?clusterNamespace.?status.?phase.orValue("") - postActions: - - name: "reportClusterStatus" - apiCall: - method: "POST" - url: "/clusters/{{ .clusterId }}/statuses" - headers: - - name: "Content-Type" - value: "application/json" - body: "{{ .clusterStatusPayload }}" + post_actions: + - name: "reportClusterStatus" + api_call: + method: "POST" + url: "/clusters/{{ .clusterId }}/statuses" + headers: + - name: "Content-Type" + value: "application/json" + body: "{{ .clusterStatusPayload }}" diff --git a/testdata/adapter-configs/cl-namespace/values.yaml b/testdata/adapter-configs/cl-namespace/values.yaml index b485edc..470c9a2 100644 --- a/testdata/adapter-configs/cl-namespace/values.yaml +++ b/testdata/adapter-configs/cl-namespace/values.yaml @@ -13,10 +13,10 @@ adapterTaskConfig: broker: create: true googlepubsub: - projectId: CHANGE_ME - subscriptionId: CHANGE_ME + project_id: CHANGE_ME + subscription_id: CHANGE_ME topic: CHANGE_ME - deadLetterTopic: CHANGE_ME + dead_letter_topic: CHANGE_ME image: registry: CHANGE_ME diff --git a/testdata/adapter-configs/np-configmap/adapter-config.yaml b/testdata/adapter-configs/np-configmap/adapter-config.yaml index a743a7c..dd0c593 100644 --- a/testdata/adapter-configs/np-configmap/adapter-config.yaml +++ b/testdata/adapter-configs/np-configmap/adapter-config.yaml @@ -1,28 +1,23 @@ -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterConfig -metadata: +adapter: name: np-configmap - labels: - hyperfleet.io/adapter-type: np-configmap - hyperfleet.io/component: adapter -spec: - adapter: - version: "0.1.0" + #version: "0.1.0" - # Log the full merged configuration after load (default: false) - debugConfig: false +# Log the full merged configuration after load (default: false) +debug_config: false +log: + level: debug - clients: - hyperfleetApi: - baseUrl: CHANGE_ME - version: v1 - timeout: 2s - retryAttempts: 3 - retryBackoff: exponential +clients: + hyperfleet_api: + base_url: CHANGE_ME + version: v1 + timeout: 2s + retry_attempts: 3 + retry_backoff: exponential - broker: - subscriptionId: CHANGE_ME - topic: CHANGE_ME + broker: + subscription_id: CHANGE_ME + topic: CHANGE_ME - kubernetes: - apiVersion: "v1" + kubernetes: + api_version: "v1" diff --git a/testdata/adapter-configs/np-configmap/adapter-task-config.yaml b/testdata/adapter-configs/np-configmap/adapter-task-config.yaml index 38c7308..bc284fe 100644 --- a/testdata/adapter-configs/np-configmap/adapter-task-config.yaml +++ b/testdata/adapter-configs/np-configmap/adapter-task-config.yaml @@ -1,136 +1,130 @@ # Simple valid HyperFleet Adapter Configuration for testing -apiVersion: hyperfleet.redhat.com/v1alpha1 -kind: AdapterTaskConfig -metadata: - name: np-configmap - labels: - hyperfleet.io/adapter-type: np-configmap - hyperfleet.io/component: adapter -spec: - # Parameters with all required variables - params: - - name: "clusterId" - source: "event.owner_references.id" - type: "string" - required: true - - name: "nodepoolId" - source: "event.id" - type: "string" - required: true +# Parameters with all required variables +params: + - name: "clusterId" + source: "event.owner_references.id" + type: "string" + required: true + - name: "nodepoolId" + source: "event.id" + type: "string" + required: true - # Preconditions with valid operators and CEL expressions - preconditions: - - name: "nodepoolStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}/nodepools/{{ .nodepoolId }}" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "nodepoolName" - field: "name" - - name: "generationSpec" - field: "generation" - - name: "readyConditionStatus" - expression: | - status.conditions.filter(c, c.type == "Ready").size() > 0 - ? status.conditions.filter(c, c.type == "Ready")[0].status - : "False" - # Structured conditions with valid operators - conditions: - - field: "readyConditionStatus" - operator: "equals" - value: "False" +# Preconditions with valid operators and CEL expressions +preconditions: + - name: "nodepoolStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}/nodepools/{{ .nodepoolId }}" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "nodepoolName" + field: "name" + - name: "generationSpec" + field: "generation" + - name: "readyConditionStatus" + expression: | + status.conditions.filter(c, c.type == "Ready").size() > 0 + ? status.conditions.filter(c, c.type == "Ready")[0].status + : "False" + # Structured conditions with valid operators + conditions: + - field: "readyConditionStatus" + operator: "equals" + value: "False" - - name: "clusterAdapterStatus" - apiCall: - method: "GET" - url: "/clusters/{{ .clusterId }}/statuses" - timeout: 10s - retryAttempts: 3 - retryBackoff: "exponential" - capture: - - name: "clusterNamespaceStatus" - field: "{.items[?(@.adapter=='cl-namespace')].data.namespace.status}" - conditions: - - field: "clusterNamespaceStatus" - operator: "equals" - value: "Active" + - name: "clusterAdapterStatus" + api_call: + method: "GET" + url: "/clusters/{{ .clusterId }}/statuses" + timeout: 10s + retry_attempts: 3 + retry_backoff: "exponential" + capture: + - name: "clusterNamespaceStatus" + field: "{.items[?(@.adapter=='cl-namespace')].data.namespace.status}" + conditions: + - field: "clusterNamespaceStatus" + operator: "equals" + value: "Active" - # Resources with valid K8s manifests - resources: - - name: "nodepoolConfigMap" - manifest: - ref: "/etc/adapter/configmap.yaml" - discovery: - namespace: "{{ .clusterId }}" - bySelectors: - labelSelector: - hyperfleet.io/resource-type: "configmap" - hyperfleet.io/cluster-id: "{{ .clusterId }}" - hyperfleet.io/nodepool-id: "{{ .nodepoolId }}" +# Resources with valid K8s manifests +resources: + - name: "nodepoolConfigMap" + transport: + client: "kubernetes" + manifest: + ref: "/etc/adapter/configmap.yaml" + discovery: + namespace: "{{ .clusterId }}" + by_selectors: + label_selector: + hyperfleet.io/resource-type: "configmap" + hyperfleet.io/cluster-id: "{{ .clusterId }}" + hyperfleet.io/nodepool-id: "{{ .nodepoolId }}" - # Post-processing with valid CEL expressions - post: - payloads: - - name: "nodepoolStatusPayload" - build: - adapter: "{{ .metadata.name }}" - conditions: - # Applied: ConfigMap successfully created - - type: "Applied" - status: - expression: | - has(resources.nodepoolConfigMap) ? "True" : "False" - reason: - expression: | - has(resources.nodepoolConfigMap) - ? "ConfigMapApplied" - : "ConfigMapPending" - message: - expression: | - has(resources.nodepoolConfigMap) - ? "nodepoolConfigMap manifest applied successfully" - : "nodepoolConfigMap is pending to be applied" - # Available: Check configmap exists - - type: "Available" - status: - expression: | - has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) ? "True" : "False" - reason: - expression: | - has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) - ? "ConfigMapReady" - : "ConfigMapNotReady" - message: - expression: | - has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) - ? "ConfigMap is available and contains data" - : "ConfigMap is not yet available" - # Health: Adapter execution status (runtime) - - type: "Health" - status: - expression: | - adapter.?executionStatus.orValue("") == "success" ? "True" : "False" - reason: - expression: | - adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" - message: - expression: | - adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" - # Event generation ID metadata field needs to use expression to avoid interpolation issues - observed_generation: - expression: "generationSpec" - observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" +# Post-processing with valid CEL expressions +post: + payloads: + - name: "nodepoolStatusPayload" + build: + adapter: "{{ .adapter.name }}" + conditions: + # Applied: ConfigMap successfully created + - type: "Applied" + status: + expression: | + has(resources.nodepoolConfigMap) ? "True" : "False" + reason: + expression: | + has(resources.nodepoolConfigMap) + ? "ConfigMapApplied" + : "ConfigMapPending" + message: + expression: | + has(resources.nodepoolConfigMap) + ? "nodepoolConfigMap manifest applied successfully" + : "nodepoolConfigMap is pending to be applied" + # Available: Check configmap exists + - type: "Available" + status: + expression: | + has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) ? "True" : "False" + reason: + expression: | + has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) + ? "ConfigMapReady" + : "ConfigMapNotReady" + message: + expression: | + has(resources.nodepoolConfigMap) && has(resources.nodepoolConfigMap.data) + ? "ConfigMap is available and contains data" + : "ConfigMap is not yet available" + # Health: Adapter execution status (runtime) + - type: "Health" + status: + expression: | + adapter.?executionStatus.orValue("") == "success" ? "True" : "False" + reason: + expression: | + adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy" + message: + expression: | + adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations in progress or completed successfully" + # Event generation ID metadata field needs to use expression to avoid interpolation issues + observed_generation: + expression: "generationSpec" + observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}" - postActions: - - name: "reportNodepoolStatus" - apiCall: - method: "POST" - url: "/clusters/{{ .clusterId }}/nodepools/{{ .nodepoolId }}/statuses" - headers: - - name: "Content-Type" - value: "application/json" - body: "{{ .nodepoolStatusPayload }}" + post_actions: + - name: "reportNodepoolStatus" + api_call: + method: "POST" + url: "/clusters/{{ .clusterId }}/nodepools/{{ .nodepoolId }}/statuses" + headers: + - name: "Content-Type" + value: "application/json" + body: "{{ .nodepoolStatusPayload }}" diff --git a/testdata/adapter-configs/np-configmap/values.yaml b/testdata/adapter-configs/np-configmap/values.yaml index 2f8e45b..79bcb23 100644 --- a/testdata/adapter-configs/np-configmap/values.yaml +++ b/testdata/adapter-configs/np-configmap/values.yaml @@ -14,10 +14,10 @@ adapterTaskConfig: broker: create: true googlepubsub: - projectId: CHANGE_ME - subscriptionId: CHANGE_ME + project_id: CHANGE_ME + subscription_id: CHANGE_ME topic: CHANGE_ME - deadLetterTopic: CHANGE_ME + dead_letter_topic: CHANGE_ME image: registry: CHANGE_ME