Skip to content

Commit 0e32be7

Browse files
committed
Add adapter-hypershift for remote HostedCluster creation
Add a new adapter helm package that creates HostedCluster resources on a remote HyperShift management cluster via a mounted kubeconfig secret. Uses NodePort for Ignition/OAuth services and parameterizes OCI region, compartment, release image, CPO image, and worker node IP via env vars.
1 parent d8ed508 commit 0e32be7

5 files changed

Lines changed: 310 additions & 0 deletions

File tree

helm/adapter-hypershift/Chart.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v2
2+
name: adapter-hypershift
3+
description: HyperShift adapter - creates HostedCluster resources on a remote management cluster
4+
type: application
5+
version: 0.1.0
6+
appVersion: "0.0.0-dev"
7+
8+
dependencies:
9+
- name: hyperfleet-adapter
10+
version: "2.0.0"
11+
repository: "git+https://github.com/openshift-hyperfleet/hyperfleet-adapter@charts?ref=main"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# HyperShift adapter deployment configuration
2+
# Creates HostedCluster resources on a remote HyperShift management cluster
3+
adapter:
4+
name: adapter-hypershift
5+
version: "0.2.0"
6+
7+
debug_config: true
8+
log:
9+
level: debug
10+
11+
clients:
12+
hyperfleet_api:
13+
base_url: http://hyperfleet-api:8000
14+
version: v1
15+
timeout: 10s
16+
retry_attempts: 3
17+
retry_backoff: exponential
18+
19+
broker:
20+
subscription_id: "adapter-hypershift"
21+
topic: "hyperfleet-clusters"
22+
23+
kubernetes:
24+
api_version: "v1"
25+
# Use the mounted kubeconfig to target the remote HyperShift management cluster
26+
kube_config_path: /etc/hypershift/kubeconfig
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# HyperShift adapter task configuration
2+
# Creates a HostedCluster + required secrets on the remote management cluster
3+
params:
4+
5+
- name: "clusterId"
6+
source: "event.id"
7+
type: "string"
8+
required: true
9+
10+
- name: "generation"
11+
source: "event.generation"
12+
type: "int"
13+
required: true
14+
15+
- name: "namespace"
16+
source: "env.CLUSTERS_NAMESPACE"
17+
type: "string"
18+
19+
- name: "ociRegion"
20+
source: "env.OCI_REGION"
21+
type: "string"
22+
23+
- name: "ociCompartmentId"
24+
source: "env.OCI_COMPARTMENT_ID"
25+
type: "string"
26+
27+
- name: "releaseImage"
28+
source: "env.OPENSHIFT_RELEASE_IMAGE"
29+
type: "string"
30+
31+
- name: "baseDomain"
32+
source: "env.BASE_DOMAIN"
33+
type: "string"
34+
35+
- name: "cpoImage"
36+
source: "env.CPO_IMAGE"
37+
type: "string"
38+
39+
- name: "workerNodeIP"
40+
source: "env.WORKER_NODE_IP"
41+
type: "string"
42+
43+
# Preconditions: check cluster details from API
44+
preconditions:
45+
- name: "clusterStatus"
46+
api_call:
47+
method: "GET"
48+
url: "/clusters/{{ .clusterId }}"
49+
timeout: 10s
50+
retry_attempts: 3
51+
retry_backoff: "exponential"
52+
capture:
53+
- name: "clusterName"
54+
field: "name"
55+
- name: "generation"
56+
field: "generation"
57+
- name: "clusterNotReady"
58+
expression: |
59+
status.conditions.filter(c, c.type == "Ready").size() > 0
60+
? status.conditions.filter(c, c.type == "Ready")[0].status != "True"
61+
: true
62+
63+
- name: "validationCheck"
64+
expression: |
65+
clusterNotReady
66+
67+
# Resources: Namespace + HostedCluster on the remote management cluster
68+
resources:
69+
70+
# Ensure the clusters namespace exists on the management cluster
71+
- name: "clustersNamespace"
72+
transport:
73+
client: "kubernetes"
74+
manifest:
75+
apiVersion: v1
76+
kind: Namespace
77+
metadata:
78+
name: "{{ .namespace }}"
79+
discovery:
80+
by_name: "{{ .namespace }}"
81+
82+
# Create the HostedCluster resource
83+
- name: "hostedCluster"
84+
transport:
85+
client: "kubernetes"
86+
manifest:
87+
apiVersion: hypershift.openshift.io/v1beta1
88+
kind: HostedCluster
89+
metadata:
90+
name: "{{ .clusterName }}"
91+
namespace: "{{ .namespace }}"
92+
annotations:
93+
hypershift.openshift.io/pod-security-admission-label-override: "privileged"
94+
hypershift.openshift.io/control-plane-operator-image: "{{ .cpoImage }}"
95+
hypershift.openshift.io/disable-monitoring-services: "true"
96+
labels:
97+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
98+
hyperfleet.io/cluster-name: "{{ .clusterName }}"
99+
spec:
100+
platform:
101+
type: OCI
102+
oci:
103+
identityRef:
104+
name: oci-credentials
105+
region: "{{ .ociRegion }}"
106+
compartmentId: "{{ .ociCompartmentId }}"
107+
controllerAvailabilityPolicy: SingleReplica
108+
pullSecret:
109+
name: pull-secret
110+
sshKey:
111+
name: ssh-key
112+
networking:
113+
clusterNetwork:
114+
- cidr: 10.132.0.0/14
115+
serviceNetwork:
116+
- cidr: 172.31.0.0/16
117+
networkType: OVNKubernetes
118+
services:
119+
- service: Ignition
120+
servicePublishingStrategy:
121+
type: NodePort
122+
nodePort:
123+
address: "{{ .workerNodeIP }}"
124+
- service: OAuthServer
125+
servicePublishingStrategy:
126+
type: NodePort
127+
nodePort:
128+
address: "{{ .workerNodeIP }}"
129+
- service: APIServer
130+
servicePublishingStrategy:
131+
type: LoadBalancer
132+
- service: Konnectivity
133+
servicePublishingStrategy:
134+
type: LoadBalancer
135+
release:
136+
image: "{{ .releaseImage }}"
137+
dns:
138+
baseDomain: "{{ .baseDomain }}"
139+
discovery:
140+
namespace: "{{ .namespace }}"
141+
by_selectors:
142+
label_selector:
143+
hyperfleet.io/cluster-id: "{{ .clusterId }}"
144+
145+
# Post-processing: report HostedCluster status back to API
146+
post:
147+
payloads:
148+
- name: "statusPayload"
149+
build:
150+
adapter: "{{ .adapter.name }}"
151+
conditions:
152+
- type: "Applied"
153+
status:
154+
expression: |
155+
has(resources.hostedCluster.metadata.creationTimestamp) ? "True" : "False"
156+
reason:
157+
expression: |
158+
has(resources.hostedCluster.metadata.creationTimestamp) ? "HostedClusterCreated" : "HostedClusterPending"
159+
message:
160+
expression: |
161+
has(resources.hostedCluster.metadata.creationTimestamp)
162+
? "HostedCluster has been created on the management cluster"
163+
: "HostedCluster is pending creation"
164+
- type: "Available"
165+
status:
166+
expression: |
167+
has(resources.hostedCluster.status) && has(resources.hostedCluster.status.conditions)
168+
? (resources.hostedCluster.status.conditions.filter(c, c.type == "Available").size() > 0
169+
? resources.hostedCluster.status.conditions.filter(c, c.type == "Available")[0].status
170+
: "False")
171+
: "False"
172+
reason:
173+
expression: |
174+
has(resources.hostedCluster.status) && has(resources.hostedCluster.status.conditions)
175+
? (resources.hostedCluster.status.conditions.filter(c, c.type == "Available").size() > 0
176+
? resources.hostedCluster.status.conditions.filter(c, c.type == "Available")[0].reason
177+
: "WaitingForControlPlane")
178+
: "WaitingForControlPlane"
179+
message:
180+
expression: |
181+
has(resources.hostedCluster.status) && has(resources.hostedCluster.status.conditions)
182+
? (resources.hostedCluster.status.conditions.filter(c, c.type == "Available").size() > 0
183+
? resources.hostedCluster.status.conditions.filter(c, c.type == "Available")[0].message
184+
: "Waiting for hosted control plane to become available")
185+
: "Waiting for hosted control plane to become available"
186+
- type: "Health"
187+
status:
188+
expression: |
189+
adapter.?executionStatus.orValue("") == "success" ? "True" : (adapter.?executionStatus.orValue("") == "failed" ? "False" : "Unknown")
190+
reason:
191+
expression: |
192+
adapter.?errorReason.orValue("") != "" ? adapter.?errorReason.orValue("") : "Healthy"
193+
message:
194+
expression: |
195+
adapter.?errorMessage.orValue("") != "" ? adapter.?errorMessage.orValue("") : "Adapter executed successfully"
196+
observed_generation:
197+
expression: "generation"
198+
observed_time: "{{ now | date \"2006-01-02T15:04:05Z07:00\" }}"
199+
200+
post_actions:
201+
- name: "reportClusterStatus"
202+
api_call:
203+
method: "POST"
204+
url: "/clusters/{{ .clusterId }}/statuses"
205+
headers:
206+
- name: "Content-Type"
207+
value: "application/json"
208+
body: "{{ .statusPayload }}"
20.5 KB
Binary file not shown.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Values for adapter-hypershift
2+
# Creates HostedCluster resources on a remote HyperShift management cluster
3+
4+
hyperfleet-adapter:
5+
image:
6+
registry: CHANGE_ME
7+
repository: CHANGE_ME
8+
tag: latest
9+
10+
adapterConfig:
11+
create: true
12+
log:
13+
level: debug
14+
15+
adapterTaskConfig:
16+
create: true
17+
18+
broker:
19+
type: googlepubsub
20+
googlepubsub:
21+
projectId: CHANGE_ME
22+
subscriptionId: CHANGE_ME
23+
topic: CHANGE_ME
24+
deadLetterTopic: ""
25+
createTopicIfMissing: true
26+
createSubscriptionIfMissing: true
27+
rabbitmq:
28+
url: CHANGE_ME
29+
queue: ""
30+
exchange: ""
31+
routingKey: ""
32+
33+
env:
34+
- name: NAMESPACE
35+
valueFrom:
36+
fieldRef:
37+
fieldPath: metadata.namespace
38+
- name: CLUSTERS_NAMESPACE
39+
value: clusters
40+
- name: OCI_REGION
41+
value: us-sanjose-1
42+
- name: OCI_COMPARTMENT_ID
43+
value: ocid1.compartment.oc1..aaaaaaaazgovbe2qxduadk3bmj5dobvoe5wnengzavax5pwsfr3bqbdrrcqa
44+
- name: OPENSHIFT_RELEASE_IMAGE
45+
value: "quay.io/openshift-release-dev/ocp-release:4.20.2-x86_64"
46+
- name: BASE_DOMAIN
47+
value: hyperfleet.local
48+
- name: CPO_IMAGE
49+
value: "quay.io/vkareh/control-plane-operator:1775859030"
50+
51+
# Mount the management cluster kubeconfig
52+
extraVolumeMounts:
53+
- name: hypershift-kubeconfig
54+
mountPath: /etc/hypershift
55+
readOnly: true
56+
57+
extraVolumes:
58+
- name: hypershift-kubeconfig
59+
secret:
60+
secretName: hypershift-mgmt-kubeconfig
61+
62+
# RBAC is for the local CLM cluster only; remote access uses the mounted kubeconfig
63+
rbac:
64+
resources:
65+
- configmaps

0 commit comments

Comments
 (0)