-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2a0e428
Showing
10 changed files
with
1,723 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.pyc | ||
test.py | ||
__pycache__ | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dev: | ||
textual run --dev application.main:UI |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
|
||
![image](./resources/image.png) | ||
|
||
# Pocker | ||
|
||
I found Docker Desktop to be bloated, heavy, slow, resource-hogging and contains features that I rarely ever use. | ||
Not to say that it's bad; just not for me. | ||
|
||
# Introduction | ||
|
||
Pocker is a lightweight CLI tool to help with docker related tasks. For example, | ||
|
||
* View containers/images. | ||
* See logs and attributes. | ||
* Manage status of containers. | ||
|
||
The tool is heavily based on [docker-py](https://docker-py.readthedocs.io/en/stable/index.html) and [textual](https://github.com/textualize/textual/). | ||
A big thanks goes over to the creator and contributors of textual as it makes for a very sleek and easy interface. | ||
|
||
## Prerequisite | ||
|
||
> [!NOTE] | ||
> Pipx must be installed. | ||
```shell | ||
pip install pipx | ||
``` | ||
|
||
## Installation | ||
|
||
### Install from remote repository | ||
|
||
This will install latest version available from main. | ||
```shell | ||
pipx install git+https://github.com/pommee/Pocker@main | ||
``` | ||
|
||
### Install from local repository | ||
```bash | ||
git clone https://github.com/pommee/Pocker.git | ||
cd Pocker | ||
pipx install . | ||
``` | ||
|
||
## Usage | ||
|
||
Start by running | ||
|
||
```shell | ||
pocker | ||
``` | ||
|
||
Keybinds can be seen in the footer when started or by pressing `?`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import logging | ||
from threading import Event | ||
import time | ||
import docker | ||
from docker.models.containers import Container, ContainerCollection | ||
from textual.widgets import RichLog | ||
from textual.logging import TextualHandler | ||
|
||
logging.basicConfig( | ||
level="INFO", | ||
handlers=[TextualHandler()], | ||
) | ||
|
||
|
||
class DockerManager: | ||
def __init__(self) -> None: | ||
self.client = docker.from_env() | ||
self.containers: ContainerCollection = self.client.containers.list(all=True) | ||
self.images = self.client.images.list(all=True) | ||
self.selected_container = self.containers[0].name | ||
|
||
def container(self, container_name: str) -> Container: | ||
return self.client.containers.get(container_name) | ||
|
||
def curr_container(self): | ||
return self.container(self.selected_container) | ||
|
||
def logs(self): | ||
return ( | ||
self.container(self.selected_container) | ||
.logs(follow=False) | ||
.decode("utf-8") | ||
.strip() | ||
) | ||
|
||
def attributes(self): | ||
return self.container(self.selected_container).attrs | ||
|
||
def status(self, container_name: str): | ||
is_running = self.container(container_name).attrs.get("State").get("Running") | ||
|
||
status = "[U]" | ||
if is_running is True: | ||
status = "running" | ||
if not is_running: | ||
status = "down" | ||
return status | ||
|
||
def live_container_logs(self, logs: RichLog, stop_event: Event): | ||
logs.clear() | ||
|
||
while not stop_event.is_set(): | ||
if len(logs.lines) == 0: | ||
last_fetch = time.time() | ||
# If logs are empty, write initial logs | ||
logs.write( | ||
self.container(self.selected_container) | ||
.logs() | ||
.decode("utf-8") | ||
.strip() | ||
) | ||
|
||
new_logs = ( | ||
self.container(self.selected_container) | ||
.logs(since=last_fetch) | ||
.decode("utf-8") | ||
) | ||
|
||
if len(new_logs) > 0: | ||
last_fetch = time.time() | ||
logs.write(new_logs.rstrip()) | ||
|
||
time.sleep(0.2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import webbrowser | ||
|
||
from rich.text import Text | ||
|
||
from textual import on | ||
from textual.app import ComposeResult | ||
from textual.containers import Center, VerticalScroll | ||
from textual.screen import ModalScreen | ||
from textual.widgets import Static, Markdown, Footer | ||
|
||
HELP_MD = """ | ||
Pocker is a tool for the terminal to do Docker related tasks. | ||
Repository: [https://github.com/pommee/Pocker](https://github.com/pommee/Pocker) | ||
Author: [pommee](https://github.com/pommee) | ||
--- | ||
- `esc` closes this window. | ||
### Navigation | ||
- `q` to quit pocker. | ||
- `?` shows this help modal. | ||
- `l` updates the content window with logs of selected container. | ||
- `a` same as above but displays attributes. | ||
- `f` display content window in full-size. | ||
- `w` will wrap logs/attributes to avoid horizontal scrolling. | ||
### Other keys | ||
- `ctrl+f` or `/` Show find dialog. | ||
""" | ||
|
||
TITLE = rf""" | ||
_____ _____ _______ _ _ _______ ______ | ||
|_____] | | | |____/ |______ |_____/ | ||
| |_____| |_____ | \_ |______ | \_ | ||
""" | ||
|
||
|
||
def get_title() -> Text: | ||
lines = TITLE.splitlines(keepends=True) | ||
return Text.assemble( | ||
*zip( | ||
lines, | ||
[ | ||
"#6076FF", | ||
"#0087FF", | ||
"#00888B", | ||
"#008157", | ||
], | ||
) | ||
) | ||
|
||
|
||
class HelpScreen(ModalScreen): | ||
BINDINGS = [ | ||
("escape", "dismiss"), | ||
] | ||
|
||
def compose(self) -> ComposeResult: | ||
yield Footer() | ||
with VerticalScroll() as vertical_scroll: | ||
with Center(): | ||
yield Static(get_title(), classes="title") | ||
yield Markdown(HELP_MD) | ||
vertical_scroll.border_title = "Help" | ||
|
||
@on(Markdown.LinkClicked) | ||
def on_markdown_link_clicked(self, event: Markdown.LinkClicked) -> None: | ||
self.action_go(event.href) | ||
|
||
def action_go(self, href: str) -> None: | ||
webbrowser.open(href) |
Oops, something went wrong.