mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: resolve stopWorkspaceIfRunning common path (#25639)
Closes #24333 This was a common setup in `updateWorkspace()` but was not appropriately ported to `changeWorkspaceVersion()`. Some tests have been added also to ensure this works 🙂 Simple smooth and easy.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
MockProvisionerJob,
|
||||
MockStoppedWorkspace,
|
||||
MockTemplate,
|
||||
MockTemplateVersion2,
|
||||
@@ -275,6 +276,102 @@ describe("api.ts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeWorkspaceVersion", () => {
|
||||
it("stops workspace before changing version if running", async () => {
|
||||
vi.spyOn(API, "stopWorkspace").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
transition: "stop",
|
||||
});
|
||||
vi.spyOn(API, "waitForBuild").mockResolvedValueOnce({
|
||||
...MockProvisionerJob,
|
||||
status: "succeeded",
|
||||
});
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce(
|
||||
[],
|
||||
);
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
template_version_id: MockTemplateVersion2.id,
|
||||
transition: "start",
|
||||
});
|
||||
|
||||
await API.changeWorkspaceVersion(MockWorkspace, MockTemplateVersion2.id);
|
||||
|
||||
expect(API.stopWorkspace).toHaveBeenCalledWith(MockWorkspace.id);
|
||||
expect(API.postWorkspaceBuild).toHaveBeenCalledWith(MockWorkspace.id, {
|
||||
transition: "start",
|
||||
template_version_id: MockTemplateVersion2.id,
|
||||
rich_parameter_values: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not stop workspace if already stopped", async () => {
|
||||
vi.spyOn(API, "stopWorkspace");
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce(
|
||||
[],
|
||||
);
|
||||
vi.spyOn(API, "postWorkspaceBuild").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
template_version_id: MockTemplateVersion2.id,
|
||||
transition: "start",
|
||||
});
|
||||
|
||||
await API.changeWorkspaceVersion(
|
||||
MockStoppedWorkspace,
|
||||
MockTemplateVersion2.id,
|
||||
);
|
||||
|
||||
expect(API.stopWorkspace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects if stop is canceled", async () => {
|
||||
vi.spyOn(API, "stopWorkspace").mockResolvedValueOnce({
|
||||
...MockWorkspaceBuild,
|
||||
transition: "stop",
|
||||
});
|
||||
vi.spyOn(API, "waitForBuild").mockResolvedValueOnce({
|
||||
...MockProvisionerJob,
|
||||
status: "canceled",
|
||||
});
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce(
|
||||
[],
|
||||
);
|
||||
vi.spyOn(API, "postWorkspaceBuild");
|
||||
|
||||
await expect(
|
||||
API.changeWorkspaceVersion(MockWorkspace, MockTemplateVersion2.id),
|
||||
).rejects.toThrow("Workspace stop was canceled");
|
||||
expect(API.postWorkspaceBuild).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throws MissingBuildParameters for missing params", async () => {
|
||||
vi.spyOn(API, "getWorkspaceBuildParameters").mockResolvedValueOnce([]);
|
||||
vi.spyOn(API, "getTemplateVersionRichParameters").mockResolvedValueOnce([
|
||||
MockTemplateVersionParameter1,
|
||||
{ ...MockTemplateVersionParameter2, mutable: false },
|
||||
]);
|
||||
|
||||
let error = new Error();
|
||||
try {
|
||||
await API.changeWorkspaceVersion(
|
||||
MockStoppedWorkspace,
|
||||
MockTemplateVersion2.id,
|
||||
);
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
|
||||
expect(error).toBeInstanceOf(MissingBuildParameters);
|
||||
expect((error as MissingBuildParameters).parameters).toEqual([
|
||||
MockTemplateVersionParameter1,
|
||||
{ ...MockTemplateVersionParameter2, mutable: false },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("chat configuration endpoints", () => {
|
||||
it.each<[string, () => Promise<unknown>, unknown]>([
|
||||
[
|
||||
|
||||
+23
-13
@@ -2498,6 +2498,24 @@ class ApiMethods {
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops a workspace if it is currently running and waits for the stop
|
||||
* to complete. Throws if the stop build is canceled.
|
||||
*/
|
||||
private stopWorkspaceIfRunning = async (
|
||||
workspace: TypesGen.Workspace,
|
||||
): Promise<void> => {
|
||||
// Workspace is already in a state where it's "stopped".
|
||||
if (workspace.latest_build.status !== "running") return;
|
||||
|
||||
const stopBuild = await this.stopWorkspace(workspace.id);
|
||||
const awaitedStopBuild = await this.waitForBuild(stopBuild);
|
||||
|
||||
if (awaitedStopBuild?.status === "canceled") {
|
||||
throw new Error("Workspace stop was canceled.");
|
||||
}
|
||||
};
|
||||
|
||||
/** Steps to change the workspace version
|
||||
* - Get the latest template to access the latest active version
|
||||
* - Get the current build parameters
|
||||
@@ -2505,6 +2523,7 @@ class ApiMethods {
|
||||
* - Update the build parameters and check if there are missed parameters for
|
||||
* the new version
|
||||
* - If there are missing parameters raise an error
|
||||
* - Stop the workspace if it is already running
|
||||
* - Create a build with the version and updated build parameters
|
||||
*/
|
||||
changeWorkspaceVersion = async (
|
||||
@@ -2539,6 +2558,8 @@ class ApiMethods {
|
||||
throw new MissingBuildParameters(missingParameters, templateVersionId);
|
||||
}
|
||||
|
||||
await this.stopWorkspaceIfRunning(workspace);
|
||||
|
||||
return this.postWorkspaceBuild(workspace.id, {
|
||||
transition: "start",
|
||||
template_version_id: templateVersionId,
|
||||
@@ -2553,7 +2574,7 @@ class ApiMethods {
|
||||
* - Update the build parameters and check if there are missed parameters for
|
||||
* the newest version
|
||||
* - If there are missing parameters raise an error
|
||||
* - Stop the workspace with the current template version if it is already running
|
||||
* - Stop the workspace if it is already running
|
||||
* - Create a build with the latest version and updated build parameters
|
||||
*/
|
||||
updateWorkspace = async (
|
||||
@@ -2585,18 +2606,7 @@ class ApiMethods {
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the workspace if it is already running.
|
||||
if (workspace.latest_build.status === "running") {
|
||||
const stopBuild = await this.stopWorkspace(workspace.id);
|
||||
const awaitedStopBuild = await this.waitForBuild(stopBuild);
|
||||
// If the stop is canceled halfway through, we bail.
|
||||
// This is the same behaviour as restartWorkspace.
|
||||
if (awaitedStopBuild?.status === "canceled") {
|
||||
return Promise.reject(
|
||||
new Error("Workspace stop was canceled, not proceeding with update."),
|
||||
);
|
||||
}
|
||||
}
|
||||
await this.stopWorkspaceIfRunning(workspace);
|
||||
|
||||
try {
|
||||
return await this.postWorkspaceBuild(workspace.id, {
|
||||
|
||||
Reference in New Issue
Block a user