Skip to content

Commit

Permalink
Contruct a container object by podman version
Browse files Browse the repository at this point in the history
This change will now build a container object and extract the info from
the json output by the podman version.

Now we can make the extension work with various podman version and break
less often by those changes.

Signed-off-by: Roy Golan <[email protected]>
  • Loading branch information
rgolangh committed Sep 3, 2020
1 parent 89f0acb commit b4b5731
Showing 1 changed file with 139 additions and 53 deletions.
192 changes: 139 additions & 53 deletions extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const GObject = imports.gi.GObject;


let containersMenu;
let debugEnabled = false;
let debugEnabled = true;
let podmanVersion;

function debug(msg) {
if (debugEnabled) {
Expand All @@ -29,6 +31,8 @@ function info(msg) {

function enable() {
info("enabling containers extension");
init();
debug(podmanVersion);
containersMenu = new ContainersMenu();
debug(containersMenu);
containersMenu.renderMenu();
Expand Down Expand Up @@ -73,8 +77,8 @@ 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(container.toString());
const subMenu = new ContainerSubMenuMenuItem(container, container.name);
this.menu.addMenuItem(subMenu);
});
} else {
Expand All @@ -89,37 +93,6 @@ class ContainersMenu extends PanelMenu.Button {
}
});


/* getContainers return a json array containers in the form of
[
{
"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": "",
"Size": "",
"Names": "sleepy_shockley",
"Labels": "key=value,"
},
]
*/
const getContainers = () => {
const [res, out, err, status] = GLib.spawn_command_line_sync("podman ps -a --format json");
if (!res) {
info(`status: ${status}, error: ${err}`);
throw new Error(_("Error occurred when fetching containers"));
}
debug(out);
const containers = JSON.parse(imports.byteArray.toString(out));
if (containers == null) {
return {};
}
return containers;
};

const runCommand = function (command, containerName) {
const cmdline = `podman ${command} ${containerName}`;
info(`running command ${cmdline}`);
Expand Down Expand Up @@ -167,6 +140,7 @@ class extends PopupMenu.PopupMenuItem {
}, false));
}
this.add_style_class_name("containers-extension-subMenuItem");
this.add_style_class_name(label.toLowerCase());
}
});

Expand Down Expand Up @@ -224,51 +198,57 @@ var ContainerSubMenuMenuItem = GObject.registerClass(
},
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));
super._init(container.name);
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.createdAt));
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);
inspect(container.name, this.menu);
}));
// end of inspect

switch (container.Status.split(" ")[0]) {
switch (container.status.split(" ")[0]) {
case "Exited":
case "exited":
case "Created":
case "created":
case "configured":
case "stopped":

this.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1);
const startMeunItem = new ContainerMenuItem(container.Names, "start");
const startMeunItem = new ContainerMenuItem(container.name, "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");
const rmMenuItem = new ContainerMenuItem(container.name, "rm");
rmMenuItem.insert_child_at_index(createIcon('user-trash-symbolic', 'status-remove'), 1);
this.menu.addMenuItem(rmMenuItem);
break;
case "Up":
case "running":
this.menu.addMenuItem(new PopupMenuItem("Started", container.startedAt));
this.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-running'), 1);
const pauseMenuIten = new ContainerMenuItem(container.Names, "pause");
const pauseMenuIten = new ContainerMenuItem(container.name, "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");
const stopMenuItem = new ContainerMenuItem(container.name, "stop");
stopMenuItem.insert_child_at_index(createIcon('process-stop-symbolic', 'status-stopped'), 1);
this.menu.addMenuItem(stopMenuItem);
const restartMenuItem = new ContainerMenuItem(container.Names, "restart");
const restartMenuItem = new ContainerMenuItem(container.name, "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":
case "paused":
this.insert_child_at_index(createIcon('media-playback-pause-symbolic', 'status-paused'), 1);
const unpauseMenuItem = new ContainerMenuItem(container.Names, "unpause");
const unpauseMenuItem = new ContainerMenuItem(container.name, "unpause");
unpauseMenuItem.insert_child_at_index(createIcon('media-playback-start-symbolic', 'status-start'), 1)
this.menu.addMenuItem(unpauseMenuItem);
break;
Expand Down Expand Up @@ -297,26 +277,132 @@ 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.name}`, "");
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.name, "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.name, "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.name, "podman stats", "");
i.insert_child_at_index(new St.Label({ style_class: 'action-stats', text: "%" }), 1);
return i;
}


function init() {
const [res, out, err, status] = GLib.spawn_command_line_sync("podman version --format json");
if (!res) {
info(`status: ${status}, error: ${err}`);
throw new Error(_("Error getting podman version"));
}
debug(out);
const versionJson = JSON.parse(imports.byteArray.toString(out));
if (versionJson.Client != null && versionJson.Client.Version != null) {
podmanVersion = new Version(versionJson.Client.Version);
}
if (versionJson == null) {
info("unable to set podman info, will fall back to syntax and output < 2.0.3");
}
}

// return list of containers : Container[]
function getContainers(containersLister) {
const [res, out, err, status] = GLib.spawn_command_line_sync("podman ps -a --format json");
// const [res, out, err, status] = containersLister.get();

if (!res) {
info(`status: ${status}, error: ${err}`);
throw new Error(_("Error occurred when fetching containers"));
}
debug(out);
const jsonContainers = JSON.parse(imports.byteArray.toString(out));
if (jsonContainers == null) {
return [];
}
const containers = [];
jsonContainers.forEach(e => {
let c = new Container(e);
containers.push(c);
});
return containers;
}

class Container {
constructor(jsonContainer) {
if (podmanVersion.newerOrEqualTo("2.0.3")) {
this.name = jsonContainer.Names[0];
this.id = jsonContainer.Id;
this.state = jsonContainer.State;
this.status = jsonContainer.State;
this.createdAt = jsonContainer.CreatedAt;
} else {
this.name = jsonContainer.Names;
this.id = jsonContainer.ID;
this.state = jsonContainer.Status;
this.status = jsonContainer.Status;
this.createdAt = jsonContainer.Created;

}
this.image = jsonContainer.Image;
this.command = jsonContainer.Command;
this.startedAt = new Date(jsonContainer.StartedAt * 1000);
this.ports = jsonContainer.Ports.map(e => `host ${e.hostPort}/${e.protocol} -> pod ${e.containerPort}`);
}

toString() {
return `name: ${this.name}
id: ${this.id}
state: ${this.state}
status: ${this.status}
image: ${this.image}`;
}
}

class Version {
constructor(v) {
const splits = v.split(".")
this.major = splits[0];
this.minor = splits[1];
if (splits.length > 2) {
this.patch = splits[2];
}
}

newerOrEqualTo(v) {
return this.compare(new Version(v)) >= 0;
}

compare(other) {
debug(`compare ${this} with ${other}`);
if (this.major != other.major) {
return Math.sign(this.major - other.major);
}
if (this.minor != other.minor) {
return Math.sign(this.minor - other.minor);
}
if (this.patch != other.patch) {
if (this.patch == null) {
return -1;
}
return this.patch.localeCompare(other.patch);
}
return 0;
}

toString() {
return `major: ${this.major} minor: ${this.minor} patch: ${this.patch}`;
}
}

0 comments on commit b4b5731

Please sign in to comment.