Set the app update status when reporting state

Change-type: minor
This commit is contained in:
Felipe Lalanne 2024-06-06 18:50:59 -04:00
parent 48e526ec43
commit 227fee9941
2 changed files with 130 additions and 16 deletions

View File

@ -865,9 +865,9 @@ export async function getLegacyState() {
return { local: apps };
}
// TODO: this function is probably more inefficient than it needs to be, since
// it tried to optimize for readability, look for a way to make it simpler
export async function getState() {
type AppsReport = { [uuid: string]: AppState };
export async function getState(): Promise<AppsReport> {
const [services, images] = await Promise.all([
serviceManager.getState(),
imageManager.getState(),
@ -980,7 +980,7 @@ export async function getState() {
);
// Assemble the state of apps
const state: { [appUuid: string]: AppState } = {};
const state: AppsReport = {};
for (const {
appId,
appUuid,
@ -989,21 +989,54 @@ export async function getState() {
createdAt,
...svc
} of servicesToReport) {
state[appUuid] = {
...state[appUuid],
const app = state[appUuid] ?? {
// Add the release_uuid if the commit has been stored in the database
...(commitsForApp[appId] && { release_uuid: commitsForApp[appId] }),
releases: {
...state[appUuid]?.releases,
[commit]: {
...state[appUuid]?.releases[commit],
services: {
...state[appUuid]?.releases[commit]?.services,
[serviceName]: svc,
},
},
},
releases: {},
};
const releases = app.releases;
releases[commit] = releases[commit] ?? {
update_status: 'done',
services: {},
};
releases[commit].services[serviceName] = svc;
// The update_status precedence order is as follows
// - aborted
// - downloading
// - downloaded
// - applying changes
// - done
if (svc.status === 'Aborted') {
releases[commit].update_status = 'aborted';
} else if (
releases[commit].update_status !== 'aborted' &&
svc.download_progress != null &&
svc.download_progress !== 100
) {
releases[commit].update_status = 'downloading';
} else if (
!['aborted', 'downloading'].includes(releases[commit].update_status!) &&
(svc.download_progress === 100 || svc.status === 'Downloaded')
) {
releases[commit].update_status = 'downloaded';
} else if (
// The `applying changes` state has lower precedence over the aborted/downloading/downloaded
// state
!['aborted', 'downloading', 'downloaded'].includes(
releases[commit].update_status!,
) &&
['installing', 'installed', 'awaiting handover'].includes(
svc.status.toLowerCase(),
)
) {
releases[commit].update_status = 'applying changes';
}
// Update the state object
state[appUuid] = app;
}
return state;
}

View File

@ -2444,6 +2444,7 @@ describe('compose/application-manager', () => {
download_progress: 50,
},
},
update_status: 'downloading',
},
},
},
@ -2456,6 +2457,7 @@ describe('compose/application-manager', () => {
status: 'Downloaded',
},
},
update_status: 'downloaded',
},
newrelease: {
services: {
@ -2465,6 +2467,7 @@ describe('compose/application-manager', () => {
download_progress: 75,
},
},
update_status: 'downloading',
},
},
},
@ -2548,6 +2551,7 @@ describe('compose/application-manager', () => {
download_progress: 0,
},
},
update_status: 'downloading',
},
},
},
@ -2560,6 +2564,81 @@ describe('compose/application-manager', () => {
status: 'exited',
},
},
update_status: 'done',
},
},
},
});
});
it('reports aborted state if one of the services/images status is aborted', async () => {
getImagesState.resolves([
{
name: 'ubuntu:latest',
commit: 'latestrelease',
appUuid: 'myapp',
serviceName: 'ubuntu',
status: 'Downloaded',
},
{
name: 'node:latest',
commit: 'latestrelease',
appUuid: 'myapp',
serviceName: 'node',
status: 'Downloaded',
downloadProgress: 100,
},
{
name: 'alpine:latest',
commit: 'latestrelease',
appUuid: 'myapp',
serviceName: 'alpine',
status: 'Aborted',
downloadProgress: 0,
},
]);
getServicesState.resolves([
{
commit: 'latestrelease',
appUuid: 'myapp',
serviceName: 'ubuntu',
status: 'Running',
createdAt: new Date('2021-09-01T13:00:00'),
},
{
appUuid: 'myapp',
commit: 'latestrelease',
serviceName: 'node',
// we don't have a way to abort a failing service install yet, but
// once we do it will need to use the status field
status: 'Aborted',
createdAt: new Date('2021-09-01T12:00:00'),
},
]);
expect(await applicationManager.getState()).to.deep.equal({
myapp: {
releases: {
latestrelease: {
services: {
ubuntu: {
image: 'ubuntu:latest',
status: 'Running',
},
alpine: {
image: 'alpine:latest',
// we don't have a way to abort a failing download yet, but
// once we do it will need to use the status field
status: 'Aborted',
download_progress: 0,
},
node: {
image: 'node:latest',
status: 'Aborted',
download_progress: 100,
},
},
update_status: 'aborted',
},
},
},
@ -2610,6 +2689,7 @@ describe('compose/application-manager', () => {
status: 'Awaiting handover',
},
},
update_status: 'applying changes',
},
oldrelease: {
services: {
@ -2618,6 +2698,7 @@ describe('compose/application-manager', () => {
status: 'Handing over',
},
},
update_status: 'done',
},
},
},