From 340f2bf696a1ce7ccd6844522df3847946d5c0de Mon Sep 17 00:00:00 2001 From: Roman Martin Date: Thu, 30 Jul 2020 13:26:29 +0200 Subject: [PATCH 1/2] :sparkles: Updated to podman 2.0.3 json format podman 2.0.3 json format includes a set of differences in the schema (e.g: Names as an array, State instead of Status, ...). This commit includes some changes to update them. --- README.md | 8 +- extension.js | 275 ++++++++++++++++++++++++++------------------------- 2 files changed, 148 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 8932884..3808139 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,23 @@ See the [gnome extensions page](https://extensions.gnome.org/extension/1500/cont # Install from source -First pick the right branch by the gnome-shell-X.XX version: +First pick the right branch by the `gnome-shell-X.XX` version: | branch | gnome-shell version | | --- | --- | | master | 3.36.1 | +| 3.36 | 3.36.4 | | 3.34.2 | 3.34.2 | | 3.28.3 | 3.28.3 | - Clone, Pack, and Install + ```console $ git clone https://github.com/rgolangh/gnome-shell-extension-containers $ make all +``` -Or using 'Tweaks' -> Extensions -> toggle 'Containers' +Or using 'Tweaks' -> Extensions -> toggle 'Containers':

diff --git a/extension.js b/extension.js index cb91063..88065d4 100644 --- a/extension.js +++ b/extension.js @@ -48,22 +48,22 @@ var ContainersMenu = GObject.registerClass( { GTypeName: 'ContainersMenu' }, -class ContainersMenu extends PanelMenu.Button { - _init() { - super._init(0.0, "Containers"); - this.menu.box.add_style_class_name('containers-extension-menu'); - const hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); - const gicon = Gio.icon_new_for_string(Me.path + "/podman-icon.png"); - const icon = new St.Icon({ gicon: gicon, icon_size: '24' }); + class ContainersMenu extends PanelMenu.Button { + _init() { + super._init(0.0, "Containers"); + this.menu.box.add_style_class_name('containers-extension-menu'); + const hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); + const gicon = Gio.icon_new_for_string(Me.path + "/podman-icon.png"); + const icon = new St.Icon({ gicon: gicon, icon_size: '24' }); - hbox.add_child(icon); - hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); - this.add_child(hbox); - this.connect('button_press_event', Lang.bind(this, () => { - if (this.menu.isOpen) { - this.menu.removeAll(); - this.renderMenu(); - } + hbox.add_child(icon); + hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); + this.add_child(hbox); + this.connect('button_press_event', Lang.bind(this, () => { + if (this.menu.isOpen) { + this.menu.removeAll(); + this.renderMenu(); + } })); } @@ -73,15 +73,15 @@ class ContainersMenu extends PanelMenu.Button { info(`found ${containers.length} containers`); if (containers.length > 0) { containers.forEach((container) => { - debug(container); - const subMenu = new ContainerSubMenuMenuItem(container, container.Names); + debug(`Menu item for container ${container.Id} with name ${container.Names[0]}`); + const subMenu = new ContainerSubMenuMenuItem(container, container.Names[0]); this.menu.addMenuItem(subMenu); }); } else { this.menu.addMenuItem(new PopupMenu.PopupMenuItem("No containers detected")); } } catch (err) { - const errMsg = _("Error occurred when fetching containers"); + const errMsg = _("Error occurred when rendering containers"); this.menu.addMenuItem(new PopupMenu.PopupMenuItem(errMsg)); info(`${errMsg}: ${err}`); } @@ -89,21 +89,24 @@ class ContainersMenu extends PanelMenu.Button { } }); - /* getContainers return a json array containers in the form of [ { - "ID": "7a9e1233db51", + "Id": "7a9e1233db51", "Image": "localhost/image-name:latest", "Command": "/entrypoint.sh bash", - "CreatedAtTime": "2018-10-10T10:14:47.884563227+03:00", - "Created": "2 weeks ago", - "Status": "Created", - "Ports": "", + "Created": "1596032658", + "CreatedAt": "2 weeks ago", + "State": "stopped", + "Ports": [], "Size": "", - "Names": "sleepy_shockley", - "Labels": "key=value," - }, + "Names": [ + "sleepy_shockley" + ], + "Labels": { + "key": "value" + } + } ] */ const getContainers = () => { @@ -156,132 +159,141 @@ var PopupMenuItem = GObject.registerClass( { GTypeName: 'PopupMenuItem' }, -class extends PopupMenu.PopupMenuItem { - _init(label, value) { - if (value === undefined) { - super._init(label); - } else { - super._init(`${label}: ${value}`); - this.connect('button_press_event', Lang.bind(this, () => { - setClipboard(value); - }, false)); + class extends PopupMenu.PopupMenuItem { + _init(label, value) { + if (value === undefined) { + super._init(label); + } else { + super._init(`${label}: ${value}`); + this.connect('button_press_event', Lang.bind(this, () => { + setClipboard(value); + }, false)); + } + this.add_style_class_name("containers-extension-subMenuItem"); } - this.add_style_class_name("containers-extension-subMenuItem"); } -}); +); var ContainerMenuItem = GObject.registerClass( { GTypeName: 'ContainerMenuItem' }, -class extends PopupMenuItem { - _init(containerName, command) { - super._init(command); - this.containerName = containerName; - this.command = command; - this.connect('activate', Lang.bind(this, () => { - runCommand(this.command, this.containerName); - })); + class extends PopupMenuItem { + _init(containerName, command) { + super._init(command); + this.containerName = containerName; + this.command = command; + this.connect('activate', Lang.bind(this, () => { + runCommand(this.command, this.containerName); + })); + } } -}); +); var ContainerMenuWithOutputItem = GObject.registerClass( - { + { GTypeName: 'ContainerMenuWithOutputItem' - }, -class extends PopupMenuItem { - _init(containerName, command, outputHdndler) { - super._init(command); - this.containerName = containerName; - this.command = command; - this.connect('activate', Lang.bind(this, () => { - var out = runCommand(this.command, this.containerName); - outputHdndler(out); - })); + }, + class extends PopupMenuItem { + _init(containerName, command, outputHdndler) { + super._init(command); + this.containerName = containerName; + this.command = command; + this.connect('activate', Lang.bind(this, () => { + var out = runCommand(this.command, this.containerName); + outputHdndler(out); + })); + } } -}); +); var ContainerMenuItemWithTerminalAction = GObject.registerClass( - { + { GTypeName: 'ContainerMenuItemWithTerminalAction' - }, -class extends PopupMenuItem { - _init(label, containerName, command, args) { - super._init(label); - this.containerName = containerName; - this.command = command; - this.args = args; - this.connect('activate', Lang.bind(this, () => { - runCommandInTerminal(this.command, this.containerName, this.args); - })); + }, + class extends PopupMenuItem { + _init(label, containerName, command, args) { + super._init(label); + this.containerName = containerName; + this.command = command; + this.args = args; + this.connect('activate', Lang.bind(this, () => { + runCommandInTerminal(this.command, this.containerName, this.args); + })); + } } -}); - +); var ContainerSubMenuMenuItem = GObject.registerClass( { GTypeName: 'ContainerSubMenuMenuItem' }, -class extends PopupMenu.PopupSubMenuMenuItem { - _init(container, name) { - super._init(container.Names); - this.menu.addMenuItem(new PopupMenuItem("Status", container.Status)); - this.menu.addMenuItem(new PopupMenuItem("Id", container.ID)); - this.menu.addMenuItem(new PopupMenuItem("Image", container.Image)); - this.menu.addMenuItem(new PopupMenuItem("Command", container.Command)); - this.menu.addMenuItem(new PopupMenuItem("Created", container.Created)); - this.menu.addMenuItem(new PopupMenuItem("Ports", container.Ports)); + class extends PopupMenu.PopupSubMenuMenuItem { + _init(container, name) { + super._init(container.Names[0]); + this.menu.addMenuItem(new PopupMenuItem("Status", container.State)); + this.menu.addMenuItem(new PopupMenuItem("Id", container.Id)); + this.menu.addMenuItem(new PopupMenuItem("Image", container.Image)); + this.menu.addMenuItem(new PopupMenuItem("Command", container.Command)); + this.menu.addMenuItem(new PopupMenuItem("Created", container.Created)); + this.menu.addMenuItem(new PopupMenuItem("Ports", container.Ports)); - // add more stats and info - inspect - SLOW - this.connect("button_press_event", Lang.bind(this, () => { - inspect(container.Names, this.menu); - })); - // end of inspect + // add more stats and info - inspect - SLOW + this.connect("button_press_event", Lang.bind(this, () => { + debug(`Inspect ${container.Names[0]}`) + inspect(container.Names[0], this.menu); + })); + // end of inspect - switch (container.Status.split(" ")[0]) { - case "Exited": - case "Created": - case "stopped": + debug(`Eval container ${container.Names[0]} state ${container.State}`) - this.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1); - const startMeunItem = new ContainerMenuItem(container.Names, "start"); - startMeunItem.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-start'), 1); - this.menu.addMenuItem(startMeunItem); - const rmMenuItem = new ContainerMenuItem(container.Names, "rm"); - rmMenuItem.insert_child_at_index(createIcon('user-trash-symbolic', 'status-remove'), 1); - this.menu.addMenuItem(rmMenuItem); - break; - case "Up": - this.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-running'), 1); - const pauseMenuIten = new ContainerMenuItem(container.Names, "pause"); - pauseMenuIten.insert_child_at_index(createIcon('media-playback-pause-symbolic', 'status-stopped'), 1); - this.menu.addMenuItem(pauseMenuIten); - const stopMenuItem = new ContainerMenuItem(container.Names, "stop"); - stopMenuItem.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1); - this.menu.addMenuItem(stopMenuItem); - const restartMenuItem = new ContainerMenuItem(container.Names, "restart"); - restartMenuItem.insert_child_at_index(createIcon('system-reboot-symbolic', 'status-restart'), 1); - this.menu.addMenuItem(restartMenuItem); - this.menu.addMenuItem(createTopMenuItem(container)); - this.menu.addMenuItem(createShellMenuItem(container)); - this.menu.addMenuItem(createStatsMenuItem(container)); - break; - case "Paused": - this.insert_child_at_index(createIcon('media-playback-pause-symbolic', 'status-paused'), 1); - const unpauseMenuItem = new ContainerMenuItem(container.Names, "unpause"); - unpauseMenuItem.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-start'), 1) - this.menu.addMenuItem(unpauseMenuItem); - break; - default: - this.insert_child_at_index(createIcon('action-unavailable-symbolic', 'status-undefined'), 1); - break; - } + switch (container.State) { + case "exited": + case "created": + case "stopped": + this.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1); + const startMeunItem = new ContainerMenuItem(container.Names[0], "start"); + startMeunItem.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-start'), 1); + this.menu.addMenuItem(startMeunItem); + const rmMenuItem = new ContainerMenuItem(container.Names[0], "rm"); + rmMenuItem.insert_child_at_index(createIcon('user-trash-symbolic', 'status-remove'), 1); + this.menu.addMenuItem(rmMenuItem); + break; + + case "running": + this.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-running'), 1); + const pauseMenuIten = new ContainerMenuItem(container.Names[0], "pause"); + pauseMenuIten.insert_child_at_index(createIcon('media-playback-pause-symbolic', 'status-stopped'), 1); + this.menu.addMenuItem(pauseMenuIten); + const stopMenuItem = new ContainerMenuItem(container.Names[0], "stop"); + stopMenuItem.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1); + this.menu.addMenuItem(stopMenuItem); + const restartMenuItem = new ContainerMenuItem(container.Names[0], "restart"); + restartMenuItem.insert_child_at_index(createIcon('system-reboot-symbolic', 'status-restart'), 1); + this.menu.addMenuItem(restartMenuItem); + this.menu.addMenuItem(createTopMenuItem(container)); + this.menu.addMenuItem(createShellMenuItem(container)); + this.menu.addMenuItem(createStatsMenuItem(container)); + break; + + case "paused": + this.insert_child_at_index(createIcon('media-playback-pause-symbolic', 'status-paused'), 1); + const unpauseMenuItem = new ContainerMenuItem(container.Names[0], "unpause"); + unpauseMenuItem.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-start'), 1) + this.menu.addMenuItem(unpauseMenuItem); + break; + + default: + this.insert_child_at_index(createIcon('action-unavailable-symbolic', 'status-undefined'), 1); + break; + } - // add log button - const logMenuItem = createLogMenuItem(container); - this.menu.addMenuItem(logMenuItem); + // add log button + const logMenuItem = createLogMenuItem(container); + this.menu.addMenuItem(logMenuItem); + } } -}); +); function setClipboard(text) { St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, text); @@ -297,26 +309,25 @@ function inspect(container, menu) { } function createLogMenuItem(container) { - let i = new ContainerMenuItemWithTerminalAction("logs", "", `podman logs -f ${container.Names}`, ""); + let i = new ContainerMenuItemWithTerminalAction("logs", "", `podman logs -f ${container.Names[0]}`, ""); i.insert_child_at_index(createIcon('document-open-symbolic.symbolic', 'action-logs'), 1) return i } function createTopMenuItem(container) { - const i = new ContainerMenuItemWithTerminalAction("top", container.Names, "watch podman top", ""); + const i = new ContainerMenuItemWithTerminalAction("top", container.Names[0], "watch podman top", ""); i.insert_child_at_index(createIcon('view-reveal-symbolic.symbolic', 'action-top'), 1); return i; } function createShellMenuItem(container) { - const i = new ContainerMenuItemWithTerminalAction("sh", container.Names, "podman exec -it", "/bin/sh"); + const i = new ContainerMenuItemWithTerminalAction("sh", container.Names[0], "podman exec -it", "/bin/sh"); i.insert_child_at_index(new St.Label({ style_class: 'action-sh', text: ">_" }), 1); return i; } function createStatsMenuItem(container) { - const i = new ContainerMenuItemWithTerminalAction("stats", container.Names, "podman stats", ""); + const i = new ContainerMenuItemWithTerminalAction("stats", container.Names[0], "podman stats", ""); i.insert_child_at_index(new St.Label({ style_class: 'action-stats', text: "%" }), 1); return i; } - From e217b2f15cdc7ae39dfdacf8a3b097f7b74d13cf Mon Sep 17 00:00:00 2001 From: Roman Martin Date: Thu, 30 Jul 2020 13:52:42 +0200 Subject: [PATCH 2/2] :children_crossing: Converting unix timestamp to human readable format Using locale configuration of the user to show the creation timestamp --- extension.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extension.js b/extension.js index 88065d4..7878930 100644 --- a/extension.js +++ b/extension.js @@ -235,7 +235,7 @@ var ContainerSubMenuMenuItem = GObject.registerClass( this.menu.addMenuItem(new PopupMenuItem("Id", container.Id)); this.menu.addMenuItem(new PopupMenuItem("Image", container.Image)); this.menu.addMenuItem(new PopupMenuItem("Command", container.Command)); - this.menu.addMenuItem(new PopupMenuItem("Created", container.Created)); + this.menu.addMenuItem(new PopupMenuItem("Created", timeConverter(container.Created))); this.menu.addMenuItem(new PopupMenuItem("Ports", container.Ports)); // add more stats and info - inspect - SLOW @@ -331,3 +331,9 @@ function createStatsMenuItem(container) { i.insert_child_at_index(new St.Label({ style_class: 'action-stats', text: "%" }), 1); return i; } + +function timeConverter(UNIX_timestamp) { + var dateObject = new Date(UNIX_timestamp * 1000); + + return dateObject.toLocaleString() +}