diff --git a/src/app.jsx b/src/app.jsx index 13c3353e8..0863a7962 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -33,6 +33,7 @@ import ContainerHeader from './ContainerHeader.jsx'; import Containers from './Containers.jsx'; import Images from './Images.jsx'; import * as client from './client.js'; +import { debug } from './util.js'; const _ = cockpit.gettext; @@ -276,6 +277,27 @@ class Application extends React.Component { return new_wait; } + /* lightweight version of updateContainer, but still needs the same serialization */ + updateContainerState(id, system, props) { + const idx = id + system.toString(); + + if (!this.state.containers[idx]) + // we got an event for a container which we didn't introspect yet + return this.updateContainer(id, system); + + const wait = this.pendingUpdateContainer[idx] ?? Promise.resolve(); + + wait.then(() => this.setState(prevState => { + const containers = { ...prevState.containers }; + const newContainer = { ...containers[idx], State: { ...containers[idx].State, ...props } }; + containers[idx] = newContainer; + debug(system, "updateContainerState", idx, "to", JSON.stringify(newContainer.State)); + return { containers }; + })); + this.pendingUpdateContainer[idx] = wait; + wait.finally(() => { delete this.pendingUpdateContainer[idx] }); + } + updateImage(id, system) { client.getImages(system, id) .then(reply => { @@ -343,6 +365,15 @@ class Application extends React.Component { case 'unmount': case 'wait': break; + + /* The following events just change the State properties */ + case 'pause': + this.updateContainerState(id, system, { Status: "paused" }); + break; + case 'unpause': + this.updateContainerState(id, system, { Status: "running" }); + break; + /* The following events need only to update the Container list * We do get the container affected in the event object but for * now we 'll do a batch update @@ -353,17 +384,28 @@ class Application extends React.Component { (event.Actor.Attributes.podId ? this.updatePod(event.Actor.Attributes.podId, system) : this.updatePods(system) - ).then(() => this.updateContainer(id, system, event)); + ).then(() => this.updateContainerState( + id, system, + { + Status: "running", + StartedAt: new Date(event.timeNano / 1000000).toISOString() + })); break; - case 'checkpoint': + + case 'died': + case 'stop': + this.updateContainerState(id, system, { Status: "stopped" }); + break; + case 'cleanup': + // don't bother with setting FinishedAt, we don't use it anywhere + this.updateContainerState(id, system, { Status: "exited" }); + break; + + case 'checkpoint': case 'create': - case 'died': case 'exec_died': // HACK: pick up health check runs, see https://github.com/containers/podman/issues/19237 - case 'pause': case 'restore': - case 'stop': - case 'unpause': case 'rename': // rename event is available starting podman v4.1; until then the container does not get refreshed after renaming this.updateContainer(id, system, event); break; diff --git a/test/check-application b/test/check-application index 121dab5e7..1f7047d68 100755 --- a/test/check-application +++ b/test/check-application @@ -1235,10 +1235,10 @@ class TestApplication(testlib.MachineCase): # Restart the container; there is no steady state change in the visible UI, so look for # a changed data-started-at attribute old_start = self.getStartTime("swamped-crate", auth=auth) - b.wait_in_text(f'#containers-containers tr[data-started-at="{old_start}"]', "swamped-crate") + old_start_dom = b.attr("#containers-containers tr[data-row-id", "data-started-at") self.performContainerAction(IMG_BUSYBOX, "Force restart") - new_start = self.waitRestart("swamped-crate", old_start, auth=auth) - b.wait_in_text(f'#containers-containers tr[data-started-at="{new_start}"]', "swamped-crate") + self.waitRestart("swamped-crate", old_start, auth=auth) + b.wait_not_attr("#containers-containers tr[data-row-id", "data-started-at", old_start_dom) self.waitContainer(container_sha, auth, name='swamped-crate', image=IMG_BUSYBOX, state='Running') self.waitContainerRow(IMG_BUSYBOX)