diff --git a/libs/ui-lib-tests/cypress/support/interceptors.ts b/libs/ui-lib-tests/cypress/support/interceptors.ts index e0799b4fdb..593eb1f947 100644 --- a/libs/ui-lib-tests/cypress/support/interceptors.ts +++ b/libs/ui-lib-tests/cypress/support/interceptors.ts @@ -248,6 +248,8 @@ const addClusterPatchAndDetailsIntercepts = () => { const addDay1InfraEnvIntercepts = () => { const infraEnvApiPath = getDay1InfraEnvApiPath(); + let infraEnvCreated = false; + // Actions on particular infraEnv cy.intercept('GET', infraEnvApiPath, mockInfraEnvResponse).as('infra-env-details'); @@ -258,11 +260,26 @@ const addDay1InfraEnvIntercepts = () => { // Actions on all the infraEnvs cy.intercept('PATCH', infraEnvApiPath, mockInfraEnvResponse).as('update-infra-env'); - cy.intercept('GET', `${allInfraEnvsApiPath}?cluster_id=${Cypress.env('clusterId')}`, [ - fixtures.baseInfraEnv, - ]).as('filter-infra-envs'); + cy.intercept('GET', `${allInfraEnvsApiPath}?cluster_id=${Cypress.env('clusterId')}`, (req) => { + // Return empty array until POST creates the infraEnv, or if signal indicates it already exists + // Signal starts as '' and is set to a value after cluster/infraenv creation + const currentSignal = Cypress.env('AI_LAST_SIGNAL'); + const willReturnFixture = infraEnvCreated || (currentSignal !== '' && currentSignal !== undefined && currentSignal !== null); + + console.log(`GET infra-envs: signal="${currentSignal}", infraEnvCreated=${infraEnvCreated}, returning ${willReturnFixture ? 'fixture' : 'empty'}`); + + if (willReturnFixture) { + req.reply([fixtures.baseInfraEnv]); + } else { + req.reply([]); + } + }).as('filter-infra-envs'); - cy.intercept('POST', allInfraEnvsApiPath, mockInfraEnvResponse).as('create-infra-env'); + cy.intercept('POST', allInfraEnvsApiPath, (req) => { + console.log('POST infra-env'); + infraEnvCreated = true; + mockInfraEnvResponse(req); + }).as('create-infra-env'); }; const getDay2InfraEnvByCpuArch = (req) => { diff --git a/libs/ui-lib/lib/ocm/hooks/useInfraEnvId.ts b/libs/ui-lib/lib/ocm/hooks/useInfraEnvId.ts index 8dca06a67a..655652a5b0 100644 --- a/libs/ui-lib/lib/ocm/hooks/useInfraEnvId.ts +++ b/libs/ui-lib/lib/ocm/hooks/useInfraEnvId.ts @@ -22,24 +22,29 @@ export default function useInfraEnvId( const findInfraEnvId = React.useCallback(async () => { try { - const infraEnvId = await InfraEnvsService.getInfraEnvId( - clusterId, - cpuArchitecture, - isSingleClusterFeatureEnabled, - ); - if (infraEnvId && !(infraEnvId instanceof Error)) { - setInfraEnv(infraEnvId); - } else { - //If infraEnv doesn't exist create a new one - if (pullSecret) { - const infraEnv = await InfraEnvsService.create({ + if (pullSecret) { + const infraEnv = await InfraEnvsService.getOrCreate( + { name: InfraEnvsService.makeInfraEnvName(cpuArchitecture, clusterName), pullSecret, clusterId, openshiftVersion, cpuArchitecture: cpuArchitecture as InfraEnvCreateParams['cpuArchitecture'], - }); - setInfraEnv(infraEnv.id); + }, + isSingleClusterFeatureEnabled, + ); + setInfraEnv(infraEnv.id); + } else { + // No pullSecret available, just try to find existing InfraEnv + const infraEnvId = await InfraEnvsService.getInfraEnvId( + clusterId, + cpuArchitecture, + isSingleClusterFeatureEnabled, + ); + if (infraEnvId && !(infraEnvId instanceof Error)) { + setInfraEnv(infraEnvId); + } else if (infraEnvId instanceof Error) { + setError(getErrorMessage(infraEnvId)); } } } catch (e) { diff --git a/libs/ui-lib/lib/ocm/services/ClustersService.ts b/libs/ui-lib/lib/ocm/services/ClustersService.ts index f6c5c94e9e..331f83f3c6 100644 --- a/libs/ui-lib/lib/ocm/services/ClustersService.ts +++ b/libs/ui-lib/lib/ocm/services/ClustersService.ts @@ -48,9 +48,7 @@ const ClustersService = { infraEnvCreateParams.imageType = 'minimal-iso'; } - if (!isSingleClusterFeatureEnabled) { - await InfraEnvsService.create(infraEnvCreateParams); - } + await InfraEnvsService.getOrCreate(infraEnvCreateParams, isSingleClusterFeatureEnabled); return cluster; }, diff --git a/libs/ui-lib/lib/ocm/services/InfraEnvsService.ts b/libs/ui-lib/lib/ocm/services/InfraEnvsService.ts index d122399700..7470aeb9cd 100644 --- a/libs/ui-lib/lib/ocm/services/InfraEnvsService.ts +++ b/libs/ui-lib/lib/ocm/services/InfraEnvsService.ts @@ -3,6 +3,7 @@ import { CpuArchitecture } from '../../common'; import { InfraEnvsAPI } from './apis'; import InfraEnvCache from './InfraEnvIdsCacheService'; import { getDummyInfraEnvField } from '../components/clusterConfiguration/staticIp/data/dummyData'; +import { isAxiosError } from '../../common/api/axiosExtensions'; import { Cluster, HostStaticNetworkConfig, @@ -67,6 +68,47 @@ const InfraEnvsService = { return infraEnv; }, + async getOrCreate( + params: InfraEnvCreateParams, + isSingleClusterFeatureEnabled?: boolean, + ): Promise { + if (!params.clusterId) { + throw new Error('Cannot create InfraEnv, clusterId is missing'); + } + + if (!params.cpuArchitecture) { + throw new Error('Cannot get or create InfraEnv, cpuArchitecture is missing'); + } + + // Check if an InfraEnv already exists + try { + const existingInfraEnvId = await InfraEnvsService.getInfraEnvId( + params.clusterId, + params.cpuArchitecture as CpuArchitecture, + isSingleClusterFeatureEnabled, + ); + + if (existingInfraEnvId && !(existingInfraEnvId instanceof Error)) { + // InfraEnv exists, fetch and return it + const { data: infraEnv } = await InfraEnvsAPI.get(existingInfraEnvId); + // Update cache to maintain consistency + InfraEnvCache.updateInfraEnvs(params.clusterId, [infraEnv]); + return infraEnv; + } + } catch (error) { + // Only suppress 404 (not found) errors - let other errors propagate + if (isAxiosError(error) && error.response?.status === 404) { + // Fall through to create a new InfraEnv + } else { + // Re-throw authentication, network, and other unexpected errors + throw error; + } + } + + // No InfraEnv exists, create one + return InfraEnvsService.create(params); + }, + async removeAll(clusterId: Cluster['id']) { const { data: infraEnvs } = await InfraEnvsAPI.list(clusterId);