Files
portainer_updater/main.py

122 lines
3.7 KiB
Python

import os
import json
import requests
import config
DOCKER_COMPOSE_PATH = config.portainer_compose_dir + '/{}/docker-compose.yml'
BASH_COMPANION_SCRIPT = os.getcwd() + '/docker_updater.sh'
REPO_UPDATER_SCRIPT = os.getcwd() + '/update_repos.sh'
class PortainerClient:
def __init__(self, portainer_url, access_token):
self.portainer_url = portainer_url
self.access_token = access_token
self.api_url = f"{self.portainer_url}/api"
def _get_headers(self):
return {
"accept": "application/json",
"X-API-Key": self.access_token
}
def list_stacks(self):
response = requests.get(f"{self.api_url}/stacks", headers=self._get_headers())
return response.json()
def get_stack(self, stack_id):
response = requests.get(f"{self.api_url}/stacks/{stack_id}", headers=self._get_headers())
return response.json()
def get_stack_file(self, stack_id):
response = requests.get(f"{self.api_url}/stacks/{stack_id}/file", headers=self._get_headers())
return response.json()
def upgrade_stack(self, stack):
file_response = self.get_stack_file(stack['Id'])
payload = {
"pullImage": True,
"prune": True,
"StackFileContent": file_response["StackFileContent"],
"Env": stack["Env"]
}
response = requests.put(
f"{self.api_url}/stacks/{stack['Id']}?endpointId={stack['EndpointId']}",
headers=self._get_headers(),
json=payload
)
return response.json()
def list_stack_containers(self, endpoint, stack_name):
filters = '{"label":["com.docker.compose.project=' + stack_name + '"]}'
response = requests.get(
f"{self.api_url}/endpoints/{endpoint}/docker/containers/json?all=1&filters={filters}",
headers=self._get_headers()
)
return response.json()
def update_image(self, endpoint, image):
response = requests.post(
f"{self.api_url}/endpoints/{endpoint}/docker/images/create?fromImage={image}",
headers=self._get_headers()
)
try:
status = json.loads(response.text.strip().split('\r\n')[-1])['status']
except Exception:
return False
return f"Downloaded newer image for {image}" in status
def update_repositories():
for repo_path in config.git_repositories:
ret = os.system(f"{REPO_UPDATER_SCRIPT} {repo_path}")
if ret > 0:
exit(ret)
def update_local_stack(stack):
project_name = stack['Name']
print(f"Updating stack: {project_name}")
compose_file = DOCKER_COMPOSE_PATH.format(stack['Id'])
ret = os.system(f"{BASH_COMPANION_SCRIPT} {project_name} {compose_file}")
if ret > 0:
exit(ret)
print(f"Stack {project_name} updated!\n")
if __name__ == '__main__':
update_repositories()
client = PortainerClient(portainer_url=config.portainer_url, access_token=config.access_token)
for stack in client.list_stacks():
endpoint = stack['EndpointId']
stack_name = stack['Name']
if stack['Status'] == 1:
containers = client.list_stack_containers(endpoint, stack_name)
an_image_was_updated = False
for c in containers:
this_image_was_updated = client.update_image(endpoint, c['Image'])
an_image_was_updated = an_image_was_updated or this_image_was_updated
if an_image_was_updated:
if endpoint == 2 and stack_name in ('portainer', 'traefik'):
update_local_stack(stack)
else:
client.upgrade_stack(stack)
os.system('docker image prune -fa')