From 26148b633ba5c25ee5230ac486c0e8d348441ffa Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Wed, 17 Apr 2024 21:34:54 -0700 Subject: [PATCH] added tags for aws integrations --- backend/src/lib/api-docs/constants.ts | 3 +- .../server/routes/v1/integration-router.ts | 9 +- .../integration-sync-secret.ts | 10 +- .../services/integration/integration-types.ts | 4 + .../cloud/aws-parameter-store.mdx | 3 +- .../integrations/cloud/aws-secret-manager.mdx | 3 +- .../src/hooks/api/integrations/queries.tsx | 4 + .../aws-parameter-store/create.tsx | 184 +++++++++++++----- .../aws-secret-manager/create.tsx | 184 +++++++++++++----- pg-migrator/src/models/integration/types.ts | 4 + 10 files changed, 300 insertions(+), 108 deletions(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 40772a207..c83234c1f 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -589,7 +589,8 @@ export const INTEGRATION = { secretSuffix: "The suffix for the saved secret. Used by GCP", initialSyncBehavoir: "Type of syncing behavoir with the integration", shouldAutoRedeploy: "Used by Render to trigger auto deploy", - secretGCPLabel: "The label for the GCP secrets" + secretGCPLabel: "The label for the GCP secrets", + secretAWSTag: "The tag for the AWS secrets" } }, UPDATE: { diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index cf8fbf41f..58a8917c5 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -56,7 +56,14 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { labelValue: z.string() }) .optional() - .describe(INTEGRATION.CREATE.metadata.secretGCPLabel) + .describe(INTEGRATION.CREATE.metadata.secretGCPLabel), + secretAWSTag: z + .object({ + key: z.string(), + value: z.string() + }) + .optional() + .describe(INTEGRATION.CREATE.metadata.secretAWSTag) }) .optional() }), diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 3273b4b2d..48226bb59 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -457,6 +457,8 @@ const syncSecretsAWSParameterStore = async ({ }); ssm.config.update(config); + const metadata = z.record(z.any()).parse(integration.metadata); + const params = { Path: integration.path as string, Recursive: false, @@ -486,7 +488,8 @@ const syncSecretsAWSParameterStore = async ({ Name: `${integration.path}${key}`, Type: "SecureString", Value: secrets[key].value, - Overwrite: true + // Overwrite: true, + Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : [] }) .promise(); // case: secret exists in AWS parameter store @@ -499,6 +502,7 @@ const syncSecretsAWSParameterStore = async ({ Type: "SecureString", Value: secrets[key].value, Overwrite: true + // Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : [] }) .promise(); } @@ -537,6 +541,7 @@ const syncSecretsAWSSecretManager = async ({ }) => { let secretsManager; const secKeyVal = getSecretKeyValuePair(secrets); + const metadata = z.record(z.any()).parse(integration.metadata); try { if (!accessId) return; @@ -573,7 +578,8 @@ const syncSecretsAWSSecretManager = async ({ await secretsManager.send( new CreateSecretCommand({ Name: integration.app as string, - SecretString: JSON.stringify(secKeyVal) + SecretString: JSON.stringify(secKeyVal), + Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : [] }) ); } diff --git a/backend/src/services/integration/integration-types.ts b/backend/src/services/integration/integration-types.ts index 8f54c4fdb..93c36cd8e 100644 --- a/backend/src/services/integration/integration-types.ts +++ b/backend/src/services/integration/integration-types.ts @@ -22,6 +22,10 @@ export type TCreateIntegrationDTO = { labelName: string; labelValue: string; }; + secretAWSTag?: { + key: string; + value: string; + }; }; } & Omit; diff --git a/docs/integrations/cloud/aws-parameter-store.mdx b/docs/integrations/cloud/aws-parameter-store.mdx index d54458db9..547387996 100644 --- a/docs/integrations/cloud/aws-parameter-store.mdx +++ b/docs/integrations/cloud/aws-parameter-store.mdx @@ -29,7 +29,8 @@ Prerequisites: "ssm:PutParameter", "ssm:DeleteParameter", "ssm:GetParametersByPath", - "ssm:DeleteParameters" + "ssm:DeleteParameters", + "ssm:AddTagsToResource" ], "Resource": "*" } diff --git a/docs/integrations/cloud/aws-secret-manager.mdx b/docs/integrations/cloud/aws-secret-manager.mdx index 0eab1a561..a0187644d 100644 --- a/docs/integrations/cloud/aws-secret-manager.mdx +++ b/docs/integrations/cloud/aws-secret-manager.mdx @@ -28,7 +28,8 @@ Prerequisites: "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:CreateSecret", - "secretsmanager:UpdateSecret" + "secretsmanager:UpdateSecret", + "secretsmanager:TagResource" ], "Resource": "*" } diff --git a/frontend/src/hooks/api/integrations/queries.tsx b/frontend/src/hooks/api/integrations/queries.tsx index 80a2df6fa..7804f0951 100644 --- a/frontend/src/hooks/api/integrations/queries.tsx +++ b/frontend/src/hooks/api/integrations/queries.tsx @@ -63,6 +63,10 @@ export const useCreateIntegration = () => { secretSuffix?: string; initialSyncBehavior?: string; shouldAutoRedeploy?: boolean; + secretAWSTag?: { + key: string; + value: string; + }; }; }) => { const { diff --git a/frontend/src/pages/integrations/aws-parameter-store/create.tsx b/frontend/src/pages/integrations/aws-parameter-store/create.tsx index 23fb210a0..dd643023c 100644 --- a/frontend/src/pages/integrations/aws-parameter-store/create.tsx +++ b/frontend/src/pages/integrations/aws-parameter-store/create.tsx @@ -10,6 +10,7 @@ import { faCircleInfo } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { motion } from "framer-motion"; import queryString from "query-string"; import { useCreateIntegration } from "@app/hooks/api"; @@ -21,11 +22,21 @@ import { FormControl, Input, Select, - SelectItem + SelectItem, + Switch, + Tab, + TabList, + TabPanel, + Tabs } from "../../../components/v2"; import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth"; import { useGetWorkspaceById } from "../../../hooks/api/workspace"; +enum TabSections { + Connection = "connection", + Options = "options" +} + const awsRegions = [ { name: "US East (Ohio)", slug: "us-east-2" }, { name: "US East (N. Virginia)", slug: "us-east-1" }, @@ -76,6 +87,9 @@ export default function AWSParameterStoreCreateIntegrationPage() { const [pathErrorText, setPathErrorText] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [shouldTag, setShouldTag] = useState(false); + const [tagKey, setTagKey] = useState(""); + const [tagValue, setTagValue] = useState(""); useEffect(() => { if (workspace) { @@ -110,7 +124,17 @@ export default function AWSParameterStoreCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, path, region: selectedAWSRegion, - secretPath + secretPath, + metadata: { + ...(shouldTag + ? { + secretAWSTag: { + key: tagKey, + value: tagValue + } + } + : {}) + } }); setIsLoading(false); @@ -157,56 +181,114 @@ export default function AWSParameterStoreCreateIntegrationPage() { - - setSelectedSourceEnvironment(val)} + className="w-full border border-mineshaft-500" + > + {workspace?.environments.map((sourceEnvironment) => ( + + {sourceEnvironment.name} + + ))} + + + + setSecretPath(evt.target.value)} + placeholder="Provide a path, default is /" + /> + + + + + - {sourceEnvironment.name} - - ))} - - - - setSecretPath(evt.target.value)} - placeholder="Provide a path, default is /" - /> - - - - - - setPath(e.target.value)} - /> - + setPath(e.target.value)} + /> + + + + + +
+ setShouldTag(!shouldTag)} + isChecked={shouldTag} + > + Tag in AWS Parameter Store + +
+ {shouldTag && ( +
+ + setTagKey(e.target.value)} + /> + + + setTagValue(e.target.value)} + /> + +
+ )} +
+
+