Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 317 additions & 0 deletions charts/configs/validation-adapter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
# HyperFleet GCP Validation Adapter Configuration
#
# This adapter creates a validation job for GCP clusters to verify
# cluster readiness and configuration.
apiVersion: hyperfleet.redhat.com/v1alpha1
kind: AdapterConfig
metadata:
name: validation-adapter
namespace: hyperfleet-system
labels:
hyperfleet.io/adapter-type: validation
hyperfleet.io/component: adapter
hyperfleet.io/provider: gcp
spec:
adapter:
version: "0.1.0"
# ============================================================================
# HyperFleet API Configuration
# ============================================================================
hyperfleetApi:
timeout: 2s
retryAttempts: 3
# ============================================================================
# Kubernetes Configuration
# ============================================================================
kubernetes:
apiVersion: "batch/v1"
# ============================================================================
# Parameters
# ============================================================================
params:
- name: "hyperfleetApiBaseUrl"
source: "env.HYPERFLEET_API_BASE_URL"
type: "string"
description: "Base URL for the HyperFleet API"
required: true
- name: "hyperfleetApiVersion"
source: "env.HYPERFLEET_API_VERSION"
type: "string"
default: "v1"
description: "API version to use"
- name: "clusterId"
source: "event.id"
type: "string"
description: "Unique identifier for the target cluster"
required: true
- name: "statusReporterImage"
source: "env.STATUS_REPORTER_IMAGE"
type: "string"
default: "quay.io/rh-ee-dawang/status-reporter:dev-04e8d0a"
description: "Container image for the status reporter"
# GCP Validator configuration
- name: "gcpValidatorImage"
source: "env.GCP_VALIDATOR_IMAGE"
type: "string"
default: "quay.io/rh-ee-dawang/gcp-validator:latest"
description: "GCP validator container image"
- name: "disabledValidators"
source: "env.DISABLED_VALIDATORS"
type: "string"
default: "quota-check"
description: "Comma-separated list of validators to disable"
- name: "requiredApis"
source: "env.REQUIRED_APIS"
type: "string"
default: "compute.googleapis.com,iam.googleapis.com,cloudresourcemanager.googleapis.com"
description: "Comma-separated list of required GCP APIs to validate"
- name: "resultPath"
source: "env.RESULTS_PATH"
type: "string"
default: "/results/adapter-result.json"
description: "Adapter shared result path with status reporter"
- name: "maxWaitTimeSeconds"
source: "env.MAX_WAIT_TIME_SECONDS"
type: "string"
default: "300"
description: "Maximum time to wait for validation completion"
- name: "gcpValidatorServiceAccount"
source: "env.GCP_VALIDATOR_SERVICE_ACCOUNT"
type: "string"
default: "gcp-validator-job-sa"
description: "Kubernetes ServiceAccount name for the validator job"
- name: "managedByResourceName"
source: "env.MANAGED_BY_RESOURCE_NAME"
type: "string"
default: "validation-adapter"
description: "The value for hyperfleet.io/managed-by"
- name: "createdByResourceName"
source: "env.CREATED_BY_RESOURCE_NAME"
type: "string"
default: "hyperfleet-adapter"
description: "The value for hyperfleet.io/created-by:"
# ============================================================================
# Preconditions
# ============================================================================
# Preconditions run before resource operations to validate state
preconditions:
- name: "clusterStatus"
apiCall:
method: "GET"
url: "{{ .hyperfleetApiBaseUrl }}/api/hyperfleet/{{ .hyperfleetApiVersion }}/clusters/{{ .clusterId }}"
timeout: 10s
retryAttempts: 3
retryBackoff: "exponential"
capture:
- name: "clusterName"
field: "name"
- name: "clusterPhase"
field: "status.phase"
- name: "generationId"
field: "generation"
# Customer GCP project ID
- name: "projectId"
field: "spec.platform.gcp.projectID"
conditions:
- field: "clusterPhase"
operator: "in"
values: ["NotReady", "Ready"]
# Ensure Customer GCP project ID is configured
- field: "projectId"
operator: "exists"

- name: "clusterAdapterStatus"
apiCall:
method: "GET"
url: "{{ .hyperfleetApiBaseUrl }}/api/hyperfleet/{{ .hyperfleetApiVersion }}/clusters/{{ .clusterId }}/statuses"
timeout: 10s
retryAttempts: 3
retryBackoff: "exponential"
capture:
- name: "clusterNamespaceStatus"
field: "{.items[?(@.adapter=='landing-zone-adapter')].data.namespace.status}"
conditions:
- field: "clusterNamespaceStatus"
operator: "equals"
values: "Active"
# ============================================================================
# Resources
# ============================================================================
resources:
# ==========================================================================
# Resource: ServiceAccount for GCP Validation Job
# ==========================================================================
- name: "gcpValidationServiceAccount"
manifest:
# ServiceAccount for the validator job
apiVersion: v1
kind: ServiceAccount
metadata:
name: "{{ .gcpValidatorServiceAccount }}"
namespace: "{{ .clusterId | lower }}"
labels:
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
hyperfleet.io/resource-type: "service-account"
annotations:
hyperfleet.io/created-by: "{{ .createdByResourceName }}"
hyperfleet.io/generation: "{{ .generationId }}"
discovery:
bySelectors:
labelSelector:
hyperfleet.io/resource-type: "service-account"
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
# ==========================================================================
# Resource: Role with necessary permissions for status reporter
# ==========================================================================
- name: "gcpValidationRole"
manifest:
# Role with necessary permissions for status reporter
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: status-reporter
namespace: "{{ .clusterId | lower }}"
labels:
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
hyperfleet.io/resource-type: "role"
annotations:
hyperfleet.io/created-by: "{{ .createdByResourceName }}"
hyperfleet.io/generation: "{{ .generationId }}"
rules:
# Permission to get and update job status
- apiGroups: [ "batch" ]
resources: [ "jobs" ]
verbs: [ "get" ]
- apiGroups: [ "batch" ]
resources: [ "jobs/status" ]
verbs: [ "get", "update", "patch" ]
# Permission to get pod status
- apiGroups: [ "" ]
resources: [ "pods" ]
verbs: [ "get", "list" ]
discovery:
bySelectors:
labelSelector:
hyperfleet.io/resource-type: "role"
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
# ==========================================================================
# Rolebinding to grant permissions to the service account
# ==========================================================================
- name: "gcpValidationRoleBinding"
manifest:
# RoleBinding to grant permissions to the service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: status-reporter
namespace: "{{ .clusterId | lower }}"
labels:
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
hyperfleet.io/resource-type: "role-binding"
annotations:
hyperfleet.io/created-by: "{{ .createdByResourceName }}"
hyperfleet.io/generation: "{{ .generationId }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: status-reporter
subjects:
- kind: ServiceAccount
name: "{{ .gcpValidatorServiceAccount }}"
namespace: "{{ .clusterId | lower }}"
discovery:
bySelectors:
labelSelector:
hyperfleet.io/resource-type: "role-binding"
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
# ==========================================================================
# Resource: GCP Validation Job
# ==========================================================================
- name: "gcpValidationJob"
manifest:
ref: "./validation-job-adapter-task.yaml"
discovery:
bySelectors:
labelSelector:
hyperfleet.io/resource-type: "validation-job"
hyperfleet.io/cluster-id: "{{ .clusterId }}"
hyperfleet.io/managed-by: "{{ .managedByResourceName }}"
# ============================================================================
# Post-Processing
# ============================================================================
post:
payloads:
# Build status payload inline
- name: "clusterStatusPayload"
build:
adapter: "{{ .metadata.name }}"
conditions:
# Applied: Job successfully created
- type: "Applied"
status:
expression: |
has(resources.gcpValidationJob) ? "True" : "False"
reason:
expression: |
has(resources.gcpValidationJob)
? "JobApplied"
: "JobPending"
message:
expression: |
has(resources.gcpValidationJob)
? "Validation job applied successfully"
: "Validation job is pending to applied"
# Available: Check job status conditions
- type: "Available"
status:
expression: |
resources.?gcpValidationJob.?status.?conditions.orValue([]).exists(c, c.type == "Available")
? resources.gcpValidationJob.status.conditions.filter(c, c.type == "Available")[0].status : "False"
Comment on lines +273 to +277
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a third value Unknown if there is no resources.gcpValidationJob.status yet ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to openshift-hyperfleet/architecture#79, it requires several changes from different CLM components. I'll update this logic in another PR.

reason:
expression: |
resources.?gcpValidationJob.?status.?conditions.orValue([]).exists(c, c.type == "Available")
? resources.gcpValidationJob.status.conditions.filter(c, c.type == "Available")[0].reason
: resources.?gcpValidationJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "ValidationFailed"
: resources.?gcpValidationJob.?status.hasValue() ? "ValidationInProgress" : "ValidationPending"
message:
expression: |
resources.?gcpValidationJob.?status.?conditions.orValue([]).exists(c, c.type == "Available")
? resources.gcpValidationJob.status.conditions.filter(c, c.type == "Available")[0].message
: resources.?gcpValidationJob.?status.?conditions.orValue([]).exists(c, c.type == "Failed") ? "Validation failed"
: resources.?gcpValidationJob.?status.hasValue() ? "Validation in progress" : "Validation is pending"
# Health: Adapter execution status (runtime)
- type: "Health"
status:
expression: |
adapter.?executionStatus.orValue("") == "success" ? "True" : (adapter.?executionStatus.orValue("") == "failed" ? "False" : "Unknown")
reason:
expression: |
adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy"
message:
expression: |
adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "All adapter operations completed successfully"
# Event generation ID metadata field needs to use expression to avoid interpolation issues
observed_generation:
expression: "generationId"
observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}"
# ============================================================================
# Post Actions
# ============================================================================
postActions:
- name: "reportClusterStatus"
apiCall:
method: "POST"
url: "{{ .hyperfleetApiBaseUrl }}/api/hyperfleet/{{ .hyperfleetApiVersion }}/clusters/{{ .clusterId }}/statuses"
body: "{{ .clusterStatusPayload }}"
timeout: 30s
retryAttempts: 3
retryBackoff: "exponential"
headers:
- name: "Content-Type"
value: "application/json"
2 changes: 1 addition & 1 deletion charts/configs/validation-dummy-adapter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ spec:
default: "gcp-validator-job-sa"
description: "Maximum time to wait for validation completion"
- name: "managedByResourceName"
source: "env.MANAGED_By_RESOURCE_NAME"
source: "env.MANAGED_BY_RESOURCE_NAME"
type: "string"
default: "dummy-validation-adapter"
description: "The value for hyperfleet.io/managed-by"
Expand Down
Loading