Skip to content

Commit

Permalink
fix(operations) use events stream for all long-running operations
Browse files Browse the repository at this point in the history
WD-6846
  • Loading branch information
lorumic authored and edlerd committed Oct 25, 2023
1 parent ece5c7e commit e1bbb19
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 250 deletions.
18 changes: 9 additions & 9 deletions src/api/images.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { TIMEOUT_300, watchOperation } from "./operations";
import { handleResponse } from "util/helpers";
import { ImportImage, LxdImage } from "types/image";
import { LxdApiResponse } from "types/apiResponse";
Expand All @@ -25,20 +24,23 @@ export const fetchImageList = (project: string): Promise<LxdImage[]> => {
});
};

export const deleteImage = (image: LxdImage, project: string) => {
export const deleteImage = (
image: LxdImage,
project: string,
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(`/1.0/images/${image.fingerprint}?project=${project}`, {
method: "DELETE",
})
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};

export const importImage = (remoteImage: ImportImage) => {
export const importImage = (
remoteImage: ImportImage,
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch("/1.0/images", {
method: "POST",
Expand All @@ -54,9 +56,7 @@ export const importImage = (remoteImage: ImportImage) => {
}),
})
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation, TIMEOUT_300).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
47 changes: 9 additions & 38 deletions src/api/instances.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {
continueOrFinish,
handleEtagResponse,
handleResponse,
handleTextResponse,
pushFailure,
pushSuccess,
} from "util/helpers";
import { LxdInstance, LxdInstanceAction } from "types/instance";
import { LxdTerminal, TerminalConnectPayload } from "types/terminal";
Expand Down Expand Up @@ -152,7 +155,6 @@ export const updateInstanceBulkAction = (
isForce: boolean,
eventQueue: EventQueue,
): Promise<PromiseSettledResult<void>[]> => {
let remainingResults = actions.length;
const results: PromiseSettledResult<void>[] = [];
return new Promise((resolve) => {
void Promise.allSettled(
Expand All @@ -161,24 +163,9 @@ export const updateInstanceBulkAction = (
(operation) => {
eventQueue.set(
operation.metadata.id,
() => {
results.push({
status: "fulfilled",
value: undefined,
});
},
(msg) => {
results.push({
status: "rejected",
reason: msg,
});
},
() => {
remainingResults--;
if (remainingResults === 0) {
resolve(results);
}
},
() => pushSuccess(results),
(msg) => pushFailure(results, msg),
() => continueOrFinish(results, actions.length, resolve),
);
},
);
Expand All @@ -204,32 +191,16 @@ export const deleteInstanceBulk = (
instances: LxdInstance[],
eventQueue: EventQueue,
): Promise<PromiseSettledResult<void>[]> => {
let remainingResults = instances.length;
const results: PromiseSettledResult<void>[] = [];
return new Promise((resolve) => {
void Promise.allSettled(
instances.map(async (instance) => {
return await deleteInstance(instance).then((operation) => {
eventQueue.set(
operation.metadata.id,
() => {
results.push({
status: "fulfilled",
value: undefined,
});
},
(msg) => {
results.push({
status: "rejected",
reason: msg,
});
},
() => {
remainingResults--;
if (remainingResults === 0) {
resolve(results);
}
},
() => pushSuccess(results),
(msg) => pushFailure(results, msg),
() => continueOrFinish(results, instances.length, resolve),
);
});
}),
Expand Down
39 changes: 1 addition & 38 deletions src/api/operations.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,7 @@
import { handleResponse } from "util/helpers";
import {
LxdOperation,
LxdOperationList,
LxdOperationResponse,
} from "types/operation";
import { LxdOperation, LxdOperationList } from "types/operation";
import { LxdApiResponse } from "types/apiResponse";

export const TIMEOUT_300 = 300;
export const TIMEOUT_120 = 120;
export const TIMEOUT_60 = 60;
export const TIMEOUT_10 = 10;

export const watchOperation = (
operationUrl: string,
timeout = TIMEOUT_10,
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
const operationParts = operationUrl.split("?");
const baseUrl = operationParts[0];
const queryString = operationParts.length === 1 ? "" : operationParts[1];
fetch(`${baseUrl}/wait?timeout=${timeout}&${queryString}`)
.then(handleResponse)
.then((data: LxdOperationResponse) => {
if (data.metadata.status === "Success") {
return resolve(data);
}
if (data.metadata.status === "Running") {
throw Error(
"Timeout while waiting for the operation to succeed. Watched operation continues in the background.",
);
} else if (data.metadata.status === "Cancelled") {
throw new Error("Cancelled");
} else {
throw Error(data.metadata.err);
}
})
.catch(reject);
});
};

const sortOperationList = (operations: LxdOperationList) => {
const newestFirst = (a: LxdOperation, b: LxdOperation) => {
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
Expand Down
10 changes: 5 additions & 5 deletions src/api/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { handleEtagResponse, handleResponse } from "util/helpers";
import { LxdProject } from "types/project";
import { LxdApiResponse } from "types/apiResponse";
import { LxdOperationResponse } from "types/operation";
import { TIMEOUT_60, watchOperation } from "api/operations";

export const fetchProjects = (recursion: number): Promise<LxdProject[]> => {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -49,7 +48,10 @@ export const updateProject = (project: LxdProject) => {
});
};

export const renameProject = (oldName: string, newName: string) => {
export const renameProject = (
oldName: string,
newName: string,
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(`/1.0/projects/${oldName}`, {
method: "POST",
Expand All @@ -58,9 +60,7 @@ export const renameProject = (oldName: string, newName: string) => {
}),
})
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation, TIMEOUT_60).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
Expand Down
64 changes: 33 additions & 31 deletions src/api/snapshots.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { TIMEOUT_120, TIMEOUT_60, watchOperation } from "./operations";
import { handleResponse } from "util/helpers";
import {
continueOrFinish,
handleResponse,
pushFailure,
pushSuccess,
} from "util/helpers";
import { LxdInstance, LxdSnapshot } from "types/instance";
import { LxdOperationResponse } from "types/operation";
import { EventQueue } from "context/eventQueue";

export const createSnapshot = (
instance: LxdInstance,
name: string,
expiresAt: string | null,
stateful: boolean,
) => {
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(
`/1.0/instances/${instance.name}/snapshots?project=${instance.project}`,
Expand All @@ -22,17 +27,15 @@ export const createSnapshot = (
},
)
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation, TIMEOUT_60).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};

export const deleteSnapshot = (
instance: LxdInstance,
snapshot: { name: string },
) => {
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(
`/1.0/instances/${instance.name}/snapshots/${snapshot.name}?project=${instance.project}`,
Expand All @@ -41,33 +44,38 @@ export const deleteSnapshot = (
},
)
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation, TIMEOUT_120).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};

export const deleteSnapshotBulk = (
instance: LxdInstance,
snapshotNames: string[],
) => {
return new Promise((resolve, reject) => {
Promise.all(
snapshotNames.map(
async (name) => await deleteSnapshot(instance, { name }),
),
)
.then(resolve)
.catch(reject);
eventQueue: EventQueue,
): Promise<PromiseSettledResult<void>[]> => {
const results: PromiseSettledResult<void>[] = [];
return new Promise((resolve) => {
void Promise.allSettled(
snapshotNames.map(async (name) => {
return await deleteSnapshot(instance, { name }).then((operation) => {
eventQueue.set(
operation.metadata.id,
() => pushSuccess(results),
(msg) => pushFailure(results, msg),
() => continueOrFinish(results, snapshotNames.length, resolve),
);
});
}),
);
});
};

export const restoreSnapshot = (
instance: LxdInstance,
snapshot: LxdSnapshot,
restoreState: boolean,
) => {
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(`/1.0/instances/${instance.name}?project=${instance.project}`, {
method: "PUT",
Expand All @@ -77,9 +85,7 @@ export const restoreSnapshot = (
}),
})
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
Expand All @@ -88,7 +94,7 @@ export const renameSnapshot = (
instance: LxdInstance,
snapshot: LxdSnapshot,
newName: string,
) => {
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(
`/1.0/instances/${instance.name}/snapshots/${snapshot.name}?project=${instance.project}`,
Expand All @@ -100,9 +106,7 @@ export const renameSnapshot = (
},
)
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
Expand All @@ -111,7 +115,7 @@ export const updateSnapshot = (
instance: LxdInstance,
snapshot: LxdSnapshot,
expiresAt: string,
) => {
): Promise<LxdOperationResponse> => {
return new Promise((resolve, reject) => {
fetch(
`/1.0/instances/${instance.name}/snapshots/${snapshot.name}?project=${instance.project}`,
Expand All @@ -123,9 +127,7 @@ export const updateSnapshot = (
},
)
.then(handleResponse)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
5 changes: 1 addition & 4 deletions src/api/storage-pools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from "types/storage";
import { LxdApiResponse } from "types/apiResponse";
import { LxdOperationResponse } from "types/operation";
import { TIMEOUT_300, watchOperation } from "api/operations";
import axios, { AxiosResponse } from "axios";

export const fetchStoragePool = (
Expand Down Expand Up @@ -199,9 +198,7 @@ export const createIsoStorageVolume = (
},
)
.then((response: AxiosResponse<LxdOperationResponse>) => response.data)
.then((data: LxdOperationResponse) => {
watchOperation(data.operation, TIMEOUT_300).then(resolve).catch(reject);
})
.then(resolve)
.catch(reject);
});
};
Expand Down
Loading

0 comments on commit e1bbb19

Please sign in to comment.