feat(caprover): display application logs for apps with pending updates
This commit is contained in:
parent
043051cb1c
commit
5c599842f6
@ -21,12 +21,23 @@ const UpdateForm = (application: Application, latestVersions: string[]) => {
|
|||||||
</form>`;
|
</form>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PendingUpdates = (pendingUpdates: UpdateDefinition[]) => {
|
const FreeUpdateForm = (application: Application) => {
|
||||||
|
return html`<form method="POST">
|
||||||
|
<input type="hidden" name="appName" value="${application.name}" />
|
||||||
|
<label for="version">
|
||||||
|
Manual version
|
||||||
|
<input name="version" placeholder="hello-world:latest" type="text" />
|
||||||
|
</label>
|
||||||
|
<button type="submit">Update</button>
|
||||||
|
</form>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PendingUpdates = (pendingUpdates: UpdateDefinition[], logs: string) => {
|
||||||
if (pendingUpdates.length === 0) {
|
if (pendingUpdates.length === 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`<div>
|
return html`<section>
|
||||||
<h2>Pending updates</h2>
|
<h2>Pending updates</h2>
|
||||||
<ul>
|
<ul>
|
||||||
${pendingUpdates
|
${pendingUpdates
|
||||||
@ -35,19 +46,29 @@ const PendingUpdates = (pendingUpdates: UpdateDefinition[]) => {
|
|||||||
})
|
})
|
||||||
.join("")}
|
.join("")}
|
||||||
</ul>
|
</ul>
|
||||||
</div>`;
|
|
||||||
|
<pre>${logs}</pre>
|
||||||
|
<button
|
||||||
|
id="refresh"
|
||||||
|
onclick="window.location.replace('#refresh'); window.location.reload();"
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</section>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Page = (
|
const Page = (
|
||||||
application: Application,
|
application: Application,
|
||||||
latestVersions: string[],
|
latestVersions: string[],
|
||||||
pendingUpdates: UpdateDefinition[]
|
pendingUpdates: UpdateDefinition[],
|
||||||
|
logs: string
|
||||||
) => {
|
) => {
|
||||||
return html`<div>
|
return html`<div>
|
||||||
<h1>Updating ${application.name}</h1>
|
<h1>Updating ${application.name}</h1>
|
||||||
|
|
||||||
${PendingUpdates(pendingUpdates)}
|
${PendingUpdates(pendingUpdates, logs)}
|
||||||
|
|
||||||
|
<section>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Last deployment</dt>
|
<dt>Last deployment</dt>
|
||||||
<dd>${application.lastDeployedAt.toLocaleString("fr-FR")}</dd>
|
<dd>${application.lastDeployedAt.toLocaleString("fr-FR")}</dd>
|
||||||
@ -67,7 +88,10 @@ const Page = (
|
|||||||
? "No version found"
|
? "No version found"
|
||||||
: UpdateForm(application, latestVersions)}
|
: UpdateForm(application, latestVersions)}
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt>Manual update</dt>
|
||||||
|
<dd>${FreeUpdateForm(application)}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
</section>
|
||||||
</div>`;
|
</div>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -108,9 +132,13 @@ export default async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const pendingUpdates = queries.pendingApplicationUpdates(appToUpdate.name);
|
const pendingUpdates = queries.pendingApplicationUpdates(appToUpdate.name);
|
||||||
|
let logs = "";
|
||||||
|
if (pendingUpdates.length > 0) {
|
||||||
|
logs = await caprover.getLogs(appToUpdate.name);
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
Layout(Page(appToUpdate, latestVersions, pendingUpdates)),
|
Layout(Page(appToUpdate, latestVersions, pendingUpdates, logs)),
|
||||||
{
|
{
|
||||||
headers: { "Content-Type": "text/html" },
|
headers: { "Content-Type": "text/html" },
|
||||||
}
|
}
|
||||||
|
@ -134,5 +134,19 @@ describe("Caprover", () => {
|
|||||||
expect(events).toBeArrayOfSize(0);
|
expect(events).toBeArrayOfSize(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getLogs", () => {
|
||||||
|
it("should return the logs of the application when it exists", async () => {
|
||||||
|
const logs = await caprover.getLogs("mysql");
|
||||||
|
expect(logs).toBe("mysql logs");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error when the application does not exist", async () => {
|
||||||
|
await expect(
|
||||||
|
caprover.getLogs("unknown")
|
||||||
|
// @ts-ignore toThrowError exists¯\_(ツ)_/¯
|
||||||
|
).rejects.toThrowError(/Failed to fetch logs for application unknown/);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -79,6 +79,7 @@ class Caprover {
|
|||||||
|
|
||||||
async updateApplication(appName: string, version: string): Promise<void> {
|
async updateApplication(appName: string, version: string): Promise<void> {
|
||||||
console.debug("Caprover: Updating application", appName, "to", version);
|
console.debug("Caprover: Updating application", appName, "to", version);
|
||||||
|
try {
|
||||||
const response = await this.fetch(`/user/apps/appData/${appName}`, {
|
const response = await this.fetch(`/user/apps/appData/${appName}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -89,11 +90,12 @@ class Caprover {
|
|||||||
gitHash: "",
|
gitHash: "",
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.debug("update application response", response);
|
|
||||||
if (response.status !== 100) {
|
if (response.status !== 100) {
|
||||||
|
throw new Error(response.description);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to update application ${appName} to ${version}: ${response.description}.`
|
`Failed to update application ${appName} to ${version}: ${error ?? ""}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +107,18 @@ class Caprover {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLogs(appName: string): Promise<string> {
|
||||||
|
console.debug("Caprover: Fetching logs for", appName);
|
||||||
|
const response = await this.fetch(`/user/apps/appData/${appName}/logs`);
|
||||||
|
if (response.status !== 100) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch logs for application ${appName}: ${response.description}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.logs;
|
||||||
|
}
|
||||||
|
|
||||||
private async authenticate() {
|
private async authenticate() {
|
||||||
if (this.authToken) {
|
if (this.authToken) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { http, HttpResponse, HttpResponseResolver, PathParams } from "msw";
|
||||||
http,
|
|
||||||
HttpResponse,
|
|
||||||
HttpResponseResolver,
|
|
||||||
PathParams
|
|
||||||
} from "msw";
|
|
||||||
import { setupServer } from "msw/node";
|
import { setupServer } from "msw/node";
|
||||||
import appsFixtures from "./apps.fixtures.json";
|
import appsFixtures from "./apps.fixtures.json";
|
||||||
|
|
||||||
@ -97,6 +92,27 @@ const handlers = [
|
|||||||
data: {},
|
data: {},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
http.get(
|
||||||
|
`${BASE_URI}/user/apps/appData/:name/logs`,
|
||||||
|
withAuth(({ params }) => {
|
||||||
|
const app = appsFixtures.find((app) => app.appName === params.name);
|
||||||
|
if (!app) {
|
||||||
|
return HttpResponse.json({
|
||||||
|
status: 1000,
|
||||||
|
description: `App (${params.name}) could not be found. Make sure that you have created the app.`,
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
status: 100,
|
||||||
|
description: "App runtime logs are retrieved",
|
||||||
|
data: {
|
||||||
|
logs: `${params.name} logs`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
const server = setupServer(...handlers);
|
const server = setupServer(...handlers);
|
||||||
|
Loading…
Reference in New Issue
Block a user