|
1 | 1 | import './App.css'
|
2 | 2 | import React, { useState } from "react";
|
3 | 3 |
|
| 4 | +// Reusable input with tooltip component |
| 5 | +const InputWithTooltip = ({ type, name, placeholder, value, onChange, required, tooltip, className = "" }) => { |
| 6 | + const [showTooltip, setShowTooltip] = useState(false); |
| 7 | + |
| 8 | + return ( |
| 9 | + <div className="relative w-full mb-3"> |
| 10 | + <div className="flex items-center"> |
| 11 | + <input |
| 12 | + type={type || "text"} |
| 13 | + name={name} |
| 14 | + placeholder={placeholder} |
| 15 | + value={value} |
| 16 | + onChange={onChange} |
| 17 | + className={`w-full p-2 border rounded ${className}`} |
| 18 | + required={required} |
| 19 | + /> |
| 20 | + <div |
| 21 | + className="ml-2 text-gray-500 cursor-help" |
| 22 | + onMouseEnter={() => setShowTooltip(true)} |
| 23 | + onMouseLeave={() => setShowTooltip(false)} |
| 24 | + > |
| 25 | + <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> |
| 26 | + <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" /> |
| 27 | + </svg> |
| 28 | + </div> |
| 29 | + </div> |
| 30 | + {showTooltip && ( |
| 31 | + <div className="absolute right-0 z-10 p-2 mt-1 text-sm bg-gray-800 text-white rounded shadow-lg w-64"> |
| 32 | + {tooltip} |
| 33 | + </div> |
| 34 | + )} |
| 35 | + </div> |
| 36 | + ); |
| 37 | +}; |
| 38 | + |
4 | 39 | export default function HcpCliAssistant() {
|
5 | 40 | const steps = [
|
6 | 41 | "Cluster Details",
|
@@ -167,27 +202,143 @@ export default function HcpCliAssistant() {
|
167 | 202 | ) : (
|
168 | 203 | <div className="space-y-4">
|
169 | 204 | {step === 0 && <>
|
170 |
| - <input type="text" name="name" placeholder="Cluster Name (required). Example: test" value={form.name} onChange={handleChange} className="w-full p-2 border rounded" required /> |
171 |
| - <input type="text" name="baseDomain" placeholder="Base Domain (required). Example: mydomain.com" value={form.baseDomain} onChange={handleChange} className="w-full p-2 border rounded" required /> |
172 |
| - <input type="number" name="nodePoolReplicas" placeholder="Node Pool Replicas (required)" value={form.nodePoolReplicas} onChange={handleChange} className="w-full p-2 border rounded" required /> |
173 |
| - <input type="text" name="pullSecret" placeholder="Pull Secret path (required). Example: /path/to/pull-secret" value={form.pullSecret} onChange={handleChange} className="w-full p-2 border rounded" required /> |
174 |
| - <input type="text" name="sshKey" placeholder="SSH Key path (required). Example: /path/to/id_rsa.pub" value={form.sshKey} onChange={handleChange} className="w-full p-2 border rounded" required /> |
| 205 | + <InputWithTooltip |
| 206 | + name="name" |
| 207 | + placeholder="Cluster Name (required). Example: test" |
| 208 | + value={form.name} |
| 209 | + onChange={handleChange} |
| 210 | + required |
| 211 | + tooltip="A unique name for your HyperShift cluster. This will be used to identify your cluster in the HyperShift control plane." |
| 212 | + /> |
| 213 | + <InputWithTooltip |
| 214 | + name="baseDomain" |
| 215 | + placeholder="Base Domain (required). Example: mydomain.com" |
| 216 | + value={form.baseDomain} |
| 217 | + onChange={handleChange} |
| 218 | + required |
| 219 | + tooltip="The base domain of your cluster. This domain will be used to create DNS records for your cluster." |
| 220 | + /> |
| 221 | + <InputWithTooltip |
| 222 | + type="number" |
| 223 | + name="nodePoolReplicas" |
| 224 | + placeholder="Node Pool Replicas (required)" |
| 225 | + value={form.nodePoolReplicas} |
| 226 | + onChange={handleChange} |
| 227 | + required |
| 228 | + tooltip="The number of worker nodes to create in your cluster's default node pool." |
| 229 | + /> |
| 230 | + <InputWithTooltip |
| 231 | + name="pullSecret" |
| 232 | + placeholder="Pull Secret path (required). Example: /path/to/pull-secret" |
| 233 | + value={form.pullSecret} |
| 234 | + onChange={handleChange} |
| 235 | + required |
| 236 | + tooltip="Path to the pull secret file. This is required to pull container images from the Red Hat registry." |
| 237 | + /> |
| 238 | + <InputWithTooltip |
| 239 | + name="sshKey" |
| 240 | + placeholder="SSH Key path (required). Example: /path/to/id_rsa.pub" |
| 241 | + value={form.sshKey} |
| 242 | + onChange={handleChange} |
| 243 | + required |
| 244 | + tooltip="Path to your SSH public key file. This key will be added to the authorized_keys on all nodes." |
| 245 | + /> |
175 | 246 | </>}
|
176 |
| - {step === 1 && <><label className="block"><input type="checkbox" name="osCloudSet" checked={form.osCloudSet} onChange={handleChange} className="mr-2" />OS_CLOUD is set in the environment</label>{!form.osCloudSet && ( |
177 |
| - <> |
178 |
| - <input type="text" name="openstackCredentialsFile" placeholder="OpenStack Credentials File (optional). Example: /path/to/clouds.yaml" value={form.openstackCredentialsFile} onChange={handleChange} className="w-full p-2 border rounded" /> |
179 |
| - </> |
180 |
| - )} |
181 |
| - <input type="text" name="openstackCloud" placeholder="OpenStack Cloud (optional). Default: openstack" value={form.openstackCloud} onChange={handleChange} className="w-full p-2 border rounded" /> |
182 |
| - <input type="text" name="openstackCaCertFile" placeholder="OpenStack CA Certificate File (optional). Example: /path/to/ca.cert" value={form.openstackCaCertFile} onChange={handleChange} className="w-full p-2 border rounded" /></>} |
| 247 | + {step === 1 && <> |
| 248 | + <label className="block"> |
| 249 | + <input |
| 250 | + type="checkbox" |
| 251 | + name="osCloudSet" |
| 252 | + checked={form.osCloudSet} |
| 253 | + onChange={handleChange} |
| 254 | + className="mr-2" |
| 255 | + /> |
| 256 | + OS_CLOUD is set in the environment |
| 257 | + <span |
| 258 | + className="ml-2 text-gray-500 cursor-help relative inline-block" |
| 259 | + onMouseEnter={(e) => e.target.querySelector('div').style.display = 'block'} |
| 260 | + onMouseLeave={(e) => e.target.querySelector('div').style.display = 'none'} |
| 261 | + > |
| 262 | + <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 inline-block" viewBox="0 0 20 20" fill="currentColor"> |
| 263 | + <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" /> |
| 264 | + </svg> |
| 265 | + <div className="absolute right-0 z-10 p-2 mt-1 text-sm bg-gray-800 text-white rounded shadow-lg w-64" style={{display: 'none'}}> |
| 266 | + Check this if you have the OS_CLOUD environment variable set. Otherwise, you'll need to provide a credentials file. |
| 267 | + </div> |
| 268 | + </span> |
| 269 | + </label> |
| 270 | + |
| 271 | + {!form.osCloudSet && ( |
| 272 | + <InputWithTooltip |
| 273 | + name="openstackCredentialsFile" |
| 274 | + placeholder="OpenStack Credentials File (optional). Example: /path/to/clouds.yaml" |
| 275 | + value={form.openstackCredentialsFile} |
| 276 | + onChange={handleChange} |
| 277 | + tooltip="Path to your OpenStack clouds.yaml file containing your authentication credentials." |
| 278 | + /> |
| 279 | + )} |
| 280 | + |
| 281 | + <InputWithTooltip |
| 282 | + name="openstackCloud" |
| 283 | + placeholder="OpenStack Cloud (optional). Default: openstack" |
| 284 | + value={form.openstackCloud} |
| 285 | + onChange={handleChange} |
| 286 | + tooltip="The named cloud to use from your clouds.yaml file if you have multiple clouds defined." |
| 287 | + /> |
| 288 | + |
| 289 | + <InputWithTooltip |
| 290 | + name="openstackCaCertFile" |
| 291 | + placeholder="OpenStack CA Certificate File (optional). Example: /path/to/ca.cert" |
| 292 | + value={form.openstackCaCertFile} |
| 293 | + onChange={handleChange} |
| 294 | + tooltip="Path to a CA certificate file if your OpenStack environment uses a self-signed certificate." |
| 295 | + /> |
| 296 | + </>} |
| 297 | + |
183 | 298 | {step === 2 && (
|
184 | 299 | <>
|
185 |
| - <input type="text" name="externalNetworkId" placeholder="External Network ID (optional). Example: 64f629fd-f75b-4e66-96ad-94f6f2125ba4" value={form.externalNetworkId} onChange={handleChange} className="w-full p-2 border rounded" /> |
186 |
| - <input type="text" name="ingressFloatingIp" placeholder="Ingress Floating IP (optional). Example: 192.168.100.7" value={form.ingressFloatingIp} onChange={handleChange} className="w-full p-2 border rounded" /> |
187 |
| - <input type="text" name="dnsNameservers" placeholder="DNS Nameservers (optional). Example: 1.1.1.1,8.8.8.8" value={form.dnsNameservers} onChange={handleChange} className="w-full p-2 border rounded" /> |
| 300 | + <InputWithTooltip |
| 301 | + name="externalNetworkId" |
| 302 | + placeholder="External Network ID (optional). Example: 64f629fd-f75b-4e66-96ad-94f6f2125ba4" |
| 303 | + value={form.externalNetworkId} |
| 304 | + onChange={handleChange} |
| 305 | + tooltip="The ID of the external network that will be used for floating IPs. If not provided, the installer will attempt to discover it." |
| 306 | + /> |
188 | 307 |
|
| 308 | + <InputWithTooltip |
| 309 | + name="ingressFloatingIp" |
| 310 | + placeholder="Ingress Floating IP (optional). Example: 192.168.100.7" |
| 311 | + value={form.ingressFloatingIp} |
| 312 | + onChange={handleChange} |
| 313 | + tooltip="A pre-allocated floating IP to use for cluster ingress. If not provided, a new floating IP will be allocated." |
| 314 | + /> |
| 315 | + |
| 316 | + <InputWithTooltip |
| 317 | + name="dnsNameservers" |
| 318 | + placeholder="DNS Nameservers (optional). Example: 1.1.1.1,8.8.8.8" |
| 319 | + value={form.dnsNameservers} |
| 320 | + onChange={handleChange} |
| 321 | + tooltip="Comma-separated list of DNS nameservers to use for the cluster's subnet." |
| 322 | + /> |
| 323 | + |
| 324 | + {/* Additional ports section remains the same */} |
189 | 325 | <div className="mt-4">
|
190 |
| - <h3 className="font-semibold mb-2">Additional Nodepool Ports (optional)</h3> |
| 326 | + <h3 className="font-semibold mb-2"> |
| 327 | + Additional Nodepool Ports (optional) |
| 328 | + <span |
| 329 | + className="ml-2 text-gray-500 cursor-help relative inline-block" |
| 330 | + onMouseEnter={(e) => e.target.querySelector('div').style.display = 'block'} |
| 331 | + onMouseLeave={(e) => e.target.querySelector('div').style.display = 'none'} |
| 332 | + > |
| 333 | + <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 inline-block" viewBox="0 0 20 20" fill="currentColor"> |
| 334 | + <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" /> |
| 335 | + </svg> |
| 336 | + <div className="absolute right-0 z-10 p-2 mt-1 text-sm bg-gray-800 text-white rounded shadow-lg w-64" style={{display: 'none'}}> |
| 337 | + Configure additional network ports for the nodes in your cluster. Useful for multi-network setups or SR-IOV configurations. |
| 338 | + </div> |
| 339 | + </span> |
| 340 | + </h3> |
| 341 | + |
191 | 342 | {form.additionalPorts && JSON.parse(form.additionalPorts).map((port, index) => (
|
192 | 343 | <div key={index} className="p-3 border rounded mb-2">
|
193 | 344 | <div className="flex items-center justify-between mb-2">
|
@@ -294,7 +445,33 @@ export default function HcpCliAssistant() {
|
294 | 445 | </div>
|
295 | 446 | </>
|
296 | 447 | )}
|
297 |
| - {step === 3 && <><input type="text" name="nodeFlavor" placeholder="Flavor name for the Nodepool (Required)" value={form.nodeFlavor} onChange={handleChange} className="w-full p-2 border rounded" required /><input type="text" name="nodeAZ" placeholder="Nova Availability Zone (optional)" value={form.nodeAZ} onChange={handleChange} className="w-full p-2 border rounded" /><input type="text" name="nodeImageName" placeholder="Glance Image Name (optional)" value={form.nodeImageName} onChange={handleChange} className="w-full p-2 border rounded" /></>} |
| 448 | + |
| 449 | + {step === 3 && <> |
| 450 | + <InputWithTooltip |
| 451 | + name="nodeFlavor" |
| 452 | + placeholder="Flavor name for the Nodepool (Required)" |
| 453 | + value={form.nodeFlavor} |
| 454 | + onChange={handleChange} |
| 455 | + required |
| 456 | + tooltip="The OpenStack flavor (instance type) to use for the worker nodes in your cluster." |
| 457 | + /> |
| 458 | + |
| 459 | + <InputWithTooltip |
| 460 | + name="nodeAZ" |
| 461 | + placeholder="Nova Availability Zone (optional)" |
| 462 | + value={form.nodeAZ} |
| 463 | + onChange={handleChange} |
| 464 | + tooltip="The availability zone where worker nodes will be created. If not specified, the default AZ will be used." |
| 465 | + /> |
| 466 | + |
| 467 | + <InputWithTooltip |
| 468 | + name="nodeImageName" |
| 469 | + placeholder="Glance Image Name (optional)" |
| 470 | + value={form.nodeImageName} |
| 471 | + onChange={handleChange} |
| 472 | + tooltip="The name of the RHCOS image in Glance to use for the worker nodes. If not specified, the installer will use the latest available RHCOS image." |
| 473 | + /> |
| 474 | + </>} |
298 | 475 | </div>
|
299 | 476 | )}
|
300 | 477 |
|
|
0 commit comments