2023-10-04 02:17:38 +00:00
#!/usr/bin/env python3
import os
import sys
import argparse
import requests
import json
import uuid
from dotenv import load_dotenv
from string import Template
from pathlib import Path
from urllib . parse import urlparse
2023-10-06 20:40:34 +00:00
load_dotenv ( ' .env.deploy ' )
2023-10-04 02:17:38 +00:00
required_env_vars = {
' PORTAINER ' : ' The portainer instance to deploy to ' ,
' PORTAINER_API_KEY ' : ' API-Key to access portainer instance ' ,
' PORTAINER_EP ' : ' Portainer Environment EndPoint to deploy to ' ,
' GITEA_API_KEY ' : ' API-Key to access Gitea instance ' ,
' DEPLOY_REPO_URL ' : ' The repository URL to deploy ' ,
' DEPLOY_BRANCH ' : ' The branch to deploy '
}
# Try getting all arguments from (in order): 1 command line, 2 .env file, 3 Environment
parser = argparse . ArgumentParser ( description = ' Deploys a docker compose application to portainer. ' )
for var , usage in required_env_vars . items ( ) :
parser . add_argument ( f ' -- { var } ' , default = os . getenv ( var , None ) , help = usage )
args = parser . parse_args ( )
# Check if all were parsed
not_parsed = [ ]
for var , usage in required_env_vars . items ( ) :
if not getattr ( args , var ) :
not_parsed . append ( var )
else :
print ( f ' -- { var } : { getattr ( args , var ) } ' )
if not_parsed :
print ( f " Error: The following required environment variables were not provided: { ' , ' . join ( not_parsed ) } " )
parser . print_help ( )
sys . exit ( 1 )
portainer_headers = {
' Content-Type ' : ' application/json ' ,
' X-API-Key ' : args . PORTAINER_API_KEY
}
endpoint_url = f ' { args . PORTAINER } /api/endpoints '
try :
endpoint_response = requests . get ( endpoint_url , headers = portainer_headers )
endpoint_response . raise_for_status ( ) # Raise HTTPError for bad requests
json_endpoints = endpoint_response . json ( )
except requests . exceptions . RequestException as err :
2023-10-06 20:40:34 +00:00
raise Exception ( f ' Could not retrieve portainer endpoints: { err } . \n \n Response: { endpoint_response . content } ' )
2023-10-04 02:17:38 +00:00
endpoint_id = None
for endpoint in json_endpoints :
if endpoint [ " Name " ] == args . PORTAINER_EP :
endpoint_id = endpoint [ " Id " ]
break
if endpoint_id is None :
raise Exception ( f ' Portainer endpoint \' { args . PORTAINER_EP } \' not found. ' )
else :
print ( f ' Found portainer endpoint \' { args . PORTAINER_EP } \' has id: \' { endpoint_id } \' . ' )
# ?filters=\{'EndpointId':'{endpoint_id}'\}
stacks_url = f " { args . PORTAINER } /api/stacks "
try :
stacks_response = requests . get ( stacks_url , headers = portainer_headers )
stacks_response . raise_for_status ( ) # Raise HTTPError for bad requests
json_stacks = stacks_response . json ( )
except requests . exceptions . RequestException as err :
2023-10-06 20:40:34 +00:00
raise Exception ( f ' Could not retrieve portainer stacks: { err } . \n \n Response: { stacks_response . content } ' )
2023-10-04 02:17:38 +00:00
stack_id = None
stack_webhook = None
for stack in json_stacks :
if (
stack [ ' GitConfig ' ] is not None
and stack [ ' GitConfig ' ] [ ' URL ' ] == args . DEPLOY_REPO_URL
and stack [ ' GitConfig ' ] [ ' ReferenceName ' ] == f " refs/heads/ { args . DEPLOY_BRANCH } "
) :
stack_id = stack [ " Id " ]
stack_webhook = stack [ " AutoUpdate " ] [ " Webhook " ]
break ;
if stack_id is None or stack_webhook is None :
raise Exception ( f " Portainer stack with url: ' { args . DEPLOY_REPO_URL } ' and branch: ' { args . DEPLOY_BRANCH } ' not found. " )
else :
print ( f " Found portainer stack to remove with url: ' { args . DEPLOY_REPO_URL } ' and branch: ' { args . DEPLOY_BRANCH } ' , stack has id: ' { stack_id } ' and webhook: ' { stack_webhook } ' " )
### Find correct webhook in gitea
repo_url = urlparse ( args . DEPLOY_REPO_URL )
gitea = f " { repo_url . scheme } :// { repo_url . netloc } "
repo_path = repo_url . path
repo_parts = repo_path . strip ( ' / ' ) . split ( ' / ' )
owner = repo_parts [ 0 ]
repo = repo_parts [ 1 ]
gitea_headers = {
" Authorization " : f " token { args . GITEA_API_KEY } "
}
webhook_url = f ' { gitea } /api/v1/repos/ { repo_path } /hooks '
try :
#TODO: Webhooks are returned paginated, this only checks first page
get_webhooks_response = requests . get ( webhook_url , headers = gitea_headers )
get_webhooks_response . raise_for_status ( ) # Raise HTTPError for bad requests
json_webhooks = get_webhooks_response . json ( )
except requests . exceptions . RequestException as err :
2023-10-06 20:40:34 +00:00
raise Exception ( f ' Could not get webhooks from Gitea: { err } . \n \n Response: { get_webhooks_response . content } ' )
2023-10-04 02:17:38 +00:00
webhook_id = None
for webhook in json_webhooks :
if webhook [ " config " ] [ " url " ] == f " { args . PORTAINER } /api/stacks/webhooks/ { stack_webhook } " :
webhook_id = webhook [ " id " ]
break
if webhook_id is None :
raise Exception ( f " Gitea webhook pointing to Portainer webhook ' { stack_webhook } ' not found. " )
else :
print ( f " Found Gitea webhook pointing to Portainer webhook ' { stack_webhook } \' has id: ' { webhook_id } ' . " )
### Remove Webhook from Gitea ###
remove_webhook_url = f " { gitea } /api/v1/repos/ { repo_path } /hooks/ { webhook_id } "
try :
del_webhooks_response = requests . delete ( remove_webhook_url , headers = gitea_headers )
del_webhooks_response . raise_for_status ( ) # Raise HTTPError for bad requests
except requests . exceptions . RequestException as err :
2023-10-06 20:40:34 +00:00
raise Exception ( f " Could not delete webhook ' { webhook_id } ' from Gitea: { err } . \n \n Response: { del_webhooks_response . content } " )
2023-10-04 02:17:38 +00:00
## Remove Stack from Portainer ###
remove_stack_url = f " { args . PORTAINER } /api/stacks/ { stack_id } ?endpointId= { endpoint_id } "
try :
del_stack_response = requests . delete ( remove_stack_url , headers = portainer_headers )
del_stack_response . raise_for_status ( ) # Raise HTTPError for bad requests
except requests . exceptions . RequestException as err :
2023-10-06 20:40:34 +00:00
raise Exception ( f " Could not delete stack ' { stack_id } ' from Portainer: { err } . \n \n Response: { del_stack_response . content } " )
2023-10-04 02:17:38 +00:00
print ( f ' Successfully undeployed project ' )