mirror of
https://gitlab.sectorq.eu/jaydee/portainer.git
synced 2026-01-29 04:49:44 +01:00
Compare commits
60 Commits
f561508d2e
...
c22287f53b
| Author | SHA1 | Date | |
|---|---|---|---|
| c22287f53b | |||
| f618476534 | |||
| 5865df9abc | |||
| f83ee560c1 | |||
| 5ce8573013 | |||
| e8191802b1 | |||
| e546d0cf3f | |||
| daf219329a | |||
| b601ecc0c3 | |||
| 1a8e532a02 | |||
| d6e4db6dd4 | |||
| 78012cec65 | |||
| ba098499f5 | |||
| ce24b5c00d | |||
| 46143a7c12 | |||
| c731fbe0de | |||
| d878a2baa0 | |||
| e280ea67f7 | |||
| 260eb63262 | |||
| 41d6ec9914 | |||
| 15e442d49b | |||
| 14c31575af | |||
| 406513b4b8 | |||
| 0c1b624972 | |||
| db5209e3fb | |||
| 111c70ef00 | |||
| 8bba4d1d18 | |||
| db45a48106 | |||
| 53438a3fb0 | |||
| 5646e0692d | |||
| 6634cc20fa | |||
| fc9f25a203 | |||
| 8351e9f1b1 | |||
| d007510704 | |||
| 3e202c9fd8 | |||
| d6e5c4087d | |||
| f70ebecc49 | |||
| b8264994c5 | |||
| 6081c44d4c | |||
| abb5fc7708 | |||
| a9eecac96d | |||
| e7bcee762d | |||
| e3fed3304a | |||
| 338aa66565 | |||
| 058554a0ea | |||
| b28a7c8273 | |||
| e464c498ff | |||
| b028a48fc0 | |||
| b2373f7016 | |||
| 1b50b3337c | |||
| ac68b5be6f | |||
| 035abffeab | |||
| 6097d2b442 | |||
| 55bdff1745 | |||
| b385f3db12 | |||
| 94a3ccfd23 | |||
| b0c570d7ba | |||
| 4339a7d769 | |||
| 039078191f | |||
| f5d76d87e0 |
@@ -1,10 +1,20 @@
|
|||||||
stages: # List of stages for jobs, and their order of execution
|
stages: # List of stages for jobs, and their order of execution
|
||||||
|
# - notify_start
|
||||||
- lint
|
- lint
|
||||||
- build
|
- build
|
||||||
- clean
|
- clean
|
||||||
- notify
|
- notify
|
||||||
variables:
|
variables:
|
||||||
GIT_SSH_COMMAND: "ssh -i /home/gitlab-runner/.ssh/id_rsa -o IdentitiesOnly=yes"
|
GIT_SSH_COMMAND: "ssh -i /home/gitlab-runner/.ssh/id_rsa -o IdentitiesOnly=yes"
|
||||||
|
# notify_start:
|
||||||
|
# stage: notify_start # Should be in a later stage than the job that might fail
|
||||||
|
# script:
|
||||||
|
# - column=':'
|
||||||
|
# - echo "${flow_id}"
|
||||||
|
# - curl -XPOST http://192.168.77.101:8123/api/webhook/voice-notifications-tC_8YKxMJIAaQRV5riKuC7Zl --data-raw 'message=portainer build job started'
|
||||||
|
# - rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
||||||
|
# rules:
|
||||||
|
# - if: '$CI_COMMIT_MESSAGE =~ /build/'
|
||||||
lint:
|
lint:
|
||||||
stage: lint
|
stage: lint
|
||||||
image: r.sectorq.eu/jaydee/builder-portainer:latest
|
image: r.sectorq.eu/jaydee/builder-portainer:latest
|
||||||
@@ -22,15 +32,19 @@ lint:
|
|||||||
build-job: # This job runs in the build stage, which runs first.
|
build-job: # This job runs in the build stage, which runs first.
|
||||||
stage: build
|
stage: build
|
||||||
image: r.sectorq.eu/jaydee/builder-portainer:latest
|
image: r.sectorq.eu/jaydee/builder-portainer:latest
|
||||||
|
before_script:
|
||||||
|
- export PYTHONPATH=$PWD
|
||||||
script:
|
script:
|
||||||
- mkdir -p ~/.ssh
|
- mkdir -p ~/.ssh
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
|
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
- chmod 600 ~/.ssh/id_rsa
|
||||||
- pyinstaller --onefile portainer.py
|
- ls -la
|
||||||
#- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.80.222:/myapps/bin/ || true
|
- pyinstaller --onefile --clean --hidden-import=portainer_api portainer.py
|
||||||
|
- strings dist/portainer | grep portainer_api || true
|
||||||
|
- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.80.222:/myapps/bin/ || true
|
||||||
- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.77.12:/myapps/bin/ || true
|
- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.77.12:/myapps/bin/ || true
|
||||||
- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.77.101:/myapps/bin/ || true
|
- scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dist/portainer jd@192.168.77.101:/myapps/bin/ || true
|
||||||
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.*
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- dist/
|
- dist/
|
||||||
@@ -53,7 +67,7 @@ cleanup_on_failure_job:
|
|||||||
script:
|
script:
|
||||||
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
||||||
|
|
||||||
notify:
|
notify_complete:
|
||||||
stage: notify # Should be in a later stage than the job that might fail
|
stage: notify # Should be in a later stage than the job that might fail
|
||||||
when: on_success # <-- This is the key keyword
|
when: on_success # <-- This is the key keyword
|
||||||
script:
|
script:
|
||||||
@@ -61,8 +75,9 @@ notify:
|
|||||||
- echo "${flow_id}"
|
- echo "${flow_id}"
|
||||||
- curl -XPOST http://192.168.77.101:8123/api/webhook/voice-notifications-tC_8YKxMJIAaQRV5riKuC7Zl --data-raw 'message=portainer build job completed'
|
- curl -XPOST http://192.168.77.101:8123/api/webhook/voice-notifications-tC_8YKxMJIAaQRV5riKuC7Zl --data-raw 'message=portainer build job completed'
|
||||||
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
- rm -rf /home/gitlab-runner/builds/1fLwHSKm2/0/jaydee/portainer.tmp
|
||||||
|
rules:
|
||||||
notify2:
|
- if: '$CI_COMMIT_MESSAGE =~ /build/'
|
||||||
|
notify_failed:
|
||||||
stage: notify # Should be in a later stage than the job that might fail
|
stage: notify # Should be in a later stage than the job that might fail
|
||||||
when: on_failure # <-- This is the key keyword
|
when: on_failure # <-- This is the key keyword
|
||||||
script:
|
script:
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"python.defaultInterpreterPath": "../../venvs/portainer/bin/python"
|
||||||
|
}
|
||||||
25
portainer.py
25
portainer.py
@@ -5,18 +5,15 @@ This module provides a wrapper for interacting with the Portainer API
|
|||||||
to manage endpoints, stacks, and containers.
|
to manage endpoints, stacks, and containers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# !/myapps/venvs/portainer/bin/python3
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import argparse
|
import argparse
|
||||||
import tty
|
|
||||||
import termios
|
|
||||||
import hvac
|
import hvac
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
from port import Portainer
|
from portainer_api import PortainerApi
|
||||||
from prompt_toolkit import prompt
|
from prompt_toolkit import prompt
|
||||||
from prompt_toolkit.completion import WordCompleter
|
from prompt_toolkit.completion import WordCompleter
|
||||||
from prompt_toolkit.shortcuts import checkboxlist_dialog
|
from prompt_toolkit.shortcuts import checkboxlist_dialog
|
||||||
@@ -39,7 +36,7 @@ else:
|
|||||||
raise Exception("Failed to authenticate with Vault")
|
raise Exception("Failed to authenticate with Vault")
|
||||||
# Specify the mount point of your KV engine
|
# Specify the mount point of your KV engine
|
||||||
|
|
||||||
VERSION = "0.1.19"
|
VERSION = "0.1.42"
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
"endpoint_id": "vm01",
|
"endpoint_id": "vm01",
|
||||||
@@ -139,10 +136,12 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument("--update", "-u", action="store_true", help="Update service if it exists")
|
parser.add_argument("--update", "-u", action="store_true", help="Update service if it exists")
|
||||||
parser.add_argument("--debug", "-D", action="store_true")
|
parser.add_argument("--debug", "-D", action="store_true")
|
||||||
|
parser.add_argument("--launcher", "-L", action="store_true")
|
||||||
parser.add_argument("--gpu", "-g", action="store_true")
|
parser.add_argument("--gpu", "-g", action="store_true")
|
||||||
parser.add_argument("--timeout", type=int, default=10, help="Request timeout seconds")
|
parser.add_argument("--timeout", type=int, default=10, help="Request timeout seconds")
|
||||||
parser.add_argument("--deploy-mode", "-m", type=str, default="git", help="Deploy mode")
|
parser.add_argument("--deploy-mode", "-m", type=str, default="git", help="Deploy mode")
|
||||||
parser.add_argument("--stack-mode", "-w", default=None, help="Stack mode")
|
parser.add_argument("--stack-mode", "-w", default=None, help="Stack mode")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print("Running version:", VERSION)
|
print("Running version:", VERSION)
|
||||||
print("Environment:", args.site)
|
print("Environment:", args.site)
|
||||||
@@ -426,7 +425,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
os.system("cls" if os.name == "nt" else "clear")
|
os.system("cls" if os.name == "nt" else "clear")
|
||||||
# Example: list endpoints
|
# Example: list endpoints
|
||||||
por = Portainer(cur_config["PORTAINER_SITE"], args)
|
por = PortainerApi(cur_config["PORTAINER_SITE"], args)
|
||||||
por.set_defaults(cur_config)
|
por.set_defaults(cur_config)
|
||||||
if args.debug:
|
if args.debug:
|
||||||
por._debug = True
|
por._debug = True
|
||||||
@@ -547,6 +546,8 @@ if __name__ == "__main__":
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
por.update_service()
|
por.update_service()
|
||||||
|
if args.launcher:
|
||||||
|
input("\nPress ENTER to continue...")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "update_containers":
|
if args.action == "update_containers":
|
||||||
@@ -574,6 +575,8 @@ if __name__ == "__main__":
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
por.print_stacks(args)
|
por.print_stacks(args)
|
||||||
|
if args.launcher:
|
||||||
|
input("Press ENTER to continue...")
|
||||||
# print(json.dumps(por.all_data, indent=2))
|
# print(json.dumps(por.all_data, indent=2))
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
@@ -588,6 +591,8 @@ if __name__ == "__main__":
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
print("\n".join(por.get_containers()))
|
print("\n".join(por.get_containers()))
|
||||||
|
if args.launcher:
|
||||||
|
input("\nPress ENTER to continue...")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "update_stack":
|
if args.action == "update_stack":
|
||||||
@@ -601,11 +606,16 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
|
|
||||||
por.update_stack(args)
|
por.update_stack(args)
|
||||||
|
if args.launcher:
|
||||||
|
input("\nPress ENTER to continue...")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "print_all_data":
|
if args.action == "print_all_data":
|
||||||
print(json.dumps(por.all_data, indent=2))
|
print(json.dumps(por.all_data, indent=2))
|
||||||
|
if args.launcher:
|
||||||
|
input("\nPress ENTER to continue...")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "update_status":
|
if args.action == "update_status":
|
||||||
por.update_status(args.endpoint_id, args.stack)
|
por.update_status(args.endpoint_id, args.stack)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
@@ -617,7 +627,8 @@ if __name__ == "__main__":
|
|||||||
export_data.append([i, eps["by_id"][i]])
|
export_data.append([i, eps["by_id"][i]])
|
||||||
headers = ["EndpointId", "Name"]
|
headers = ["EndpointId", "Name"]
|
||||||
print(tabulate(export_data, headers=headers, tablefmt="github"))
|
print(tabulate(export_data, headers=headers, tablefmt="github"))
|
||||||
|
if args.launcher:
|
||||||
|
input("\nPress ENTER to continue...")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if args.action == "stop_containers":
|
if args.action == "stop_containers":
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from prompt_toolkit.shortcuts import radiolist_dialog
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Portainer:
|
class PortainerApi:
|
||||||
"""
|
"""
|
||||||
Simple wrapper around the module-level Portainer helper functions.
|
Simple wrapper around the module-level Portainer helper functions.
|
||||||
Instantiate with base_url and optional token/timeout and call methods
|
Instantiate with base_url and optional token/timeout and call methods
|
||||||
@@ -532,10 +532,10 @@ class Portainer:
|
|||||||
if not args.autostart:
|
if not args.autostart:
|
||||||
time.sleep(120)
|
time.sleep(120)
|
||||||
cont = []
|
cont = []
|
||||||
for c in self.all_data["containers"][endpoint]:
|
for c in self.all_data["containers"][args.endpoint_id]:
|
||||||
if stack == c or stack == "all":
|
if args.stack == c or args.stack == "all":
|
||||||
cont += self.all_data["containers"][endpoint][c]
|
cont += self.all_data["containers"][args.endpoint_id][c]
|
||||||
self.stop_containers(endpoint, cont)
|
self.stop_containers(args.endpoint_id, cont)
|
||||||
|
|
||||||
def get_endpoints(self, timeout=10):
|
def get_endpoints(self, timeout=10):
|
||||||
'''Get a list of all endpoints.'''
|
'''Get a list of all endpoints.'''
|
||||||
@@ -914,6 +914,7 @@ class Portainer:
|
|||||||
#print(longest)
|
#print(longest)
|
||||||
ok = "\033[92m✔\033[0m"
|
ok = "\033[92m✔\033[0m"
|
||||||
err = "\033[91m✖\033[0m"
|
err = "\033[91m✖\033[0m"
|
||||||
|
updates = []
|
||||||
for service_id in service_ids:
|
for service_id in service_ids:
|
||||||
# print(self.all_data["containers"][self.args.endpoint_id])
|
# print(self.all_data["containers"][self.args.endpoint_id])
|
||||||
|
|
||||||
@@ -934,7 +935,7 @@ class Portainer:
|
|||||||
#print("Recreate")
|
#print("Recreate")
|
||||||
self.recreate_container(service_id, pull)
|
self.recreate_container(service_id, pull)
|
||||||
#print(f"Service {service_dict[service_id]:<{longest}} : updated")
|
#print(f"Service {service_dict[service_id]:<{longest}} : updated")
|
||||||
self.gotify_message(f"Service {service_dict[service_id]} updated")
|
updates.append(service_dict[service_id])
|
||||||
print(ok, end=" ")
|
print(ok, end=" ")
|
||||||
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
||||||
if name.startswith(service_dict[service_id]):
|
if name.startswith(service_dict[service_id]):
|
||||||
@@ -942,7 +943,7 @@ class Portainer:
|
|||||||
else:
|
else:
|
||||||
print(f"\r\033[4m{service_dict[service_id]:<{longest}}\033[0m ", end="", flush=True)
|
print(f"\r\033[4m{service_dict[service_id]:<{longest}}\033[0m ", end="", flush=True)
|
||||||
#print(f"\033[4m{service_dict[service_id]:<{longest}} {err}\033[0m")
|
#print(f"\033[4m{service_dict[service_id]:<{longest}} {err}\033[0m")
|
||||||
self.gotify_message(f"Service update available for {service_dict[service_id]}")
|
updates.append(service_dict[service_id])
|
||||||
print(err, end=" ")
|
print(err, end=" ")
|
||||||
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
||||||
if name.startswith(service_dict[service_id]):
|
if name.startswith(service_dict[service_id]):
|
||||||
@@ -952,6 +953,11 @@ class Portainer:
|
|||||||
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
for name, hash_, image in self.all_data["containers"][self.args.endpoint_id]:
|
||||||
if name.startswith(service_dict[service_id]):
|
if name.startswith(service_dict[service_id]):
|
||||||
print(image)
|
print(image)
|
||||||
|
if len(updates) > 0:
|
||||||
|
if pull:
|
||||||
|
self.gotify_message(f"Services updated: {", ".join(updates)}")
|
||||||
|
else:
|
||||||
|
self.gotify_message(f"Services updates available: {', '.join(updates)}")
|
||||||
print("\033[?25h", end="")
|
print("\033[?25h", end="")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1352,4 +1358,5 @@ class Portainer:
|
|||||||
path = f"/endpoints/{endpoint_id}/docker/secrets/create"
|
path = f"/endpoints/{endpoint_id}/docker/secrets/create"
|
||||||
encoded = base64.b64encode(value.encode()).decode()
|
encoded = base64.b64encode(value.encode()).decode()
|
||||||
data = {"Name": name, "Data": encoded}
|
data = {"Name": name, "Data": encoded}
|
||||||
|
|
||||||
return self._api_post(path, data, timeout=timeout)
|
return self._api_post(path, data, timeout=timeout)
|
||||||
@@ -4,4 +4,5 @@ tabulate
|
|||||||
# Other dev tools
|
# Other dev tools
|
||||||
flake8
|
flake8
|
||||||
pylint
|
pylint
|
||||||
black
|
black
|
||||||
|
docker
|
||||||
Reference in New Issue
Block a user