diff --git a/frontend/src/assets/Logos/gcp.svg b/frontend/src/assets/Logos/gcp.svg new file mode 100644 index 0000000000..2e0200e474 --- /dev/null +++ b/frontend/src/assets/Logos/gcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/AccountActions/AccountActions.tsx b/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/AccountActions/AccountActions.tsx index 55920fc262..efc60ae47b 100644 --- a/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/AccountActions/AccountActions.tsx +++ b/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/AccountActions/AccountActions.tsx @@ -20,6 +20,7 @@ import AzureAccountSettingsModal from '../../AzureCloudServices/EditAccount/Acco import { mapAccountDtoToAwsCloudAccount, mapAccountDtoToAzureCloudAccount, + mapAccountDtoToGcpCloudAccount, } from '../../mapCloudAccountFromDto'; import AwsCloudAccountSetupModal from '../AddNewAccount/CloudAccountSetupModal'; import AwsAccountSettingsModal from '../EditAccount/AccountSettingsModal'; @@ -156,6 +157,18 @@ function AccountActions({ type }: { type: IntegrationType }): JSX.Element { }); } + if (type === IntegrationType.GCP_SERVICES) { + raw.forEach((account) => { + if (!account) { + return; + } + const mapped = mapAccountDtoToGcpCloudAccount(account); + if (mapped) { + mappedAccounts.push(mapped); + } + }); + } + return mappedAccounts; }, [listAccountsResponse, type]); @@ -207,13 +220,23 @@ function AccountActions({ type }: { type: IntegrationType }): JSX.Element { // log telemetry event when an account is viewed. useEffect(() => { if (activeAccount) { + const { config } = activeAccount; + let enabledRegions: string[]; + if ('regions' in config) { + // AWS + enabledRegions = config.regions; + } else if ('resource_groups' in config) { + // Azure + enabledRegions = config.resource_groups; + } else { + // GCP + enabledRegions = config.project_ids; + } + logEvent(`${type} Integration: Account viewed`, { cloudAccountId: activeAccount?.cloud_account_id, status: activeAccount?.status, - enabledRegions: - 'regions' in activeAccount.config - ? activeAccount.config.regions - : activeAccount.config.resource_groups, + enabledRegions, }); } }, [activeAccount, type]); diff --git a/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/ServiceDetails/ServiceDetails.tsx b/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/ServiceDetails/ServiceDetails.tsx index ce03341cb9..0f28594965 100644 --- a/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/ServiceDetails/ServiceDetails.tsx +++ b/frontend/src/container/Integrations/CloudIntegration/AmazonWebServices/ServiceDetails/ServiceDetails.tsx @@ -46,14 +46,31 @@ const EMPTY_FORM_VALUES: ServiceConfigFormValues = { s3BucketsByRegion: {}, }; +function getIntegrationServiceConfig( + type: IntegrationType, + serviceDetailsData?: ServiceDetailsData, +): + | { logs?: { enabled?: boolean }; metrics?: { enabled?: boolean } } + | undefined { + const config = serviceDetailsData?.cloudIntegrationService?.config; + + if (type === IntegrationType.AWS_SERVICES) { + return config?.aws; + } + if (type === IntegrationType.GCP_SERVICES) { + return config?.gcp; + } + return config?.azure; +} + function getInitialFormValues( type: IntegrationType, serviceDetailsData?: ServiceDetailsData, ): ServiceConfigFormValues { - const integrationConfig = - type === IntegrationType.AWS_SERVICES - ? serviceDetailsData?.cloudIntegrationService?.config?.aws - : serviceDetailsData?.cloudIntegrationService?.config?.azure; + const integrationConfig = getIntegrationServiceConfig( + type, + serviceDetailsData, + ); return { logsEnabled: integrationConfig?.logs?.enabled || false, @@ -98,16 +115,21 @@ function getServiceConfigPayload({ }; } - return { - azure: { - logs: { - enabled: isLogsSupported ? logsEnabled : false, - }, - metrics: { - enabled: isMetricsSupported ? metricsEnabled : false, - }, + // Azure and GCP share the same simple logs/metrics enable-flag shape. + const signalConfig = { + logs: { + enabled: isLogsSupported ? logsEnabled : false, + }, + metrics: { + enabled: isMetricsSupported ? metricsEnabled : false, }, }; + + if (type === IntegrationType.GCP_SERVICES) { + return { gcp: signalConfig }; + } + + return { azure: signalConfig }; } function ServiceDetails({ @@ -163,10 +185,10 @@ function ServiceDetails({ ? isAccountServiceLoading : isReadOnlyServiceLoading; - const integrationConfig = - type === IntegrationType.AWS_SERVICES - ? serviceDetailsData?.cloudIntegrationService?.config?.aws - : serviceDetailsData?.cloudIntegrationService?.config?.azure; + const integrationConfig = getIntegrationServiceConfig( + type, + serviceDetailsData, + ); const isServiceEnabledInPersistedConfig = Boolean(integrationConfig?.logs?.enabled) || Boolean(integrationConfig?.metrics?.enabled); diff --git a/frontend/src/container/Integrations/CloudIntegration/AzureCloudServices/EditAccount/AccountSettingsModal.tsx b/frontend/src/container/Integrations/CloudIntegration/AzureCloudServices/EditAccount/AccountSettingsModal.tsx index cbe0b1dc63..81b9ba9a3f 100644 --- a/frontend/src/container/Integrations/CloudIntegration/AzureCloudServices/EditAccount/AccountSettingsModal.tsx +++ b/frontend/src/container/Integrations/CloudIntegration/AzureCloudServices/EditAccount/AccountSettingsModal.tsx @@ -36,8 +36,12 @@ function AccountSettingsModal({ const queryClient = useQueryClient(); + // `account.config` is the shared per-provider union (Azure | AWS | GCP). + // Narrow to Azure by `resource_groups` (Azure-only) rather than + // `deployment_region`, which GCP also has — so it no longer identifies + // Azure uniquely. const azureConfig = useMemo( - () => ('deployment_region' in account.config ? account.config : null), + () => ('resource_groups' in account.config ? account.config : null), [account.config], ); diff --git a/frontend/src/container/Integrations/CloudIntegration/RemoveAccount/RemoveIntegrationAccount.tsx b/frontend/src/container/Integrations/CloudIntegration/RemoveAccount/RemoveIntegrationAccount.tsx index 9e3f166eb1..548046fa9e 100644 --- a/frontend/src/container/Integrations/CloudIntegration/RemoveAccount/RemoveIntegrationAccount.tsx +++ b/frontend/src/container/Integrations/CloudIntegration/RemoveAccount/RemoveIntegrationAccount.tsx @@ -60,6 +60,44 @@ function RemoveIntegrationAccount({ setIsModalOpen(false); }; + let modalDescription: JSX.Element; + if (cloudProvider === INTEGRATION_TYPES.AWS) { + modalDescription = ( + <> + Removing this account will remove all components created for sending + telemetry to SigNoz in your AWS account within the next ~15 minutes + (cloudformation stacks named signoz-integration-telemetry-collection in + enabled regions).
+
+ After that, you can delete the cloudformation stack that was created + manually when connecting this account. + + ); + } else if (cloudProvider === INTEGRATION_TYPES.GCP) { + modalDescription = ( + <> + Removing this account will stop SigNoz from monitoring it.
+
+ Since you manage the GCP resources yourself, remember to manually tear down + the OTel collector and Pub/Sub resources you created for this integration if + you no longer need them. + + ); + } else { + modalDescription = ( + <> + Removing this account will remove all components created for sending + telemetry to SigNoz in your Azure subscription within the next ~15 minutes + (deployment stack named signoz-integration-telemetry will be deleted + automatically).
+
+ After that, you have to manually delete 'signoz-integration' + deployment stack that was created while connecting this account (Takes ~20 + minutes to delete). + + ); + } + return (
); diff --git a/frontend/src/container/Integrations/CloudIntegration/mapCloudAccountFromDto.ts b/frontend/src/container/Integrations/CloudIntegration/mapCloudAccountFromDto.ts index cbd84f6338..71d0b15cdb 100644 --- a/frontend/src/container/Integrations/CloudIntegration/mapCloudAccountFromDto.ts +++ b/frontend/src/container/Integrations/CloudIntegration/mapCloudAccountFromDto.ts @@ -47,3 +47,27 @@ export function mapAccountDtoToAzureCloudAccount( providerAccountId: account.providerAccountId, }; } + +export function mapAccountDtoToGcpCloudAccount( + account: CloudintegrationtypesAccountDTO, +): IntegrationCloudAccount | null { + if (!account.providerAccountId) { + return null; + } + + return { + id: account.id, + cloud_account_id: account.id, + config: { + deployment_region: account.config?.gcp?.deploymentRegion ?? '', + deployment_project_id: account.config?.gcp?.deploymentProjectId ?? '', + project_ids: account.config?.gcp?.projectIds ?? [], + }, + status: { + integration: { + last_heartbeat_ts_ms: account.agentReport?.timestampMillis ?? 0, + }, + }, + providerAccountId: account.providerAccountId, + }; +} diff --git a/frontend/src/container/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx b/frontend/src/container/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx index 340b5e7f30..d58824468b 100644 --- a/frontend/src/container/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx +++ b/frontend/src/container/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx @@ -13,8 +13,8 @@ import { ArrowLeft, MoveUpRight, RotateCw } from '@signozhq/icons'; import awwSnapUrl from '@/assets/Icons/awwSnap.svg'; import CloudIntegration from '../CloudIntegration/CloudIntegration'; -import { INTEGRATION_TYPES } from '../constants'; import { IntegrationType } from '../types'; +import { INTEGRATION_TYPES } from '../constants'; import { handleContactSupport } from '../utils'; import IntegrationDetailContent from './IntegrationDetailContent'; import IntegrationDetailHeader from './IntegrationDetailHeader'; @@ -24,6 +24,12 @@ import { getConnectionStatesFromConnectionStatus } from './utils'; import './IntegrationDetailPage.styles.scss'; +const cloudIntegrationTypeById: Record = { + [INTEGRATION_TYPES.AWS]: IntegrationType.AWS_SERVICES, + [INTEGRATION_TYPES.AZURE]: IntegrationType.AZURE_SERVICES, + [INTEGRATION_TYPES.GCP]: IntegrationType.GCP_SERVICES, +}; + // eslint-disable-next-line sonarjs/cognitive-complexity function IntegrationDetailPage(): JSX.Element { const history = useHistory(); @@ -55,19 +61,8 @@ function IntegrationDetailPage(): JSX.Element { ), ); - if ( - integrationId === INTEGRATION_TYPES.AWS || - integrationId === INTEGRATION_TYPES.AZURE - ) { - return ( - - ); + if (integrationId && cloudIntegrationTypeById[integrationId]) { + return ; } return ( diff --git a/frontend/src/container/Integrations/constants.ts b/frontend/src/container/Integrations/constants.ts index 8645bcf9a0..bc28506636 100644 --- a/frontend/src/container/Integrations/constants.ts +++ b/frontend/src/container/Integrations/constants.ts @@ -1,7 +1,8 @@ import awsDarkLogo from '@/assets/Logos/aws-dark.svg'; import azureOpenaiLogo from '@/assets/Logos/azure-openai.svg'; +import gcpLogo from '@/assets/Logos/gcp.svg'; -import { AzureRegion } from './types'; +import { AzureRegion, GCPRegion } from './types'; export const INTEGRATION_TELEMETRY_EVENTS = { INTEGRATIONS_LIST_VISITED: 'Integrations Page: Visited the list page', @@ -21,6 +22,7 @@ export const INTEGRATION_TELEMETRY_EVENTS = { export const INTEGRATION_TYPES = { AWS: 'aws', AZURE: 'azure', + GCP: 'gcp', }; export const AWS_INTEGRATION = { @@ -53,7 +55,26 @@ export const AZURE_INTEGRATION = { is_new: true, }; -export const ONE_CLICK_INTEGRATIONS = [AWS_INTEGRATION, AZURE_INTEGRATION]; +export const GCP_INTEGRATION = { + id: INTEGRATION_TYPES.GCP, + title: 'Google Cloud Platform', + description: 'Setup for GCP monitoring with SigNoz', + author: { + name: 'SigNoz', + email: 'integrations@signoz.io', + homepage: 'https://signoz.io', + }, + icon: gcpLogo, + icon_alt: 'gcp-logo', + is_installed: false, + is_new: true, +}; + +export const ONE_CLICK_INTEGRATIONS = [ + AWS_INTEGRATION, + AZURE_INTEGRATION, + GCP_INTEGRATION, +]; export const AZURE_REGIONS: AzureRegion[] = [ { @@ -165,3 +186,66 @@ export const AZURE_REGIONS: AzureRegion[] = [ { label: 'West US 2', value: 'westus2', geography: 'United States' }, { label: 'West US 3', value: 'westus3', geography: 'United States' }, ]; + +// Source of truth: pkg/types/cloudintegrationtypes/regions.go (GCP regions). +export const GCP_REGIONS: GCPRegion[] = [ + { label: 'Johannesburg', value: 'africa-south1', geography: 'Africa' }, + { label: 'Changhua County', value: 'asia-east1', geography: 'APAC' }, + { label: 'Hong Kong', value: 'asia-east2', geography: 'APAC' }, + { label: 'Tokyo', value: 'asia-northeast1', geography: 'APAC' }, + { label: 'Osaka', value: 'asia-northeast2', geography: 'APAC' }, + { label: 'Seoul', value: 'asia-northeast3', geography: 'APAC' }, + { label: 'Mumbai', value: 'asia-south1', geography: 'APAC' }, + { label: 'Delhi', value: 'asia-south2', geography: 'APAC' }, + { label: 'Singapore', value: 'asia-southeast1', geography: 'APAC' }, + { label: 'Jakarta', value: 'asia-southeast2', geography: 'APAC' }, + { label: 'Bangkok', value: 'asia-southeast3', geography: 'APAC' }, + { label: 'Sydney', value: 'australia-southeast1', geography: 'APAC' }, + { label: 'Melbourne', value: 'australia-southeast2', geography: 'APAC' }, + { label: 'Warsaw', value: 'europe-central2', geography: 'Europe' }, + { label: 'Hamina', value: 'europe-north1', geography: 'Europe' }, + { label: 'Stockholm', value: 'europe-north2', geography: 'Europe' }, + { label: 'Madrid', value: 'europe-southwest1', geography: 'Europe' }, + { label: 'St. Ghislain', value: 'europe-west1', geography: 'Europe' }, + { label: 'London', value: 'europe-west2', geography: 'Europe' }, + { label: 'Frankfurt', value: 'europe-west3', geography: 'Europe' }, + { label: 'Eemshaven', value: 'europe-west4', geography: 'Europe' }, + { label: 'Zurich', value: 'europe-west6', geography: 'Europe' }, + { label: 'Milan', value: 'europe-west8', geography: 'Europe' }, + { label: 'Paris', value: 'europe-west9', geography: 'Europe' }, + { label: 'Berlin', value: 'europe-west10', geography: 'Europe' }, + { label: 'Turin', value: 'europe-west12', geography: 'Europe' }, + { label: 'Doha', value: 'me-central1', geography: 'Middle East' }, + { label: 'Dammam', value: 'me-central2', geography: 'Middle East' }, + { label: 'Tel Aviv', value: 'me-west1', geography: 'Middle East' }, + { + label: 'Montréal', + value: 'northamerica-northeast1', + geography: 'North America', + }, + { + label: 'Toronto', + value: 'northamerica-northeast2', + geography: 'North America', + }, + { + label: 'Querétaro', + value: 'northamerica-south1', + geography: 'North America', + }, + { + label: 'São Paulo', + value: 'southamerica-east1', + geography: 'South America', + }, + { label: 'Santiago', value: 'southamerica-west1', geography: 'South America' }, + { label: 'Council Bluffs', value: 'us-central1', geography: 'North America' }, + { label: 'Moncks Corner', value: 'us-east1', geography: 'North America' }, + { label: 'Ashburn', value: 'us-east4', geography: 'North America' }, + { label: 'Columbus', value: 'us-east5', geography: 'North America' }, + { label: 'Dallas', value: 'us-south1', geography: 'North America' }, + { label: 'The Dalles', value: 'us-west1', geography: 'North America' }, + { label: 'Los Angeles', value: 'us-west2', geography: 'North America' }, + { label: 'Salt Lake City', value: 'us-west3', geography: 'North America' }, + { label: 'Las Vegas', value: 'us-west4', geography: 'North America' }, +]; diff --git a/frontend/src/container/Integrations/types.ts b/frontend/src/container/Integrations/types.ts index 9e29a1a087..aa154898b3 100644 --- a/frontend/src/container/Integrations/types.ts +++ b/frontend/src/container/Integrations/types.ts @@ -6,6 +6,7 @@ import { export enum IntegrationType { AWS_SERVICES = 'aws', AZURE_SERVICES = 'azure', + GCP_SERVICES = 'gcp', } interface LogField { @@ -87,7 +88,10 @@ export interface ServiceData { export interface CloudAccount { id: string; cloud_account_id: string; - config: AzureCloudAccountConfig | AWSCloudAccountConfig; + config: + | AzureCloudAccountConfig + | AWSCloudAccountConfig + | GCPCloudAccountConfig; status: AccountStatus | IServiceStatus; providerAccountId: string; } @@ -97,6 +101,12 @@ export interface AzureCloudAccountConfig { resource_groups: string[]; } +export interface GCPCloudAccountConfig { + deployment_region: string; + deployment_project_id: string; + project_ids: string[]; +} + export interface AccountStatus { integration: IntegrationStatus; } @@ -111,6 +121,12 @@ export interface AzureRegion { value: string; } +export interface GCPRegion { + label: string; + geography: string; + value: string; +} + export interface UpdateServiceConfigPayload { cloud_account_id: string; config: AzureServicesConfig; diff --git a/frontend/src/hooks/integration/azure/useAccountSettingsModal.ts b/frontend/src/hooks/integration/azure/useAccountSettingsModal.ts index 25eb705b41..7ffb9af58a 100644 --- a/frontend/src/hooks/integration/azure/useAccountSettingsModal.ts +++ b/frontend/src/hooks/integration/azure/useAccountSettingsModal.ts @@ -39,8 +39,12 @@ export function useAccountSettingsModal({ }: UseAccountSettingsModalProps): UseAccountSettingsModal { const [form] = Form.useForm(); const { mutate: updateAccount, isLoading } = useUpdateAccount(); + // `account.config` is the shared per-provider union (Azure | AWS | GCP). + // Narrow to Azure by `resource_groups` (Azure-only) rather than + // `deployment_region`, which GCP also has — so it no longer identifies + // Azure uniquely. const accountConfig = useMemo( - () => ('deployment_region' in account.config ? account.config : null), + () => ('resource_groups' in account.config ? account.config : null), [account.config], ); const [resourceGroups, setResourceGroups] = useState(