From 53031f1b8b3b5eb6c4f1fec8e49f276b34982c01 Mon Sep 17 00:00:00 2001 From: jaydee Date: Wed, 10 Dec 2025 18:41:19 +0100 Subject: [PATCH] build --- port.py | 78 +++++++++++++++++++++++++++++++++++++++++++++------- portainer.py | 16 +++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/port.py b/port.py index 66f99f3..2ceac88 100644 --- a/port.py +++ b/port.py @@ -12,7 +12,10 @@ import base64 import tabulate from git import Repo import requests - +from prompt_toolkit import prompt +from prompt_toolkit.completion import WordCompleter +from prompt_toolkit.shortcuts import checkboxlist_dialog +from prompt_toolkit.shortcuts import radiolist_dialog logger = logging.getLogger(__name__) @@ -156,6 +159,14 @@ class Portainer: resp = requests.post(url, headers=headers, json=json, timeout=timeout) return resp.text + def _api_put(self, path, json="", timeout=120): + url = f"{self.base_url.rstrip('/')}{path}" + headers = {"X-API-Key": f"{self.token}"} + # print(url) + # print(json) + resp = requests.put(url, headers=headers, json=json, timeout=timeout) + return resp.text + def _api_post_file(self, path, endpoint_id, name, envs, file, timeout=120): # input("API POST2 called. Press Enter to continue.") """Example authenticated GET request to Portainer API.""" @@ -261,6 +272,13 @@ class Portainer: # input(json.dumps(self.stacks_all,indent=2)) return stcks + def get_services(self, endpoint, stack, timeout=30): + '''Get a list of services for a specific stack on an endpoint.''' + path = f"/endpoints/{self.all_data['endpoints']['by_name'][endpoint]}/docker/services" + #path += f'?filters={{"label": ["com.docker.compose.project={stack}"]}}' + services = self._api_get(path, timeout=timeout) + return services + def update_status(self, endpoint, stack): '''Get the update status of a specific stack on an endpoint.''' path = f"/stacks/{self.all_data['stacks'][endpoint]['by_name'][stack]}/images_status?refresh=true" @@ -480,15 +498,13 @@ class Portainer: return 1 def create_stack( - self, - endpoint, - stacks=None, - mode="git", - autostart=False, - stack_mode="swarm", - ): - - + self, + endpoint, + stacks=None, + mode="git", + autostart=False, + stack_mode="swarm", + ): for stack in stacks: if stack_mode == "swarm": swarm_id = self.get_swarm_id(endpoint) @@ -705,6 +721,7 @@ class Portainer: } self._api_post_file(path, self.endpoint_id, stack, envs, file) + def print_stacks(self, endpoint="all"): """Print a table of stacks, optionally filtered by endpoint.""" stacks = self.get_stacks() @@ -739,6 +756,47 @@ class Portainer: print(f"Total stacks: {count}") # print(sorted(stack_names)) + def restart_service(self, endpoint_id, service_id): + stacks = [(s["Id"], s["Name"]) for s in self.get_stacks(endpoint_id)] + stack_id = radiolist_dialog( + title="Select one service", + text="Choose a service:", + values=stacks + ).run() + service_dict = dict(stacks) + services = self.get_services(self.endpoint_name, stack_id) + svc_name = service_dict.get(stack_id) + stack_svcs = [] + svc_menu = [] + for s in services: + try: + if svc_name in s['Spec']['Name']: + stack_svcs.append([s['Version']['Index'], s['Spec']['Name']]) + svc_menu.append([s['ID'], s['Spec']['Name']]) + except KeyError as e: + print(e) + + + service_id = radiolist_dialog( + title="Select one service", + text="Choose a service:", + values=svc_menu + ).run() + + + """Restart a service on an endpoint.""" + path = f"/endpoints/{self.endpoint_id}/forceupdateservice" + params={"serviceID": service_id, "pullImage": False} + try: + resp = self._api_put(path, json=params, timeout=20) + print(resp) + except ValueError as e: + print(f"Error restarting service: {e}") + return [] + + print(f"Service {service_id} : restarted") + return True + def start_stack(self, stack=None, endpoint_id=None): """Start one stack or all stacks on an endpoint.""" if endpoint_id is not None: diff --git a/portainer.py b/portainer.py index 0f0c1e6..abb946b 100755 --- a/portainer.py +++ b/portainer.py @@ -392,6 +392,7 @@ if __name__ == "__main__": ("delete_stack","delete_stack"), ("stop_stack","stop_stack"), ("start_stack","start_stack"), + ("restart_service","restart_service"), ("list_stacks","list_stacks"), ("update_stack","update_stack"), ("secrets","secrets"), @@ -526,6 +527,18 @@ if __name__ == "__main__": por.start_stack(args.stack, args.endpoint_id) sys.exit() + if args.action == "restart_service": + args = prompt_missing_args( + args, + cur_config, + [ + ("site", "Site"), + ("endpoint_id", "Endpoint ID") + ], + ) + por.restart_service(args.endpoint_id, "lala") + sys.exit() + if args.action == "list_stacks": args = prompt_missing_args( args, @@ -544,6 +557,9 @@ if __name__ == "__main__": por.get_containers(args.endpoint_id, args.stack) sys.exit() + + + if args.action == "update_stack": print("Updating stacks") por.update_stack(args.endpoint_id, args.stack, args.autostart)