Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,58 @@ describe('ClusterCardFeature', () => {
const toggle = screen.getByTestId('feature')
expect(toggle).toBeInTheDocument()
})

it('should show NAT_GATEWAY as false when gcp nested static_ips_enabled is false', () => {
renderWithProviders(
wrapWithReactHookForm(
<ClusterCardFeature
cloudProvider={CloudProviderEnum.GCP}
feature={{
id: 'NAT_GATEWAY',
title: 'Static IP / Nat Gateways',
value_object: {
type: 'NAT_GATEWAY',
value: {
nat_gateway_type: {
provider: 'gcp',
static_ips_enabled: false,
static_ips_count: 2,
},
},
},
}}
disabled
/>
)
)

expect(screen.getByDisplayValue('false')).toBeInTheDocument()
})

it('should read NAT_GATEWAY nested nat_gateway_type static_ips_enabled', () => {
renderWithProviders(
wrapWithReactHookForm(
<ClusterCardFeature
cloudProvider={CloudProviderEnum.GCP}
feature={{
id: 'NAT_GATEWAY',
title: 'Static IP / Nat Gateways',
value_object: {
type: 'NAT_GATEWAY',
value: {
nat_gateway_type: {
provider: 'gcp',
static_ips_enabled: true,
static_ips_count: 2,
},
},
},
}}
disabled
/>
)
)

expect(screen.getByDisplayValue('true')).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type CloudVendorEnum, type ClusterFeatureResponse } from 'qovery-typesc
import { type PropsWithChildren, type ReactNode, useEffect, useState } from 'react'
import { type Control, Controller, type FieldValues, type UseFormSetValue, type UseFormWatch } from 'react-hook-form'
import { ExternalLink, Icon, InputSelect, InputToggle, Tooltip } from '@qovery/shared/ui'
import { getGcpNatGatewaySettings } from '../utils/get-gcp-nat-gateway-settings'

export interface ClusterCardFeatureProps extends PropsWithChildren {
feature: ClusterFeatureResponse
Expand All @@ -27,11 +28,21 @@ export function ClusterCardFeature({

const name = watch && watch(`features.${feature.id}.value`)

const getValue = (value: boolean | string) => {
const getFeatureToggleValue = (feature: ClusterFeatureResponse) => {
const value = feature.value_object?.value

if (feature.id === 'NAT_GATEWAY' && cloudProvider === 'GCP') {
const gcpNatGatewaySettings = getGcpNatGatewaySettings(feature)
if (gcpNatGatewaySettings) {
return gcpNatGatewaySettings.static_ips_enabled
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this case is legacy-only (NAT_GATEWAY without STATIC_IP). In the normal flow we merge both into GcpStaticIp, where static_ips_enabled is already represented by the sub-toggle, so changing this fallback toggle to always true would be misleading.

}
}

if (typeof value === 'string') {
return true
}
return value

return Boolean(value)
}

useEffect(() => {
Expand Down Expand Up @@ -69,12 +80,7 @@ export function ClusterCardFeature({
) : (
<Tooltip content={tooltip} disabled={!tooltip}>
<span>
<InputToggle
disabled
small
className="relative top-[2px]"
value={getValue(Boolean(feature?.value_object?.value) || false)}
/>
<InputToggle disabled small className="relative top-[2px]" value={getFeatureToggleValue(feature)} />
</span>
</Tooltip>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ describe('StepFeatures', () => {
})
})

it('should hide NAT_GATEWAY feature for GCP cluster creation', async () => {
it('should merge STATIC_IP and NAT_GATEWAY in GCP network configuration', async () => {
useCloudProviderFeaturesMockSpy.mockReturnValue({
data: [
{
Expand All @@ -119,7 +119,12 @@ describe('StepFeatures', () => {
{
id: 'NAT_GATEWAY',
title: 'NAT Gateway',
value_object: { value: false },
value_object: {
value: {
static_ips_enabled: false,
static_ips_count: 2,
},
},
},
{
id: 'PRIVATE_CLUSTER',
Expand All @@ -145,9 +150,11 @@ describe('StepFeatures', () => {
renderWithProviders(<StepFeatures {...defaultProps} />, { wrapper: getWrapper(gcpContextValue) })

await waitFor(() => {
expect(screen.getAllByText('Static IP / Nat Gateways').length).toBeGreaterThan(0)
expect(screen.getByText('Enable static egress IPs')).toBeInTheDocument()
expect(screen.queryByText('Static IP count')).not.toBeInTheDocument()
expect(screen.getByText('Private Cluster')).toBeInTheDocument()
expect(screen.getByText('Static IP')).toBeInTheDocument()
expect(screen.queryByText('NAT Gateway')).not.toBeInTheDocument()
expect(screen.queryByText(/^NAT Gateway$/)).not.toBeInTheDocument()
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import {
} from '@qovery/shared/ui'
import { twMerge } from '@qovery/shared/util-js'
import { ClusterCardFeature } from '../../cluster-card-feature/cluster-card-feature'
import { GcpStaticIp } from '../../gcp-static-ip/gcp-static-ip'
import { ScalewayStaticIp } from '../../scaleway-static-ip/scaleway-static-ip'
import { steps, useClusterContainerCreateContext } from '../cluster-creation-flow'
import AWSVpcFeature from './aws-vpc-feature/aws-vpc-feature'
import GCPVpcFeature from './gcp-vpc-feature/gcp-vpc-feature'

const Qovery = '/assets/logos/logo-icon.svg'
const GCP_HIDDEN_FEATURE_IDS = new Set(['NAT_GATEWAY'])
const removeEmptySubnet = (objects?: Subnets[]) =>
objects?.filter((field) => field.A !== '' || field.B !== '' || field.C !== '')

Expand Down Expand Up @@ -212,11 +212,32 @@ function StepFeaturesForm({
{cloudProvider === 'GCP' && (
<div>
{match(watchVpcMode)
.with('DEFAULT', () =>
features && features.length > 0 ? (
features
.filter((feature) => !GCP_HIDDEN_FEATURE_IDS.has(feature.id ?? ''))
.map((feature) => (
.with('DEFAULT', () => {
if (!features || features.length === 0) {
return (
<div className="mt-2 flex justify-center">
<LoaderSpinner className="w-4" />
</div>
)
}

const staticIpFeature = features.find(({ id }) => id === 'STATIC_IP')
const natGatewayFeature = features.find(({ id }) => id === 'NAT_GATEWAY')
const hasMergedStaticIpNatGateway = Boolean(staticIpFeature && natGatewayFeature)
const remainingFeatures = hasMergedStaticIpNatGateway
? features.filter(({ id }) => id !== 'STATIC_IP' && id !== 'NAT_GATEWAY')
: features

return (
<>
{hasMergedStaticIpNatGateway && (
<GcpStaticIp
staticIpFeature={staticIpFeature}
natGatewayFeature={natGatewayFeature}
production={isProduction || false}
/>
)}
{remainingFeatures.map((feature) => (
<ClusterCardFeature
key={feature.id}
feature={feature}
Expand All @@ -225,13 +246,10 @@ function StepFeaturesForm({
watch={clusterCardFeatureFormBindings.watch}
setValue={clusterCardFeatureFormBindings.setValue}
/>
))
) : (
<div className="mt-2 flex justify-center">
<LoaderSpinner className="w-4" />
</div>
))}
</>
)
)
})
.with('EXISTING_VPC', () => <GCPVpcFeature />)
.otherwise(() => null)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ function SubnetsList({ title, index, subnets }: { title: string; index: string;
)
}

function formatFeatureValue(feature: ClusterFeaturesData['features'][string]) {
if (typeof feature.extendedValue === 'string') {
return feature.extendedValue
}

if (feature.extendedValue && typeof feature.extendedValue === 'object') {
const staticIpsEnabled = feature.extendedValue.static_ips_enabled
const staticIpsCount = feature.extendedValue.static_ips_count
return `static_ips_enabled=${staticIpsEnabled}, static_ips_count=${staticIpsCount}`
}

return feature.value.toString()
}

export function StepSummaryPresentation(props: StepSummaryPresentationProps) {
const clusterBackup = props.resourcesData.infrastructure_charts_parameters?.eks_anywhere_parameters?.cluster_backup
const showClusterBackup = Boolean(clusterBackup?.enabled)
Expand Down Expand Up @@ -595,7 +609,7 @@ export function StepSummaryPresentation(props: StepSummaryPresentationProps) {
return (
<li key={id}>
<strong className="font-medium">{currentFeature.title}: </strong>
{currentFeature.extendedValue ? currentFeature.extendedValue : currentFeature.value.toString()}
{formatFeatureValue(currentFeature)}
</li>
)
})}
Expand Down
Loading
Loading