mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 10:26:16 +00:00
Improve render api usage
This commit is contained in:
@@ -10,20 +10,24 @@ import { TRenderSecret, TRenderSyncWithCredentials } from "./render-sync-types";
|
|||||||
|
|
||||||
const MAX_RETRIES = 5;
|
const MAX_RETRIES = 5;
|
||||||
|
|
||||||
const sleep = async () =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
const retrySleep = async () =>
|
const retrySleep = async () =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
setTimeout(resolve, 60000);
|
setTimeout(resolve, 60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getRenderEnvironmentSecrets = async (
|
const makeRequestWithRetry = async <T>(requestFn: () => Promise<T>, attempt = 0): Promise<T> => {
|
||||||
secretSync: TRenderSyncWithCredentials,
|
try {
|
||||||
attempt = 0
|
return await requestFn();
|
||||||
): Promise<TRenderSecret[]> => {
|
} catch (error) {
|
||||||
|
if (isAxiosError(error) && error.response?.status === 429 && attempt < MAX_RETRIES) {
|
||||||
|
await retrySleep();
|
||||||
|
return await makeRequestWithRetry(requestFn, attempt + 1);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRenderEnvironmentSecrets = async (secretSync: TRenderSyncWithCredentials): Promise<TRenderSecret[]> => {
|
||||||
const {
|
const {
|
||||||
destinationConfig,
|
destinationConfig,
|
||||||
connection: {
|
connection: {
|
||||||
@@ -38,8 +42,8 @@ const getRenderEnvironmentSecrets = async (
|
|||||||
do {
|
do {
|
||||||
const url = cursor ? `${baseUrl}?cursor=${cursor}` : baseUrl;
|
const url = cursor ? `${baseUrl}?cursor=${cursor}` : baseUrl;
|
||||||
|
|
||||||
try {
|
const { data } = await makeRequestWithRetry(() =>
|
||||||
const { data } = await request.get<
|
request.get<
|
||||||
{
|
{
|
||||||
envVar: {
|
envVar: {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -52,32 +56,29 @@ const getRenderEnvironmentSecrets = async (
|
|||||||
Authorization: `Bearer ${apiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
Accept: "application/json"
|
Accept: "application/json"
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const secrets = data.map((item) => ({
|
const secrets = data.map((item) => ({
|
||||||
key: item.envVar.key,
|
key: item.envVar.key,
|
||||||
value: item.envVar.value
|
value: item.envVar.value
|
||||||
}));
|
}));
|
||||||
|
|
||||||
allSecrets.push(...secrets);
|
allSecrets.push(...secrets);
|
||||||
cursor = data[data.length - 1]?.cursor;
|
|
||||||
} catch (error) {
|
if (data.length > 0 && data[data.length - 1]?.cursor) {
|
||||||
if (isAxiosError(error) && error.response?.status === 429 && attempt < MAX_RETRIES) {
|
cursor = data[data.length - 1].cursor;
|
||||||
await retrySleep();
|
} else {
|
||||||
return await getRenderEnvironmentSecrets(secretSync, attempt + 1);
|
cursor = undefined;
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
} while (cursor);
|
} while (cursor);
|
||||||
|
|
||||||
return allSecrets;
|
return allSecrets;
|
||||||
};
|
};
|
||||||
|
|
||||||
const putEnvironmentSecret = async (
|
const batchUpdateEnvironmentSecrets = async (
|
||||||
secretSync: TRenderSyncWithCredentials,
|
secretSync: TRenderSyncWithCredentials,
|
||||||
secretMap: TSecretMap,
|
envVars: Array<{ key: string; value: string }>
|
||||||
key: string,
|
|
||||||
attempt = 0
|
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
destinationConfig,
|
destinationConfig,
|
||||||
@@ -86,92 +87,50 @@ const putEnvironmentSecret = async (
|
|||||||
}
|
}
|
||||||
} = secretSync;
|
} = secretSync;
|
||||||
|
|
||||||
try {
|
await makeRequestWithRetry(() =>
|
||||||
await request.put(
|
request.put(`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars`, envVars, {
|
||||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars/${key}`,
|
headers: {
|
||||||
{
|
Authorization: `Bearer ${apiKey}`,
|
||||||
key,
|
Accept: "application/json"
|
||||||
value: secretMap[key].value
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${apiKey}`,
|
|
||||||
Accept: "application/json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
})
|
||||||
} catch (error) {
|
);
|
||||||
if (isAxiosError(error) && error.response?.status === 429 && attempt < MAX_RETRIES) {
|
|
||||||
await retrySleep();
|
|
||||||
return await putEnvironmentSecret(secretSync, secretMap, key, attempt + 1);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteEnvironmentSecret = async (
|
|
||||||
secretSync: TRenderSyncWithCredentials,
|
|
||||||
secret: Pick<TRenderSecret, "key">,
|
|
||||||
attempt = 0
|
|
||||||
): Promise<void> => {
|
|
||||||
const {
|
|
||||||
destinationConfig,
|
|
||||||
connection: {
|
|
||||||
credentials: { apiKey }
|
|
||||||
}
|
|
||||||
} = secretSync;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await request.delete(
|
|
||||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars/${secret.key}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${apiKey}`,
|
|
||||||
Accept: "application/json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (isAxiosError(error) && error.response?.status === 404) {
|
|
||||||
// If the secret does not exist, we can ignore this error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAxiosError(error) && error.response?.status === 429 && attempt < MAX_RETRIES) {
|
|
||||||
await retrySleep();
|
|
||||||
return await deleteEnvironmentSecret(secretSync, secret, attempt + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RenderSyncFns = {
|
export const RenderSyncFns = {
|
||||||
syncSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
syncSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
||||||
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||||
|
|
||||||
for await (const key of Object.keys(secretMap)) {
|
const finalEnvVars: Array<{ key: string; value: string }> = [];
|
||||||
// If value is empty skip it as render does not allow empty variables
|
|
||||||
if (secretMap[key].value === "") {
|
for (const renderSecret of renderSecrets) {
|
||||||
|
const shouldKeep =
|
||||||
|
secretMap[renderSecret.key] ||
|
||||||
|
(secretSync.syncOptions.disableSecretDeletion &&
|
||||||
|
!matchesSchema(renderSecret.key, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema));
|
||||||
|
|
||||||
|
if (shouldKeep && !secretMap[renderSecret.key]) {
|
||||||
|
finalEnvVars.push({
|
||||||
|
key: renderSecret.key,
|
||||||
|
value: renderSecret.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, secret] of Object.entries(secretMap)) {
|
||||||
|
// Skip empty values as render does not allow empty variables
|
||||||
|
if (secret.value === "") {
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await putEnvironmentSecret(secretSync, secretMap, key);
|
|
||||||
await sleep();
|
finalEnvVars.push({
|
||||||
|
key,
|
||||||
|
value: secret.value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
await batchUpdateEnvironmentSecrets(secretSync, finalEnvVars);
|
||||||
|
|
||||||
for await (const renderSecret of renderSecrets) {
|
|
||||||
if (!matchesSchema(renderSecret.key, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema))
|
|
||||||
// eslint-disable-next-line no-continue
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!secretMap[renderSecret.key]) {
|
|
||||||
await deleteEnvironmentSecret(secretSync, renderSecret);
|
|
||||||
await sleep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getSecrets: async (secretSync: TRenderSyncWithCredentials): Promise<TSecretMap> => {
|
getSecrets: async (secretSync: TRenderSyncWithCredentials): Promise<TSecretMap> => {
|
||||||
@@ -180,13 +139,17 @@ export const RenderSyncFns = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
removeSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
removeSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
||||||
const encryptedSecrets = await getRenderEnvironmentSecrets(secretSync);
|
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||||
|
const finalEnvVars: Array<{ key: string; value: string }> = [];
|
||||||
|
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
for (const renderSecret of renderSecrets) {
|
||||||
if (encryptedSecret.key in secretMap) {
|
if (!(renderSecret.key in secretMap)) {
|
||||||
await deleteEnvironmentSecret(secretSync, encryptedSecret);
|
finalEnvVars.push({
|
||||||
await sleep();
|
key: renderSecret.key,
|
||||||
|
value: renderSecret.value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await batchUpdateEnvironmentSecrets(secretSync, finalEnvVars);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user