Compare commits

..

34 Commits

Author SHA1 Message Date
8ba22f79b9 build 2026-01-24 21:07:26 +01:00
ddeb67750f build 2026-01-24 21:05:09 +01:00
12ff88f8e8 build 2026-01-24 21:03:32 +01:00
9d27e804a5 build 2026-01-24 21:02:34 +01:00
1f0a19b7b1 build 2026-01-24 21:00:19 +01:00
5adfbbcf3d build 2026-01-24 20:59:37 +01:00
0dda82be87 build 2026-01-24 20:57:21 +01:00
1a54c1e341 Merge branch 'main' of gitlab.sectorq.eu:jaydee/portainer 2026-01-24 20:55:55 +01:00
8d4bd382ee build 2026-01-24 20:55:51 +01:00
ladislav.dusa
96068d4fb3 Merge branch 'main' of https://gitlab.sectorq.eu/jaydee/portainer 2026-01-13 13:22:24 +01:00
ladislav.dusa
de37276ab6 build 2026-01-13 13:22:20 +01:00
2dc800f7f9 build 2026-01-12 23:30:30 +01:00
ae387a794c build 2026-01-09 17:34:31 +01:00
a3518ec0bb build 2026-01-09 14:07:31 +01:00
ladislav.dusa
3e86a75502 build 2026-01-08 09:06:23 +01:00
ladislav.dusa
11cd76215a build 2026-01-08 08:52:35 +01:00
4bbe283211 build 2026-01-06 22:44:38 +01:00
fc3fe7b837 build 2026-01-06 22:02:06 +01:00
3152014ca3 build 2026-01-05 17:45:29 +01:00
e411c81224 build 2026-01-05 17:43:44 +01:00
8ae696a96a build 2026-01-05 17:35:54 +01:00
abd989a0db build 2026-01-05 14:55:37 +01:00
bb8ef3bdb8 build 2025-12-30 22:08:02 +01:00
99aa451620 build 2025-12-30 21:51:26 +01:00
fd1fcf90a4 build 2025-12-27 19:17:53 +01:00
135447d7aa build 2025-12-27 19:04:45 +01:00
164252534e build 2025-12-27 18:48:26 +01:00
807437c47e build 2025-12-23 22:33:08 +01:00
08a15f3bb9 Merge branch 'main' of gitlab.sectorq.eu:jaydee/portainer 2025-12-23 22:32:41 +01:00
1f4319f4dd build 2025-12-23 22:32:36 +01:00
9800b01ea2 Update .gitlab-ci.yml file 2025-12-23 22:32:18 +01:00
4cd9cfce20 build 2025-12-23 22:30:57 +01:00
12a095e169 build 2025-12-23 22:23:00 +01:00
162c270c02 build 2025-12-23 22:19:02 +01:00
5 changed files with 51 additions and 33 deletions

View File

@@ -30,7 +30,7 @@ build-job: # This job runs in the build stage, which runs first.
- pip install uuid - pip install uuid
#- pyinstaller --onefile --add-data "port.py:." portainer.py #- pyinstaller --onefile --add-data "port.py:." portainer.py
- rm -rf build dist *.spec - rm -rf build dist *.spec
- pyinstaller --onefile --clean portainer.py - pyinstaller --onefile --clean -n portainer main.py
#- 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.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

View File

@@ -12,14 +12,22 @@ import sys
import json import json
import argparse import argparse
import hvac import hvac
import time
import base64
import shutil
import requests
from portainer.api import PortainerApi
from git import Repo
from concurrent.futures import ThreadPoolExecutor
from tabulate import tabulate from tabulate import tabulate
import port
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
from prompt_toolkit.shortcuts import radiolist_dialog from prompt_toolkit.shortcuts import radiolist_dialog
VAULT_ADDR = os.environ.get("VAULT_ADDR", "http://192.168.77.101:8200")
# VAULT_ADDR = os.environ.get("VAULT_ADDR", "http://192.168.77.101:8200")
VAULT_ADDR = os.environ.get("VAULT_ADDR", "https://vault.sectorq.eu")
try: try:
VAULT_TOKEN = os.environ.get("VAULT_TOKEN") VAULT_TOKEN = os.environ.get("VAULT_TOKEN")
if VAULT_TOKEN is None: if VAULT_TOKEN is None:
@@ -36,7 +44,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.50" VERSION = "0.1.55"
defaults = { defaults = {
"endpoint_id": "vm01", "endpoint_id": "vm01",
@@ -241,11 +249,11 @@ def prompt_missing_args(args_in, defaults_in, fields, action=None,stacks=None):
if args.action == "create_stack": if args.action == "create_stack":
# input(json.dumps(stacks, indent=2)) # input(json.dumps(stacks, indent=2))
commands = [ commands = [
'authentik', 'bitwarden', 'bookstack', 'dockermon', 'fail2ban', 'gitea', 'gitlab', 'grafana', 'authentik', 'bitwarden', 'bookstack', 'dockermon', 'duplicati', 'fail2ban', 'gitea', 'gitlab', 'grafana', 'grocy',
'hashicorp', 'home-assistant', 'homepage', 'immich', 'influxdb', 'jupyter', 'kestra', 'mailu3', 'hashicorp', 'home-assistant', 'homebox','homepage', 'immich', 'influxdb', 'jupyter', 'kestra', 'kopia', 'mailu3',
'mealie', 'mediacenter', 'mosquitto', 'motioneye', 'n8n', 'nebula', 'nextcloud', 'nginx', 'mealie', 'mediacenter', 'mosquitto', 'motioneye', 'n8n', 'nebula', 'nextcloud', 'nginx',
'node-red', 'octoprint', 'ollama', 'onlyoffice', 'paperless-ngx', 'pihole', 'portainer-ce', 'rancher', 'registry', 'node-red', 'octoprint', 'ollama', 'onlyoffice', 'paperless-ngx', 'pihole', 'portainer-ce', 'rancher', 'registry',
'regsync', 'semaphore', 'unifibrowser', 'uptime-kuma', 'watchtower', 'wazuh', 'webhub', 'wordpress', 'regsync', 'searxng','semaphore', 'unifibrowser', 'uptime-kuma', 'watchtower', 'wazuh', 'webhub', 'wordpress',
'wud', 'zabbix-server'] 'wud', 'zabbix-server']
try: try:
print(por.all_data['stacks'][defaults_in[f"PORTAINER_ENDPOINT_ID".upper()]]['by_name'].keys()) print(por.all_data['stacks'][defaults_in[f"PORTAINER_ENDPOINT_ID".upper()]]['by_name'].keys())
@@ -402,18 +410,13 @@ if __name__ == "__main__":
] ]
selected_action = radiolist_dialog( selected_action = radiolist_dialog(
title="Select one service", title=f"Select one service - version: {VERSION}",
text="Choose a service:", text="Choose a service:",
values=actions values=actions
).run() ).run()
print("Selected:", selected_action) print("Selected:", selected_action)
# print("Possible actions: \n") # print("Possible actions: \n")
# i = 1 # i = 1
# for a in actions: # for a in actions:
@@ -425,7 +428,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 = port.PortainerApi(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
@@ -675,8 +678,4 @@ if __name__ == "__main__":
sys.exit() sys.exit()
if args.action == "refresh_status": if args.action == "refresh_status":
if args.stack == "all": por.refresh_status(args)
print("Stopping all stacks...")
stcks = por.get_stacks(endpoint_id=args.endpoint_id)
else:
por.refresh_status(args.stack_id)

View File

@@ -141,6 +141,13 @@ class PortainerApi:
self.get_endpoints() self.get_endpoints()
self.get_stacks() self.get_stacks()
def refresh_status(self, args):
for s in self.all_data['stacks']['m-s']['by_id']:
path = f'/stacks/{s}/images_status?refresh=true'
print(path)
res = self._api_get(path, timeout=args.timeout)
def _is_number(self, s): def _is_number(self, s):
"""Check if the input string is a number.""" """Check if the input string is a number."""
try: try:
@@ -159,7 +166,7 @@ class PortainerApi:
response = requests.post( response = requests.post(
"https://gotify.sectorq.eu/message", "https://gotify.sectorq.eu/message",
data=payload, data=payload,
headers={"X-Gotify-Key": "ASn_fIAd5OVjm8c"} headers={"X-Gotify-Key": "A1krRuo8GIW-fpY"}
) )
logger.debug(response.text) logger.debug(response.text)
# print("Status:", response.status_code) # print("Status:", response.status_code)
@@ -343,7 +350,6 @@ class PortainerApi:
# print(stack) # print(stack)
cont = [] cont = []
data = {} data = {}
eps = [ep for ep in self.all_data['endpoints']['by_id'].keys()] eps = [ep for ep in self.all_data['endpoints']['by_id'].keys()]
#input(eps) #input(eps)
for endpoint in eps: for endpoint in eps:
@@ -362,11 +368,15 @@ class PortainerApi:
print(f"failed to get containers from {path}: {e}") print(f"failed to get containers from {path}: {e}")
continue continue
contr = [] contr = []
# print(f"Containers: {containers}")
try: try:
for c in containers: for c in containers:
#input(c) # print(c)
cont.append([c["Names"][0].replace("/", ""),c["Id"], c['Image']]) try:
contr.append([c["Names"][0].replace("/", ""), c["Id"], c['Image']]) cont.append([c["Names"][0].replace("/", ""),c["Id"], c['Image']])
contr.append([c["Names"][0].replace("/", ""), c["Id"], c['Image']])
except:
print("Unable to parse container info")
if self.all_data["endpoints"]["by_id"][endpoint] in data: if self.all_data["endpoints"]["by_id"][endpoint] in data:
data[self.all_data["endpoints"]["by_id"][endpoint]] = contr data[self.all_data["endpoints"]["by_id"][endpoint]] = contr
data[endpoint] = contr data[endpoint] = contr
@@ -489,7 +499,7 @@ class PortainerApi:
stacks_tuples.append((s['Webhook'],s['Name'])) stacks_tuples.append((s['Webhook'],s['Name']))
# print(s['Name'], " : ", s['Webhook']) # print(s['Name'], " : ", s['Webhook'])
stacks_dict = dict(stacks_tuples) stacks_dict = dict(stacks_tuples)
print(stacks_dict) # print(stacks_dict)
#input(stacks_tuples) #input(stacks_tuples)
# stacks_tuples = [(s['AutoUpdate']['Webhook'], s['Name']) for s in stacks if "Webhook" in s['AutoUpdate'] ] # stacks_tuples = [(s['AutoUpdate']['Webhook'], s['Name']) for s in stacks if "Webhook" in s['AutoUpdate'] ]
@@ -514,7 +524,7 @@ class PortainerApi:
values=stacks_tuples values=stacks_tuples
).run() ).run()
stcs = [] stcs = []
input(stack_ids) #input(stack_ids)
if args.stack == "all": if args.stack == "all":
for s in stack_dict: for s in stack_dict:
@@ -524,11 +534,11 @@ class PortainerApi:
if s in stack_ids: if s in stack_ids:
stcs.append([s, stack_dict[s]]) stcs.append([s, stack_dict[s]])
print(stcs) # print(stcs)
with ThreadPoolExecutor(max_workers=10) as exe: with ThreadPoolExecutor(max_workers=10) as exe:
list(exe.map(update, stcs)) list(exe.map(update, stcs))
input('UPDATED') #input('UPDATED')
if not args.autostart: if not args.autostart:
time.sleep(120) time.sleep(120)
cont = [] cont = []
@@ -868,7 +878,6 @@ class PortainerApi:
headers = ["StackID", "Name", "Endpoint"] headers = ["StackID", "Name", "Endpoint"]
print(tabulate.tabulate(data, headers=headers, tablefmt="github")) print(tabulate.tabulate(data, headers=headers, tablefmt="github"))
print(f"Total stacks: {count}") print(f"Total stacks: {count}")
input("Continue...")
# print(sorted(stack_names)) # print(sorted(stack_names))
def update_containers(self): def update_containers(self):
@@ -963,7 +972,14 @@ class PortainerApi:
def update_service(self): def update_service(self):
all_services = self.get_services(self.get_endpoint_id()) all_services = self.get_services(self.get_endpoint_id())
#input(all_services) if self.args.debug:
print(all_services)
if all_services == 503:
print("No services found on this endpoint.")
return False
if len(all_services) == 0:
print("No services found on this endpoint.")
return False
service_tuples = [(s['ID'], s['Spec']['Name']) for s in all_services] service_tuples = [(s['ID'], s['Spec']['Name']) for s in all_services]
service_tuples = sorted(service_tuples, key=lambda x: x[1]) service_tuples = sorted(service_tuples, key=lambda x: x[1])
service_dict = dict(service_tuples) service_dict = dict(service_tuples)
@@ -1031,6 +1047,7 @@ class PortainerApi:
print(err) print(err)
else: else:
print(ok) print(ok)
self.gotify_message(f"Service update process finished")
print("\033[?25h", end="") print("\033[?25h", end="")
return True return True
@@ -1104,7 +1121,7 @@ class PortainerApi:
# print(path) # print(path)
params={"pullImage": pull} params={"pullImage": pull}
try: try:
resp = self._api_post(path, json=params, timeout=20) resp = self._api_post(path, json=params, timeout=120)
#print(resp) #print(resp)
except ValueError as e: except ValueError as e:
print(f"Error restarting service: {e}") print(f"Error restarting service: {e}")

View File

@@ -6,3 +6,5 @@ flake8
pylint pylint
black black
docker docker
hvac
prompt_toolkit