portmgr 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- portmgr-0.1.0/LICENSE +22 -0
- portmgr-0.1.0/PKG-INFO +81 -0
- portmgr-0.1.0/README.md +66 -0
- portmgr-0.1.0/portmgr/__init__.py +2 -0
- portmgr-0.1.0/portmgr/__main__.py +15 -0
- portmgr-0.1.0/portmgr/commands/__init__.py +0 -0
- portmgr-0.1.0/portmgr/commands/attach.py +51 -0
- portmgr-0.1.0/portmgr/commands/build.py +25 -0
- portmgr-0.1.0/portmgr/commands/down.py +27 -0
- portmgr-0.1.0/portmgr/commands/logs.py +19 -0
- portmgr-0.1.0/portmgr/commands/ps.py +19 -0
- portmgr-0.1.0/portmgr/commands/pull.py +19 -0
- portmgr-0.1.0/portmgr/commands/push.py +76 -0
- portmgr-0.1.0/portmgr/commands/scan.py +43 -0
- portmgr-0.1.0/portmgr/commands/stats.py +52 -0
- portmgr-0.1.0/portmgr/commands/stop.py +26 -0
- portmgr-0.1.0/portmgr/commands/top.py +18 -0
- portmgr-0.1.0/portmgr/commands/up.py +26 -0
- portmgr-0.1.0/portmgr/portmgr.py +170 -0
- portmgr-0.1.0/portmgr/wrapper.py +67 -0
- portmgr-0.1.0/portmgr.egg-info/PKG-INFO +81 -0
- portmgr-0.1.0/portmgr.egg-info/SOURCES.txt +27 -0
- portmgr-0.1.0/portmgr.egg-info/dependency_links.txt +1 -0
- portmgr-0.1.0/portmgr.egg-info/not-zip-safe +1 -0
- portmgr-0.1.0/portmgr.egg-info/requires.txt +4 -0
- portmgr-0.1.0/portmgr.egg-info/top_level.txt +1 -0
- portmgr-0.1.0/pyproject.toml +12 -0
- portmgr-0.1.0/setup.cfg +4 -0
- portmgr-0.1.0/setup.py +26 -0
portmgr-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 theascone
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
portmgr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: portmgr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Home-page: https://github.com/Craeckie/portmgr
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: humanfriendly~=10.0
|
|
10
|
+
Requires-Dist: jsonschema~=3.2.0
|
|
11
|
+
Requires-Dist: pyyaml~=6.0.2
|
|
12
|
+
Requires-Dist: tabulate~=0.8.9
|
|
13
|
+
Dynamic: home-page
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# portmgr
|
|
17
|
+
portmgr is a wrapper around [docker-compose](https://docs.docker.com/compose/) that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.
|
|
18
|
+
|
|
19
|
+
Let's say you have organized your compose files like this, you just add a `dckrsub.yml` in each parent folder:
|
|
20
|
+
<pre>
|
|
21
|
+
docker/
|
|
22
|
+
├── <b>dckrsub.yml</b>
|
|
23
|
+
├── reverse-proxy/
|
|
24
|
+
│ └── docker-compose.yml
|
|
25
|
+
├── storage
|
|
26
|
+
│ ├── <b>dckrsub.yml</b>
|
|
27
|
+
│ ├── nextcloud/
|
|
28
|
+
│ │ └── docker-compose.yml
|
|
29
|
+
│ └── immich/
|
|
30
|
+
│ └── docker-compose.yml
|
|
31
|
+
└── scripts
|
|
32
|
+
</pre>
|
|
33
|
+
|
|
34
|
+
Each `dckrsub.yml` has a list of subdirectories, which portmgr should decend into.
|
|
35
|
+
For example, the `dckrsub.yml` in `docker/` might look like this:
|
|
36
|
+
```yaml
|
|
37
|
+
- reverse-proxy
|
|
38
|
+
- storage
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
And the `dckrsub.yml` in `docker/storage/` like this:
|
|
42
|
+
```yaml
|
|
43
|
+
- nextcloud
|
|
44
|
+
- immich
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Now, if you run `portmgr u` in `docker/` it will run `docker compose up -d` in `reverse-proxy/`, `storage/nextcloud/` and `storage/immich/`.
|
|
48
|
+
|
|
49
|
+
portmgr starts from the current directory, so when running it in `docker/storage/`, it will run `docker compose` only in `nextcloud/` and `immich/`. You can also use it in a directory with a `docker-compose.yml` as a shortener for docker-compose commands.
|
|
50
|
+
|
|
51
|
+
### Commands
|
|
52
|
+
The following commands are available. The respective docker-compose commands are in brackets.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
u Create and start containers (up)
|
|
56
|
+
p Pull images (pull)
|
|
57
|
+
s Stop services (stop)
|
|
58
|
+
d Stop and remove containers (down)
|
|
59
|
+
l Show container logs (logs)
|
|
60
|
+
a Run shell in container (exec -it <service> sh)
|
|
61
|
+
b Build images (build)
|
|
62
|
+
c List containers (ps)
|
|
63
|
+
t List processes in containers (top)
|
|
64
|
+
r Build and push to registry (build, push)
|
|
65
|
+
v Scan container images for vulnerabilities
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
You combine multiple commands. For example `portmgr dul`, runs docker compose with `down`, `up` and `logs`, thus stopping, removing and starting all containers and then showing the logs.
|
|
69
|
+
|
|
70
|
+
### Installation
|
|
71
|
+
```
|
|
72
|
+
sudo pip install portmgr
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or build it from source (here using the latest commit on master branch)
|
|
76
|
+
```
|
|
77
|
+
sudo pip install https://github.com/Craeckie/portmgr.git
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Tipps
|
|
81
|
+
If you use portmgr a lot like me, you might want to shorten it to one letter. For bash, you can add `alias p='portmgr'` to `~/.bashrc`. For fish-shell you can add `abbr p portmgr` to `~/.config/fish/config.fish`.
|
portmgr-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# portmgr
|
|
2
|
+
portmgr is a wrapper around [docker-compose](https://docs.docker.com/compose/) that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.
|
|
3
|
+
|
|
4
|
+
Let's say you have organized your compose files like this, you just add a `dckrsub.yml` in each parent folder:
|
|
5
|
+
<pre>
|
|
6
|
+
docker/
|
|
7
|
+
├── <b>dckrsub.yml</b>
|
|
8
|
+
├── reverse-proxy/
|
|
9
|
+
│ └── docker-compose.yml
|
|
10
|
+
├── storage
|
|
11
|
+
│ ├── <b>dckrsub.yml</b>
|
|
12
|
+
│ ├── nextcloud/
|
|
13
|
+
│ │ └── docker-compose.yml
|
|
14
|
+
│ └── immich/
|
|
15
|
+
│ └── docker-compose.yml
|
|
16
|
+
└── scripts
|
|
17
|
+
</pre>
|
|
18
|
+
|
|
19
|
+
Each `dckrsub.yml` has a list of subdirectories, which portmgr should decend into.
|
|
20
|
+
For example, the `dckrsub.yml` in `docker/` might look like this:
|
|
21
|
+
```yaml
|
|
22
|
+
- reverse-proxy
|
|
23
|
+
- storage
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And the `dckrsub.yml` in `docker/storage/` like this:
|
|
27
|
+
```yaml
|
|
28
|
+
- nextcloud
|
|
29
|
+
- immich
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Now, if you run `portmgr u` in `docker/` it will run `docker compose up -d` in `reverse-proxy/`, `storage/nextcloud/` and `storage/immich/`.
|
|
33
|
+
|
|
34
|
+
portmgr starts from the current directory, so when running it in `docker/storage/`, it will run `docker compose` only in `nextcloud/` and `immich/`. You can also use it in a directory with a `docker-compose.yml` as a shortener for docker-compose commands.
|
|
35
|
+
|
|
36
|
+
### Commands
|
|
37
|
+
The following commands are available. The respective docker-compose commands are in brackets.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
u Create and start containers (up)
|
|
41
|
+
p Pull images (pull)
|
|
42
|
+
s Stop services (stop)
|
|
43
|
+
d Stop and remove containers (down)
|
|
44
|
+
l Show container logs (logs)
|
|
45
|
+
a Run shell in container (exec -it <service> sh)
|
|
46
|
+
b Build images (build)
|
|
47
|
+
c List containers (ps)
|
|
48
|
+
t List processes in containers (top)
|
|
49
|
+
r Build and push to registry (build, push)
|
|
50
|
+
v Scan container images for vulnerabilities
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
You combine multiple commands. For example `portmgr dul`, runs docker compose with `down`, `up` and `logs`, thus stopping, removing and starting all containers and then showing the logs.
|
|
54
|
+
|
|
55
|
+
### Installation
|
|
56
|
+
```
|
|
57
|
+
sudo pip install portmgr
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or build it from source (here using the latest commit on master branch)
|
|
61
|
+
```
|
|
62
|
+
sudo pip install https://github.com/Craeckie/portmgr.git
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Tipps
|
|
66
|
+
If you use portmgr a lot like me, you might want to shorten it to one letter. For bash, you can add `alias p='portmgr'` to `~/.bashrc`. For fish-shell you can add `abbr p portmgr` to `~/.config/fish/config.fish`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import unicode_literals
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
if __package__ is None and not hasattr(sys, 'frozen'):
|
|
7
|
+
# direct call of __main__.py
|
|
8
|
+
import os.path
|
|
9
|
+
path = os.path.realpath(os.path.abspath(__file__))
|
|
10
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(path)))
|
|
11
|
+
|
|
12
|
+
import portmgr
|
|
13
|
+
|
|
14
|
+
if __name__ == '__main__':
|
|
15
|
+
portmgr.main()
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
from portmgr.wrapper import getServicesRunning
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def func(action):
|
|
8
|
+
directory = action['directory']
|
|
9
|
+
relative = action['relative']
|
|
10
|
+
|
|
11
|
+
names = getServicesRunning()
|
|
12
|
+
|
|
13
|
+
index = 0
|
|
14
|
+
cont_count = len(names)
|
|
15
|
+
if cont_count == 0:
|
|
16
|
+
print("No containers found!")
|
|
17
|
+
return 1
|
|
18
|
+
elif cont_count > 1:
|
|
19
|
+
i = 0
|
|
20
|
+
for cont in names:
|
|
21
|
+
print("(" + str(i) + ") " + cont)
|
|
22
|
+
i += 1
|
|
23
|
+
|
|
24
|
+
while True:
|
|
25
|
+
choice = input("Choose container: ")
|
|
26
|
+
if choice:
|
|
27
|
+
try:
|
|
28
|
+
index = int(choice)
|
|
29
|
+
if index >= 0 and index < cont_count:
|
|
30
|
+
break
|
|
31
|
+
except ValueError:
|
|
32
|
+
print("Please enter a number!")
|
|
33
|
+
pass
|
|
34
|
+
print("Please enter a number between 0 and " + cont_count - 1 + "!")
|
|
35
|
+
|
|
36
|
+
container_id = names[index]
|
|
37
|
+
print("Attaching to " + container_id)
|
|
38
|
+
subprocess.call(["docker", "exec", "-it", container_id, "sh"])
|
|
39
|
+
|
|
40
|
+
# res = subprocess.call(["docker-compose", "logs", "--follow", "--tail=200"])
|
|
41
|
+
|
|
42
|
+
# if res != 0:
|
|
43
|
+
# print("Error showing logs for " + relative + "!\n")
|
|
44
|
+
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
command_list['a'] = {
|
|
48
|
+
'hlp': 'Attach to process',
|
|
49
|
+
'ord': 'nrm',
|
|
50
|
+
'fnc': func
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(
|
|
9
|
+
['build', '--pull'],
|
|
10
|
+
env={
|
|
11
|
+
'COMPOSE_DOCKER_CLI_BUILD': '1',
|
|
12
|
+
'DOCKER_BUILDKIT': '1'
|
|
13
|
+
},
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if res != 0:
|
|
17
|
+
print("Error building " + relative + "!")
|
|
18
|
+
|
|
19
|
+
return res
|
|
20
|
+
|
|
21
|
+
command_list['b'] = {
|
|
22
|
+
'hlp': 'Build local image',
|
|
23
|
+
'ord': 'nrm',
|
|
24
|
+
'fnc': func
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["down"])
|
|
9
|
+
# p = subprocess.Popen(["docker-compose", "down"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
10
|
+
|
|
11
|
+
# out, err = p.communicate()
|
|
12
|
+
|
|
13
|
+
# if out != "":
|
|
14
|
+
# print(out.decode("UTF-8"))
|
|
15
|
+
|
|
16
|
+
if not res == 0:
|
|
17
|
+
print("Error removing " + relative + "!")
|
|
18
|
+
|
|
19
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
20
|
+
|
|
21
|
+
return 0
|
|
22
|
+
|
|
23
|
+
command_list['d'] = {
|
|
24
|
+
'hlp': 'Stop and remove container',
|
|
25
|
+
'ord': 'rev',
|
|
26
|
+
'fnc': func
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["logs", "--follow", "--tail=200"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error showing logs for " + relative + "!\n")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['l'] = {
|
|
16
|
+
'hlp': 'Show logs',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["ps"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error listing containers for " + relative + "!\n")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['c'] = {
|
|
16
|
+
'hlp': 'List containers',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def func(action):
|
|
5
|
+
directory = action['directory']
|
|
6
|
+
relative = action['relative']
|
|
7
|
+
|
|
8
|
+
res = runCompose(["pull"])
|
|
9
|
+
|
|
10
|
+
if res != 0:
|
|
11
|
+
print("Error pulling " + relative + "!")
|
|
12
|
+
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
command_list['p'] = {
|
|
16
|
+
'hlp': 'Pull image from repository',
|
|
17
|
+
'ord': 'nrm',
|
|
18
|
+
'fnc': func
|
|
19
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose, runBuildx
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from portmgr.wrapper import getServices
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def func(action):
|
|
9
|
+
directory = action['directory']
|
|
10
|
+
relative = action['relative']
|
|
11
|
+
|
|
12
|
+
services = getServices(includeOnlyBuildable=True)
|
|
13
|
+
|
|
14
|
+
print('Services to build: ' + ', '.join(services))
|
|
15
|
+
|
|
16
|
+
res = 0
|
|
17
|
+
for service in services.keys():
|
|
18
|
+
print(f"\nBuilding {service}")
|
|
19
|
+
|
|
20
|
+
new_res = 2
|
|
21
|
+
if multi_platform := os.environ.get("PORTMGR_MULTI_PLATFORM", "").lower():
|
|
22
|
+
try:
|
|
23
|
+
new_res = runBuildx(
|
|
24
|
+
['bake',
|
|
25
|
+
'--pull',
|
|
26
|
+
'--push',
|
|
27
|
+
'--set', f'*.platform={multi_platform}',
|
|
28
|
+
service
|
|
29
|
+
], timeout=1200 #kill after 20mins
|
|
30
|
+
)
|
|
31
|
+
if new_res != 0:
|
|
32
|
+
res = new_res
|
|
33
|
+
print(f"Error building {service}!")
|
|
34
|
+
except TimeoutExpired:
|
|
35
|
+
print(f"Error building {service}! Build timed out.")
|
|
36
|
+
else:
|
|
37
|
+
try:
|
|
38
|
+
new_res = runCompose(
|
|
39
|
+
['build',
|
|
40
|
+
'--pull',
|
|
41
|
+
'--force-rm',
|
|
42
|
+
'--compress',
|
|
43
|
+
service
|
|
44
|
+
], timeout=1200
|
|
45
|
+
)
|
|
46
|
+
if new_res != 0:
|
|
47
|
+
res = new_res
|
|
48
|
+
print(f"Error building {service}!")
|
|
49
|
+
else:
|
|
50
|
+
new_res = runCompose(
|
|
51
|
+
['push',
|
|
52
|
+
'--ignore-push-failures',
|
|
53
|
+
service
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
print(f"Error pushing {service}!")
|
|
57
|
+
except TimeoutExpired:
|
|
58
|
+
print(f"Error building {service}! Build timed out.")
|
|
59
|
+
if new_res != 1:
|
|
60
|
+
res = new_res
|
|
61
|
+
if os.environ.get("PORTMGR_CLEAN_AFTER_PUSH", "").lower() == "true":
|
|
62
|
+
subprocess.call(['docker', 'system', 'prune', '--all', '--force'])
|
|
63
|
+
subprocess.call(['docker', 'buildx', 'prune', '--all', '--force'])
|
|
64
|
+
|
|
65
|
+
if res != 0:
|
|
66
|
+
print("Error building&pushing " + relative + "!")
|
|
67
|
+
return res
|
|
68
|
+
|
|
69
|
+
return res
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
command_list['r'] = {
|
|
73
|
+
'hlp': 'build, push to registry & remove image',
|
|
74
|
+
'ord': 'nrm',
|
|
75
|
+
'fnc': func
|
|
76
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from portmgr import command_list
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
from portmgr.wrapper import getImages
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def func(action):
|
|
8
|
+
directory = action['directory']
|
|
9
|
+
relative = action['relative']
|
|
10
|
+
|
|
11
|
+
images = getImages()
|
|
12
|
+
|
|
13
|
+
res = 0
|
|
14
|
+
|
|
15
|
+
for image in images:
|
|
16
|
+
image_name = image["Name"]
|
|
17
|
+
print(f'Scanning {image_name} of {image["ContainerName"]}')
|
|
18
|
+
scan_res = subprocess.run(['trivy', '-q', 'image', '-s', 'CRITICAL', '--exit-code', '1', image_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
19
|
+
if scan_res.returncode != 0:
|
|
20
|
+
print('Found vulnerabilites:')
|
|
21
|
+
print(scan_res.stdout)
|
|
22
|
+
res = scan_res.returncode
|
|
23
|
+
|
|
24
|
+
#print("Name: %s" % container.name)
|
|
25
|
+
#names.append(container.name)
|
|
26
|
+
#config_str = subprocess.check_output(["docker", "inspect", "-f", '{{json .}}', container.id], stdout=subprocess.PIPE)
|
|
27
|
+
#json.loads(config_str)
|
|
28
|
+
#print("IP: %s" % ip)
|
|
29
|
+
#print(container.inspect)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
#ID = subprocess.run(["docker-compose", "ps", '-q'], stdout=subprocess.PIPE).stdout
|
|
33
|
+
|
|
34
|
+
#if res != 0:
|
|
35
|
+
# print("Error listing containers for " + relative + "!\n")
|
|
36
|
+
|
|
37
|
+
return res
|
|
38
|
+
|
|
39
|
+
command_list['v'] = {
|
|
40
|
+
'hlp': 'Scan container images for vulnerabilities',
|
|
41
|
+
'ord': 'nrm',
|
|
42
|
+
'fnc': func
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from functools import cmp_to_key
|
|
2
|
+
|
|
3
|
+
from portmgr import command_list
|
|
4
|
+
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
from humanfriendly import format_size, parse_size
|
|
7
|
+
|
|
8
|
+
from portmgr.wrapper import getStats
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def func(action):
|
|
12
|
+
directory = action['directory']
|
|
13
|
+
relative = action['relative']
|
|
14
|
+
|
|
15
|
+
stats_list = getStats()
|
|
16
|
+
|
|
17
|
+
values = []
|
|
18
|
+
has_network_stats = False
|
|
19
|
+
for stats in stats_list:
|
|
20
|
+
name = stats['Name'] if 'Name' in stats else stats['Container']
|
|
21
|
+
#memory = stats["memory_stats"]
|
|
22
|
+
#usage = format_size(memory['usage'])
|
|
23
|
+
#limit = format_size(memory['limit'])
|
|
24
|
+
memory_string = stats["MemUsage"]
|
|
25
|
+
usage, limit = [p.strip() for p in memory_string.split('/')]
|
|
26
|
+
if 'NetIO' in stats:
|
|
27
|
+
network = stats["NetIO"]
|
|
28
|
+
received, sent = [p.strip() for p in memory_string.split('/')]
|
|
29
|
+
#received = format_size(sum(stats['rx_bytes'] for iface, stats in network.items()))
|
|
30
|
+
#sent = format_size(sum(stats['tx_bytes'] for iface, stats in network.items()))
|
|
31
|
+
columns = (stats['Name'], usage, limit, received, sent)
|
|
32
|
+
has_network_stats = True
|
|
33
|
+
else:
|
|
34
|
+
columns = (stats['Name'], usage, limit)
|
|
35
|
+
values.append(columns)
|
|
36
|
+
if values:
|
|
37
|
+
# sort by memory usage
|
|
38
|
+
values = sorted(values, key=cmp_to_key(lambda s1, s2: parse_size(s2[1]) - parse_size(s1[1])))
|
|
39
|
+
print(tabulate(values,
|
|
40
|
+
headers=['Service', 'Mem Usage', 'Mem Limit', 'Net Recv', 'Net Sent'],
|
|
41
|
+
colalign=['left', 'right', 'right', 'right', 'right'] if has_network_stats
|
|
42
|
+
else ['left', 'right', 'right']))
|
|
43
|
+
print('')
|
|
44
|
+
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
command_list['o'] = {
|
|
49
|
+
'hlp': 'Show container stats',
|
|
50
|
+
'ord': 'nrm',
|
|
51
|
+
'fnc': func
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
directory = action['directory']
|
|
5
|
+
relative = action['relative']
|
|
6
|
+
|
|
7
|
+
res = runCompose(["stop"])
|
|
8
|
+
# p = subprocess.Popen(["docker-compose", "stop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
9
|
+
|
|
10
|
+
#out, err = p.communicate()
|
|
11
|
+
|
|
12
|
+
#if out != "":
|
|
13
|
+
# print(out.decode("UTF-8"))
|
|
14
|
+
|
|
15
|
+
if res != 0:
|
|
16
|
+
print("Error stopping " + relative + "!")
|
|
17
|
+
|
|
18
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
19
|
+
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
command_list['s'] = {
|
|
23
|
+
'hlp': 'Stop container',
|
|
24
|
+
'ord': 'rev',
|
|
25
|
+
'fnc': func
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from portmgr import command_list, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
relative = action['relative']
|
|
5
|
+
|
|
6
|
+
res = runCompose(["top"])
|
|
7
|
+
|
|
8
|
+
if res != 0:
|
|
9
|
+
print("Error listing processes of containers in " + relative + "!\n")
|
|
10
|
+
|
|
11
|
+
return 0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
command_list['t'] = {
|
|
15
|
+
'hlp': 'List processes in containers',
|
|
16
|
+
'ord': 'nrm',
|
|
17
|
+
'fnc': func
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from portmgr import command_list, bcolors, runCompose
|
|
2
|
+
|
|
3
|
+
def func(action):
|
|
4
|
+
directory = action['directory']
|
|
5
|
+
relative = action['relative']
|
|
6
|
+
|
|
7
|
+
res = runCompose(["up", "-d"])
|
|
8
|
+
# p = subprocess.Popen(["docker-compose", "up", "-d"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
9
|
+
|
|
10
|
+
# out, err = p.communicate()
|
|
11
|
+
|
|
12
|
+
# if out != "":
|
|
13
|
+
# print(out.decode("UTF-8"))
|
|
14
|
+
|
|
15
|
+
if res != 0:
|
|
16
|
+
print("Error creating " + relative + "!")
|
|
17
|
+
|
|
18
|
+
# print(bcolors.FAIL + err.decode("UTF-8") + bcolors.ENDC)
|
|
19
|
+
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
command_list['u'] = {
|
|
23
|
+
'hlp': 'Create container',
|
|
24
|
+
'ord': 'nrm',
|
|
25
|
+
'fnc': func
|
|
26
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
import os, sys
|
|
3
|
+
import argparse
|
|
4
|
+
import importlib
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MyParser(argparse.ArgumentParser):
|
|
11
|
+
def error(self, message):
|
|
12
|
+
sys.stderr.write('Error: %s\n' % message)
|
|
13
|
+
self.print_help()
|
|
14
|
+
sys.exit(2)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
sub_names = [x.strip() for x in re.split('[ ,;]+', os.environ.get('PORTMGR_SUB_NAME', 'dckrsub.yml'))]
|
|
18
|
+
compose_names = [x.strip() for x in
|
|
19
|
+
os.environ.get('PORTMGR_COMPOSE_NAME', 'docker-compose.yml, docker-compose.yaml').split(',')]
|
|
20
|
+
|
|
21
|
+
# sub_scheme_name = 'dckrsub.schema.yml'
|
|
22
|
+
|
|
23
|
+
src_path = os.path.dirname(os.path.abspath(__file__))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# conf_scheme_path = os.path.join(src_path, sub_scheme_name)
|
|
27
|
+
# sub_scheme_path = os.path.join(src_path, sub_scheme_name)
|
|
28
|
+
|
|
29
|
+
# conf_scheme = dckrjsn.read_json(conf_scheme_path)
|
|
30
|
+
# sub_scheme = dckrjsn.read_json(sub_scheme_path)
|
|
31
|
+
|
|
32
|
+
class bcolors:
|
|
33
|
+
HEADER = '\033[95m'
|
|
34
|
+
OKBLUE = '\033[94m'
|
|
35
|
+
OKGREEN = '\033[92m'
|
|
36
|
+
WARNING = '\033[93m'
|
|
37
|
+
FAIL = '\033[91m'
|
|
38
|
+
ENDC = '\033[0m'
|
|
39
|
+
BOLD = '\033[1m'
|
|
40
|
+
UNDERLINE = '\033[4m'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
command_list = {}
|
|
44
|
+
action_list = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def addCommand(cur_directory):
|
|
48
|
+
if not any(action['directory'] == cur_directory for action in action_list):
|
|
49
|
+
relative_dir = os.path.relpath(cur_directory, base_directory)
|
|
50
|
+
if relative_dir == '.':
|
|
51
|
+
relative_dir = os.path.basename(os.path.normpath(cur_directory))
|
|
52
|
+
action_list.append({
|
|
53
|
+
'directory': cur_directory,
|
|
54
|
+
'relative': relative_dir
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def read_yaml(path):
|
|
59
|
+
with open(path, 'r') as stream:
|
|
60
|
+
try:
|
|
61
|
+
return yaml.load(stream, Loader=yaml.SafeLoader)
|
|
62
|
+
except yaml.YAMLError as exc:
|
|
63
|
+
print(exc)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def traverse(cur_directory):
|
|
67
|
+
# print("Traversing in " + cur_directory)
|
|
68
|
+
for sub_name in sub_names:
|
|
69
|
+
sub_path = os.path.join(cur_directory, sub_name)
|
|
70
|
+
compose_paths = [os.path.join(cur_directory, name) for name in compose_names]
|
|
71
|
+
|
|
72
|
+
# print("Checking file at " + sub_path)
|
|
73
|
+
if os.path.isfile(sub_path): # has sub folders
|
|
74
|
+
# print("Has sub folders!")
|
|
75
|
+
# sub_folders = dckrjsn.read_json(sub_path, sch = sub_scheme)
|
|
76
|
+
sub_folders = read_yaml(sub_path)
|
|
77
|
+
for sub_folder in sub_folders:
|
|
78
|
+
# print("Checking out " + sub_folder)
|
|
79
|
+
next_directory = os.path.join(cur_directory, sub_folder)
|
|
80
|
+
traverse(next_directory)
|
|
81
|
+
elif any(os.path.isfile(path) for path in compose_paths): # has a docker-compose file
|
|
82
|
+
addCommand(cur_directory)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main():
|
|
86
|
+
# global cli
|
|
87
|
+
global base_directory
|
|
88
|
+
|
|
89
|
+
# cli = docker.Client('unix://var/run/docker.sock')
|
|
90
|
+
|
|
91
|
+
# Include external source files for commands
|
|
92
|
+
# These fill the m_cmd list
|
|
93
|
+
for file in os.listdir(os.path.join(src_path, 'commands')):
|
|
94
|
+
ext_file = os.path.splitext(file)
|
|
95
|
+
|
|
96
|
+
if ext_file[1] == '.py' and not ext_file[0] == '__init__':
|
|
97
|
+
importlib.import_module('portmgr.commands.' + ext_file[0])
|
|
98
|
+
|
|
99
|
+
parser = MyParser()
|
|
100
|
+
parser.add_argument('-D',
|
|
101
|
+
dest='base_directory',
|
|
102
|
+
action='store',
|
|
103
|
+
default='',
|
|
104
|
+
help='Set working directory')
|
|
105
|
+
# parser.add_argument('-R',
|
|
106
|
+
# dest='recursive',
|
|
107
|
+
# action='store_true',
|
|
108
|
+
# help='Use dckrsub.json files to recursively apply operations')
|
|
109
|
+
|
|
110
|
+
for cmd in command_list.items():
|
|
111
|
+
parser.add_argument('-' + cmd[0],
|
|
112
|
+
dest='a_cmd',
|
|
113
|
+
action='append_const',
|
|
114
|
+
const=cmd[0],
|
|
115
|
+
help=cmd[1]['hlp'])
|
|
116
|
+
|
|
117
|
+
argv = sys.argv[1:]
|
|
118
|
+
if len(argv) == 1 and not argv[0].startswith('-'):
|
|
119
|
+
argv[0] = '-' + argv[0]
|
|
120
|
+
args = parser.parse_args(argv)
|
|
121
|
+
|
|
122
|
+
if len(sys.argv) == 1:
|
|
123
|
+
parser.print_help()
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
|
|
126
|
+
base_directory = os.path.join(os.getcwd(), args.base_directory)
|
|
127
|
+
|
|
128
|
+
# if args.recursive:
|
|
129
|
+
traverse(base_directory)
|
|
130
|
+
# else:
|
|
131
|
+
# addCommand(base_directory)
|
|
132
|
+
|
|
133
|
+
last_cmd = ''
|
|
134
|
+
for cmd in args.a_cmd: # loop over all passed arguments (t, r, u)
|
|
135
|
+
cur_cmd = command_list[cmd]
|
|
136
|
+
cmd_function = cur_cmd['fnc']
|
|
137
|
+
cmd_order = cur_cmd['ord'];
|
|
138
|
+
|
|
139
|
+
# if last_cmd == 'r' and cur_cmd == 'u':
|
|
140
|
+
# print("Waiting 3 seconds.. ")
|
|
141
|
+
# sleep(3)
|
|
142
|
+
|
|
143
|
+
if cmd_order == 'nrm':
|
|
144
|
+
action_list_sorted = action_list
|
|
145
|
+
elif cmd_order == 'rev':
|
|
146
|
+
action_list_sorted = reversed(action_list)
|
|
147
|
+
else:
|
|
148
|
+
exit(1)
|
|
149
|
+
|
|
150
|
+
failed_list = []
|
|
151
|
+
|
|
152
|
+
for action in action_list_sorted:
|
|
153
|
+
origWD = os.getcwd()
|
|
154
|
+
newWD = action['directory']
|
|
155
|
+
os.chdir(newWD)
|
|
156
|
+
print('-> ' + action["relative"])
|
|
157
|
+
if cmd_function(action) != 0: # execute the function through reflection
|
|
158
|
+
failed_list.append(action)
|
|
159
|
+
os.chdir(origWD)
|
|
160
|
+
if failed_list:
|
|
161
|
+
print('Failed containers:')
|
|
162
|
+
for action in failed_list:
|
|
163
|
+
print('- ' + action['relative'])
|
|
164
|
+
print("")
|
|
165
|
+
|
|
166
|
+
exit(0)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == '__main__':
|
|
170
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from subprocess import run, check_output
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def getServices(includeOnlyBuildable=False):
|
|
7
|
+
data = check_output(['docker', 'compose', 'config', '--format', 'json'])
|
|
8
|
+
config = json.loads(data)
|
|
9
|
+
services = config['services']
|
|
10
|
+
if includeOnlyBuildable:
|
|
11
|
+
services = {name: values for name, values in services.items() if 'build' in values.keys()}
|
|
12
|
+
return services
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def getServicesRunning():
|
|
16
|
+
data = check_output(['docker', 'compose', 'ps', '--format', 'json'])
|
|
17
|
+
try:
|
|
18
|
+
lines = data.decode().splitlines()
|
|
19
|
+
if len(lines) > 1:
|
|
20
|
+
container_list = []
|
|
21
|
+
for l in lines:
|
|
22
|
+
#print(f'l: {l}')
|
|
23
|
+
container_list.append(json.loads(l.strip()))
|
|
24
|
+
else:
|
|
25
|
+
container_list = json.loads(data)
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(e)
|
|
28
|
+
print(data.decode())
|
|
29
|
+
if isinstance(container_list, dict):
|
|
30
|
+
container_names = [container_list['Name']]
|
|
31
|
+
else:
|
|
32
|
+
container_names = [s['Name'] for s in container_list]
|
|
33
|
+
return container_names
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def getImages():
|
|
37
|
+
data = check_output(['docker', 'compose', 'images', '--format', 'json'])
|
|
38
|
+
image_list = json.loads(data)
|
|
39
|
+
images = [
|
|
40
|
+
{'ID': image['ID'],
|
|
41
|
+
'Name': image['Repository'],
|
|
42
|
+
'ContainerName': image['ContainerName'],
|
|
43
|
+
'Tag': image['Tag']}
|
|
44
|
+
for image in image_list
|
|
45
|
+
]
|
|
46
|
+
return images
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def getStats():
|
|
50
|
+
containers = getServicesRunning()
|
|
51
|
+
data = check_output(['docker', 'stats', '--format', 'json', '--no-stream'] + containers, text=True).strip()
|
|
52
|
+
data_lines = data.split('\n')
|
|
53
|
+
stats = [json.loads(line) for line in data_lines]
|
|
54
|
+
return stats
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def runCompose(args, **kwargs):
|
|
58
|
+
command = ['docker', 'compose']
|
|
59
|
+
if os.environ.get("PORTMGR_IN_SCRIPT", "").lower() == "true":
|
|
60
|
+
command += ["--ansi", "never"]
|
|
61
|
+
command += list(args)
|
|
62
|
+
return run(command, **kwargs).returncode
|
|
63
|
+
|
|
64
|
+
def runBuildx(args, **kwargs):
|
|
65
|
+
command = ['docker', 'buildx']
|
|
66
|
+
command += list(args)
|
|
67
|
+
return run(command, **kwargs).returncode
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: portmgr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Home-page: https://github.com/Craeckie/portmgr
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: humanfriendly~=10.0
|
|
10
|
+
Requires-Dist: jsonschema~=3.2.0
|
|
11
|
+
Requires-Dist: pyyaml~=6.0.2
|
|
12
|
+
Requires-Dist: tabulate~=0.8.9
|
|
13
|
+
Dynamic: home-page
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# portmgr
|
|
17
|
+
portmgr is a wrapper around [docker-compose](https://docs.docker.com/compose/) that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.
|
|
18
|
+
|
|
19
|
+
Let's say you have organized your compose files like this, you just add a `dckrsub.yml` in each parent folder:
|
|
20
|
+
<pre>
|
|
21
|
+
docker/
|
|
22
|
+
├── <b>dckrsub.yml</b>
|
|
23
|
+
├── reverse-proxy/
|
|
24
|
+
│ └── docker-compose.yml
|
|
25
|
+
├── storage
|
|
26
|
+
│ ├── <b>dckrsub.yml</b>
|
|
27
|
+
│ ├── nextcloud/
|
|
28
|
+
│ │ └── docker-compose.yml
|
|
29
|
+
│ └── immich/
|
|
30
|
+
│ └── docker-compose.yml
|
|
31
|
+
└── scripts
|
|
32
|
+
</pre>
|
|
33
|
+
|
|
34
|
+
Each `dckrsub.yml` has a list of subdirectories, which portmgr should decend into.
|
|
35
|
+
For example, the `dckrsub.yml` in `docker/` might look like this:
|
|
36
|
+
```yaml
|
|
37
|
+
- reverse-proxy
|
|
38
|
+
- storage
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
And the `dckrsub.yml` in `docker/storage/` like this:
|
|
42
|
+
```yaml
|
|
43
|
+
- nextcloud
|
|
44
|
+
- immich
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Now, if you run `portmgr u` in `docker/` it will run `docker compose up -d` in `reverse-proxy/`, `storage/nextcloud/` and `storage/immich/`.
|
|
48
|
+
|
|
49
|
+
portmgr starts from the current directory, so when running it in `docker/storage/`, it will run `docker compose` only in `nextcloud/` and `immich/`. You can also use it in a directory with a `docker-compose.yml` as a shortener for docker-compose commands.
|
|
50
|
+
|
|
51
|
+
### Commands
|
|
52
|
+
The following commands are available. The respective docker-compose commands are in brackets.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
u Create and start containers (up)
|
|
56
|
+
p Pull images (pull)
|
|
57
|
+
s Stop services (stop)
|
|
58
|
+
d Stop and remove containers (down)
|
|
59
|
+
l Show container logs (logs)
|
|
60
|
+
a Run shell in container (exec -it <service> sh)
|
|
61
|
+
b Build images (build)
|
|
62
|
+
c List containers (ps)
|
|
63
|
+
t List processes in containers (top)
|
|
64
|
+
r Build and push to registry (build, push)
|
|
65
|
+
v Scan container images for vulnerabilities
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
You combine multiple commands. For example `portmgr dul`, runs docker compose with `down`, `up` and `logs`, thus stopping, removing and starting all containers and then showing the logs.
|
|
69
|
+
|
|
70
|
+
### Installation
|
|
71
|
+
```
|
|
72
|
+
sudo pip install portmgr
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or build it from source (here using the latest commit on master branch)
|
|
76
|
+
```
|
|
77
|
+
sudo pip install https://github.com/Craeckie/portmgr.git
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Tipps
|
|
81
|
+
If you use portmgr a lot like me, you might want to shorten it to one letter. For bash, you can add `alias p='portmgr'` to `~/.bashrc`. For fish-shell you can add `abbr p portmgr` to `~/.config/fish/config.fish`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
portmgr/__init__.py
|
|
6
|
+
portmgr/__main__.py
|
|
7
|
+
portmgr/portmgr.py
|
|
8
|
+
portmgr/wrapper.py
|
|
9
|
+
portmgr.egg-info/PKG-INFO
|
|
10
|
+
portmgr.egg-info/SOURCES.txt
|
|
11
|
+
portmgr.egg-info/dependency_links.txt
|
|
12
|
+
portmgr.egg-info/not-zip-safe
|
|
13
|
+
portmgr.egg-info/requires.txt
|
|
14
|
+
portmgr.egg-info/top_level.txt
|
|
15
|
+
portmgr/commands/__init__.py
|
|
16
|
+
portmgr/commands/attach.py
|
|
17
|
+
portmgr/commands/build.py
|
|
18
|
+
portmgr/commands/down.py
|
|
19
|
+
portmgr/commands/logs.py
|
|
20
|
+
portmgr/commands/ps.py
|
|
21
|
+
portmgr/commands/pull.py
|
|
22
|
+
portmgr/commands/push.py
|
|
23
|
+
portmgr/commands/scan.py
|
|
24
|
+
portmgr/commands/stats.py
|
|
25
|
+
portmgr/commands/stop.py
|
|
26
|
+
portmgr/commands/top.py
|
|
27
|
+
portmgr/commands/up.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
portmgr
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "portmgr"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"humanfriendly~=10.0",
|
|
9
|
+
"jsonschema~=3.2.0",
|
|
10
|
+
"pyyaml~=6.0.2",
|
|
11
|
+
"tabulate~=0.8.9",
|
|
12
|
+
]
|
portmgr-0.1.0/setup.cfg
ADDED
portmgr-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='portmgr',
|
|
5
|
+
version='1.8.0dev0',
|
|
6
|
+
url="https://github.com/Craeckie/portmgr",
|
|
7
|
+
description="Simple command interface to manage multiple Docker container",
|
|
8
|
+
packages=find_packages(), #['portmgr'],
|
|
9
|
+
license='Creative Commons Attribution-Noncommercial-Share Alike license',
|
|
10
|
+
long_description=open('README.md').read(),
|
|
11
|
+
long_description_content_type="text/markdown",
|
|
12
|
+
zip_safe=False,
|
|
13
|
+
|
|
14
|
+
install_requires=[
|
|
15
|
+
'jsonschema',
|
|
16
|
+
'tabulate',
|
|
17
|
+
'PyYAML',
|
|
18
|
+
'jsonschema',
|
|
19
|
+
'humanfriendly'
|
|
20
|
+
],
|
|
21
|
+
entry_points = {
|
|
22
|
+
'console_scripts': [
|
|
23
|
+
'portmgr = portmgr:main'
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
)
|