Skip to content

Commit 8b8726c

Browse files
committed
Support listing volumes
1 parent 4a4ba15 commit 8b8726c

File tree

4 files changed

+81
-4
lines changed

4 files changed

+81
-4
lines changed

src/app.jsx

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { superuser } from "superuser";
2222
import ContainerHeader from './ContainerHeader.jsx';
2323
import Containers from './Containers.jsx';
2424
import Images from './Images.jsx';
25+
import Volumes from './Volumes.jsx';
2526
import * as client from './client.js';
2627
import detect_quadlets from './detect-quadlets.py';
2728
import rest from './rest.js';
@@ -46,7 +47,7 @@ class Application extends React.Component {
4647
constructor(props) {
4748
super(props);
4849
this.state = {
49-
// currently connected services per user: { con, uid, name, dbus: { client, subscription }, imagesLoaded, containersLoaded, podsLoaded, quadletsLoaded }
50+
// currently connected services per user: { con, uid, name, dbus: { client, subscription }, imagesLoaded, containersLoaded, podsLoaded, quadletsLoaded, volumesLoaded }
5051
// start with dummy state to wait for initialization
5152
users: [{ con: null, uid: 0, name: _("system"), dbus: null }, { con: null, uid: null, name: _("user"), dbus: null }],
5253
images: null,
@@ -60,6 +61,7 @@ class Application extends React.Component {
6061
quadletContainers: {},
6162
// { "$uid-$name-pod.service": { source_path, name } }
6263
quadletPods: {},
64+
volumes: null,
6365
textFilter: "",
6466
ownerFilter: "all",
6567
dropDownValue: 'Everything',
@@ -196,6 +198,30 @@ class Application extends React.Component {
196198
.catch(e => console.warn("initContainers uid", con.uid, "getContainers failed:", e.toString()));
197199
}
198200

201+
initVolumes(con) {
202+
return client.getVolumes(con)
203+
.then(volumesList => {
204+
console.log(volumesList);
205+
this.setState(prevState => {
206+
const copyVolumes = {};
207+
Object.entries(prevState.volumes || {}).forEach(([id, volume]) => {
208+
if (volume.uid !== con.uid)
209+
copyVolumes[id] = volume;
210+
});
211+
212+
for (const volume of volumesList || []) {
213+
volume.uid = con.uid;
214+
volume.key = makeKey(con.uid, volume.Name);
215+
copyVolumes[volume.key] = volume;
216+
}
217+
218+
const users = prevState.users.map(u => u.uid === con.uid ? { ...u, volumesLoaded: true } : u);
219+
return { volumes: copyVolumes, users };
220+
});
221+
})
222+
.catch(ex => console.warn("Failed to fetch volumes for uid", con.uid, ":", JSON.stringify(ex)));
223+
}
224+
199225
updateImages(con) {
200226
client.getImages(con)
201227
.then(reply => {
@@ -418,6 +444,24 @@ class Application extends React.Component {
418444
}
419445
}
420446

447+
handleVolumeEvent(event, con) {
448+
console.log(event, con);
449+
switch (event.Action) {
450+
case 'create':
451+
this.initVolumes(con);
452+
break;
453+
case 'remove':
454+
this.setState(prevState => {
455+
const volumes = { ...prevState.volumes };
456+
delete volumes[makeKey(con.uid, event.Actor.Attributes.name)];
457+
return { volumes };
458+
});
459+
break;
460+
default:
461+
console.warn('Unhandled event type ', event.Type, event.Action);
462+
}
463+
}
464+
421465
handleEvent(event, con) {
422466
switch (event.Type) {
423467
case 'container':
@@ -429,6 +473,9 @@ class Application extends React.Component {
429473
case 'pod':
430474
this.handlePodEvent(event, con);
431475
break;
476+
case 'volume':
477+
this.handleVolumeEvent(event, con);
478+
break;
432479
default:
433480
console.warn('Unhandled event type ', event.Type);
434481
}
@@ -638,7 +685,7 @@ class Application extends React.Component {
638685
const reply = await client.getInfo(con);
639686
this.setState(prevState => {
640687
const users = prevState.users.filter(u => u.uid !== uid);
641-
users.push({ con, uid, name: username, containersLoaded: false, podsLoaded: false, imagesLoaded: false, quadletsLoaded: false });
688+
users.push({ con, uid, name: username, containersLoaded: false, podsLoaded: false, imagesLoaded: false, quadletsLoaded: false, volumesLoaded: false });
642689
// keep a nice sort order for dialogs
643690
users.sort(compareUser);
644691
debug("init uid", uid, "username", username, "new users:", users);
@@ -660,6 +707,7 @@ class Application extends React.Component {
660707
this.updateImages(con);
661708
this.initContainers(con);
662709
this.initQuadlets(con);
710+
this.initVolumes(con);
663711
this.subscribeDaemonReload(con);
664712
this.updatePods(con);
665713

@@ -878,6 +926,7 @@ class Application extends React.Component {
878926
const loadingContainers = this.state.users.find(u => u.con && !u.containersLoaded);
879927
const loadingPods = this.state.users.find(u => u.con && !u.podsLoaded);
880928
const loadingQuadlets = this.state.users.find(u => u.con && !u.quadletsLoaded);
929+
const loadingVolumes = this.state.users.find(u => u.con && !u.volumesLoaded);
881930

882931
const imageList = (
883932
<Images
@@ -912,6 +961,16 @@ class Application extends React.Component {
912961
/>
913962
);
914963

964+
const volumeList = (
965+
<Volumes
966+
key="volumeList"
967+
users={this.state.users}
968+
textFilter={this.state.textFilter}
969+
ownerFilter={this.state.ownerFilter}
970+
volumes={loadingVolumes ? null : this.state.volumes}
971+
/>
972+
);
973+
915974
const notificationList = (
916975
<AlertGroup isToast>
917976
{this.state.notifications.map((notification, index) => {
@@ -953,6 +1012,7 @@ class Application extends React.Component {
9531012
<PageSection hasBodyWrapper={false} className='ct-pagesection-mobile'>
9541013
<Stack hasGutter>
9551014
{imageList}
1015+
{volumeList}
9561016
{containerList}
9571017
</Stack>
9581018
</PageSection>

src/client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,5 @@ export const imageHistory = (con: Connection, id: string) => podmanJson(con, `li
161161
export const imageExists = (con: Connection, id: string) => podmanCall(con, "libpod/images/" + id + "/exists", "GET", {});
162162

163163
export const containerExists = (con: Connection, id: string) => podmanCall(con, "libpod/containers/" + id + "/exists", "GET", {});
164+
165+
export const getVolumes = (con: Connection) => podmanJson(con, "libpod/volumes/json", "GET", {});

src/podman.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
// For pf-v6-line-clamp
66
@use "@patternfly/patternfly/sass-utilities/mixins.scss";
77

8-
#app .pf-v6-c-card.containers-containers, #app .pf-v6-c-card.containers-images {
8+
#app .pf-v6-c-card.containers-containers, #app .pf-v6-c-card.containers-images, #app .pf-v6-c-card.containers-volumes {
99
@extend .ct-card;
1010
}
1111

12-
#containers-images, #containers-containers {
12+
#containers-images, #containers-containers, #containers-volumes {
1313
// Decrease padding for the image/container toggle button list
1414
.pf-v6-c-table.pf-m-compact .pf-v6-c-table__toggle {
1515
padding-inline-start: 0;

test/check-application

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3728,6 +3728,21 @@ ContainerName=guitarq1
37283728
b.wait_in_text(f"{sel} + tr", "sleep infinity")
37293729
b.wait_not_in_text(f"{sel} + tr", "systemd service")
37303730

3731+
def _testVolumes(self, *, system: bool = False):
3732+
volume_name = "nextcloud-data"
3733+
3734+
self.execute(f"podman volume create {volume_name}", system=system)
3735+
self.execute(f"podman run -d --name nextcloud --stop-timeout 0 --volume {volume_name}:/data {IMG_ALPINE} sh",
3736+
system=system)
3737+
3738+
self.login()
3739+
3740+
def testVolumesUser(self):
3741+
self._testVolumes(system=False)
3742+
3743+
def testVolumesSystem(self):
3744+
self._testVolumes(system=True)
3745+
37313746

37323747
if __name__ == '__main__':
37333748
testlib.test_main()

0 commit comments

Comments
 (0)