mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 15:28:25 +00:00
feat: finished integration sync for rundeck
This commit is contained in:
@@ -661,6 +661,7 @@ export const INTEGRATION = {
|
|||||||
targetServiceId:
|
targetServiceId:
|
||||||
"The service based grouping identifier ID of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
|
"The service based grouping identifier ID of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank",
|
||||||
owner: "External integration providers service entity owner. Used in Github.",
|
owner: "External integration providers service entity owner. Used in Github.",
|
||||||
|
url: "The self-hosted URL of the platform to integrate with",
|
||||||
path: "Path to save the synced secrets. Used by Gitlab, AWS Parameter Store, Vault",
|
path: "Path to save the synced secrets. Used by Gitlab, AWS Parameter Store, Vault",
|
||||||
region: "AWS region to sync secrets to.",
|
region: "AWS region to sync secrets to.",
|
||||||
scope: "Scope of the provider. Used by Github, Qovery",
|
scope: "Scope of the provider. Used by Github, Qovery",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
targetService: z.string().trim().optional().describe(INTEGRATION.CREATE.targetService),
|
targetService: z.string().trim().optional().describe(INTEGRATION.CREATE.targetService),
|
||||||
targetServiceId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetServiceId),
|
targetServiceId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetServiceId),
|
||||||
owner: z.string().trim().optional().describe(INTEGRATION.CREATE.owner),
|
owner: z.string().trim().optional().describe(INTEGRATION.CREATE.owner),
|
||||||
|
url: z.string().trim().optional().describe(INTEGRATION.CREATE.url),
|
||||||
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
||||||
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
||||||
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ export const integrationAuthServiceFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
namespace,
|
namespace,
|
||||||
integration,
|
integration,
|
||||||
|
url,
|
||||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||||
keyEncoding: SecretKeyEncoding.UTF8,
|
keyEncoding: SecretKeyEncoding.UTF8,
|
||||||
...(integration === Integrations.GCP_SECRET_MANAGER
|
...(integration === Integrations.GCP_SECRET_MANAGER
|
||||||
|
|||||||
@@ -3355,6 +3355,74 @@ const syncSecretsHasuraCloud = async ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Sync/push [secrets] to Rundeck
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {TIntegrations} obj.integration - integration details
|
||||||
|
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||||
|
* @param {String} obj.accessToken - access token for Rundeck integration
|
||||||
|
*/
|
||||||
|
const syncSecretsRundeck = async ({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken
|
||||||
|
}: {
|
||||||
|
integration: TIntegrations;
|
||||||
|
secrets: Record<string, { value: string; comment?: string }>;
|
||||||
|
accessToken: string;
|
||||||
|
}) => {
|
||||||
|
interface RundeckSecretResource {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
interface RundeckSecretsGetRes {
|
||||||
|
resources: RundeckSecretResource[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingRundeckSecrets: string[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const listResult = await request.get<RundeckSecretsGetRes>(
|
||||||
|
`${integration.url}/api/44/storage/${integration.path}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"X-Rundeck-Auth-Token": accessToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
existingRundeckSecrets = listResult.data.resources.map((res) => res.name);
|
||||||
|
} catch (err) {
|
||||||
|
logger.info("No existing rundeck secrets");
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const [key, value] of Object.entries(secrets)) {
|
||||||
|
if (existingRundeckSecrets.includes(key)) {
|
||||||
|
await request.put(`${integration.url}/api/44/storage/${integration.path}/${key}`, value, {
|
||||||
|
headers: {
|
||||||
|
"X-Rundeck-Auth-Token": accessToken,
|
||||||
|
"Content-Type": "application/x-rundeck-data-password"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await request.post(`${integration.url}/api/44/storage/${integration.path}/${key}`, value, {
|
||||||
|
headers: {
|
||||||
|
"X-Rundeck-Auth-Token": accessToken,
|
||||||
|
"Content-Type": "application/x-rundeck-data-password"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const existingSecret of existingRundeckSecrets) {
|
||||||
|
if (!(existingSecret in secrets)) {
|
||||||
|
await request.delete(`${integration.url}/api/44/storage/${integration.path}/${existingSecret}`, {
|
||||||
|
headers: {
|
||||||
|
"X-Rundeck-Auth-Token": accessToken
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to [app] in integration named [integration]
|
* Sync/push [secrets] to [app] in integration named [integration]
|
||||||
*
|
*
|
||||||
@@ -3621,6 +3689,13 @@ export const syncIntegrationSecrets = async ({
|
|||||||
accessToken
|
accessToken
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case Integrations.RUNDECK:
|
||||||
|
await syncSecretsRundeck({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new BadRequestError({ message: "Invalid integration" });
|
throw new BadRequestError({ message: "Invalid integration" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const integrationServiceFactory = ({
|
|||||||
scope,
|
scope,
|
||||||
actorId,
|
actorId,
|
||||||
region,
|
region,
|
||||||
|
url,
|
||||||
isActive,
|
isActive,
|
||||||
metadata,
|
metadata,
|
||||||
secretPath,
|
secretPath,
|
||||||
@@ -87,6 +88,7 @@ export const integrationServiceFactory = ({
|
|||||||
region,
|
region,
|
||||||
scope,
|
scope,
|
||||||
owner,
|
owner,
|
||||||
|
url,
|
||||||
appId,
|
appId,
|
||||||
path,
|
path,
|
||||||
app,
|
app,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export type TCreateIntegrationDTO = {
|
|||||||
targetService?: string;
|
targetService?: string;
|
||||||
targetServiceId?: string;
|
targetServiceId?: string;
|
||||||
owner?: string;
|
owner?: string;
|
||||||
|
url?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
scope?: string;
|
scope?: string;
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ const integrationSlugNameMapping: Mapping = {
|
|||||||
northflank: "Northflank",
|
northflank: "Northflank",
|
||||||
windmill: "Windmill",
|
windmill: "Windmill",
|
||||||
"gcp-secret-manager": "GCP Secret Manager",
|
"gcp-secret-manager": "GCP Secret Manager",
|
||||||
"hasura-cloud": "Hasura Cloud"
|
"hasura-cloud": "Hasura Cloud",
|
||||||
|
rundeck: "Rundeck"
|
||||||
};
|
};
|
||||||
|
|
||||||
const envMapping: Mapping = {
|
const envMapping: Mapping = {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export type IntegrationAuth = {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
algorithm: string;
|
algorithm: string;
|
||||||
keyEncoding: string;
|
keyEncoding: string;
|
||||||
|
url?: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const useCreateIntegration = () => {
|
|||||||
owner,
|
owner,
|
||||||
path,
|
path,
|
||||||
region,
|
region,
|
||||||
|
url,
|
||||||
scope,
|
scope,
|
||||||
secretPath,
|
secretPath,
|
||||||
metadata
|
metadata
|
||||||
@@ -56,6 +57,7 @@ export const useCreateIntegration = () => {
|
|||||||
targetService?: string;
|
targetService?: string;
|
||||||
targetServiceId?: string;
|
targetServiceId?: string;
|
||||||
owner?: string;
|
owner?: string;
|
||||||
|
url?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
scope?: string;
|
scope?: string;
|
||||||
@@ -85,6 +87,7 @@ export const useCreateIntegration = () => {
|
|||||||
targetEnvironmentId,
|
targetEnvironmentId,
|
||||||
targetService,
|
targetService,
|
||||||
targetServiceId,
|
targetServiceId,
|
||||||
|
url,
|
||||||
owner,
|
owner,
|
||||||
path,
|
path,
|
||||||
scope,
|
scope,
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ export default function RundeckCreateIntegrationPage() {
|
|||||||
watch,
|
watch,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<TFormSchema>({
|
} = useForm<TFormSchema>({
|
||||||
resolver: zodResolver(schema)
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
secretPath: "/"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
@@ -60,6 +63,7 @@ export default function RundeckCreateIntegrationPage() {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
path: keyStoragePath,
|
path: keyStoragePath,
|
||||||
sourceEnvironment,
|
sourceEnvironment,
|
||||||
|
url: integrationAuth.url,
|
||||||
secretPath
|
secretPath
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -158,7 +162,7 @@ export default function RundeckCreateIntegrationPage() {
|
|||||||
placeholder={`keys/project/${workspace.name
|
placeholder={`keys/project/${workspace.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
|
.replace(/ /g, "-")}/${selectedSourceEnvironment}`}
|
||||||
value={field.value}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+3
-2
@@ -141,7 +141,8 @@ export const IntegrationsSection = ({
|
|||||||
label={
|
label={
|
||||||
(integration.integration === "qovery" && integration?.scope) ||
|
(integration.integration === "qovery" && integration?.scope) ||
|
||||||
(integration.integration === "aws-secret-manager" && "Secret") ||
|
(integration.integration === "aws-secret-manager" && "Secret") ||
|
||||||
(integration.integration === "aws-parameter-store" && "Path") ||
|
(["aws-parameter-store", "rundeck"].includes(integration.integration) &&
|
||||||
|
"Path") ||
|
||||||
(integration?.integration === "terraform-cloud" && "Project") ||
|
(integration?.integration === "terraform-cloud" && "Project") ||
|
||||||
(integration?.scope === "github-org" && "Organization") ||
|
(integration?.scope === "github-org" && "Organization") ||
|
||||||
(["github-repo", "github-env"].includes(integration?.scope as string) &&
|
(["github-repo", "github-env"].includes(integration?.scope as string) &&
|
||||||
@@ -153,7 +154,7 @@ export const IntegrationsSection = ({
|
|||||||
{(integration.integration === "hashicorp-vault" &&
|
{(integration.integration === "hashicorp-vault" &&
|
||||||
`${integration.app} - path: ${integration.path}`) ||
|
`${integration.app} - path: ${integration.path}`) ||
|
||||||
(integration.scope === "github-org" && `${integration.owner}`) ||
|
(integration.scope === "github-org" && `${integration.owner}`) ||
|
||||||
(integration.integration === "aws-parameter-store" &&
|
(["aws-parameter-store", "rundeck"].includes(integration.integration) &&
|
||||||
`${integration.path}`) ||
|
`${integration.path}`) ||
|
||||||
(integration.scope?.startsWith("github-") &&
|
(integration.scope?.startsWith("github-") &&
|
||||||
`${integration.owner}/${integration.app}`) ||
|
`${integration.owner}/${integration.app}`) ||
|
||||||
|
|||||||
Reference in New Issue
Block a user