DeployTests/deploy/portainer/deploy.py

143 lines
4.7 KiB
Python
Raw Normal View History

2023-10-03 01:34:40 +00:00
#!/usr/bin/env python3
import os
import sys
import argparse
import requests
import json
2023-10-03 19:29:22 +00:00
import uuid
2023-10-06 20:40:34 +00:00
from dotenv import load_dotenv, dotenv_values
2023-10-03 01:34:40 +00:00
from string import Template
2023-10-03 01:50:16 +00:00
from pathlib import Path
2023-10-03 19:29:22 +00:00
from urllib.parse import urlparse
2023-10-03 01:34:40 +00:00
2023-10-06 20:40:34 +00:00
load_dotenv('.env.deploy')
2023-10-03 01:34:40 +00:00
required_env_vars = {
'PORTAINER': 'The portainer instance to deploy to',
2023-10-03 19:29:22 +00:00
'PORTAINER_API_KEY': 'API-Key to access portainer instance',
2023-10-03 01:34:40 +00:00
'PORTAINER_EP': 'Portainer Environment EndPoint to deploy to',
2023-10-03 19:29:22 +00:00
'GITEA_API_KEY': 'API-Key to access Gitea instance',
2023-10-03 01:34:40 +00:00
'DEPLOY_REPO_URL': 'The repository URL to deploy',
2023-10-03 20:09:43 +00:00
'DEPLOY_BRANCH': 'The branch to deploy',
2023-10-03 01:34:40 +00:00
}
# 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)
2023-10-03 19:58:51 +00:00
deploy_webhook = str(uuid.uuid4())
2023-10-03 01:34:40 +00:00
### Find CICD-runner portainer environment endpointId ###
2023-10-03 19:29:22 +00:00
portainer_headers = {
2023-10-03 01:34:40 +00:00
'Content-Type': 'application/json',
2023-10-03 19:58:51 +00:00
'X-API-Key': args.PORTAINER_API_KEY
2023-10-03 01:34:40 +00:00
}
2023-10-03 19:58:51 +00:00
endpoint_url = f'{args.PORTAINER}/api/endpoints'
2023-10-03 01:34:40 +00:00
json_endpoints = None
try:
2023-10-03 19:29:22 +00:00
response = requests.get(endpoint_url, headers=portainer_headers)
2023-10-03 01:34:40 +00:00
response.raise_for_status() # Raise HTTPError for bad requests
json_endpoints = 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: {response.content}')
2023-10-03 01:34:40 +00:00
endpoint_id = None
for endpoint in json_endpoints:
2023-10-03 19:58:51 +00:00
if endpoint["Name"] == args.PORTAINER_EP:
2023-10-03 01:34:40 +00:00
endpoint_id = endpoint["Id"]
break
if endpoint_id is None:
2023-10-03 19:58:51 +00:00
raise Exception(f'Portainer endpoint \'{args.PORTAINER_EP}\' not found.')
2023-10-03 01:34:40 +00:00
else:
2023-10-03 19:58:51 +00:00
print(f'Found portainer endpoint \'{args.PORTAINER_EP}\' has id: \'{endpoint_id}\'.')
2023-10-03 01:34:40 +00:00
2023-10-03 20:09:43 +00:00
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]
2023-10-03 01:34:40 +00:00
### Template substitution for the portainer stack deployment ###
2023-10-06 20:40:34 +00:00
app_env = dotenv_values('.env.app')
portainer_deploy_env = [{
"name": "COMPOSE_PROJECT_NAME",
"value": args.DEPLOY_BRANCH
}]
for key, value in app_env.items():
portainer_deploy_env.append({
"name": key,
"value": os.getenv(value) if value.startswith("$") else value
})
2023-10-03 19:29:22 +00:00
portainer_deploy_payload = {
"additionalFiles": [
"deploy/portainer/portainer_deploy.docker-compose.yml"
],
"autoUpdate": {
2023-10-03 19:58:51 +00:00
"webhook": deploy_webhook
2023-10-03 19:29:22 +00:00
},
"composeFile": "docker-compose.yml",
2023-10-06 20:40:34 +00:00
"env": portainer_deploy_env,
2023-10-03 19:29:22 +00:00
"fromAppTemplate": False,
2023-10-03 20:09:43 +00:00
"name": f"{owner}_{repo}_{args.DEPLOY_BRANCH.replace('/', '_')}".lower(),
2023-10-03 19:29:22 +00:00
"repositoryAuthentication": True,
"repositoryUsername": "cicd",
"repositoryPassword": "gJ6@$7ZjWGyV4%i",
2023-10-03 20:09:43 +00:00
"repositoryReferenceName": f"refs/heads/{args.DEPLOY_BRANCH}",
2023-10-03 19:58:51 +00:00
"repositoryURL": args.DEPLOY_REPO_URL,
2023-10-03 19:29:22 +00:00
"tlsskipVerify": False
}
2023-10-03 01:34:40 +00:00
### Deploy to portainer ###
2023-10-03 19:58:51 +00:00
deploy_url = f'{args.PORTAINER}/api/stacks/create/standalone/repository?endpointId={endpoint_id}'
2023-10-03 01:34:40 +00:00
try:
2023-10-03 19:29:22 +00:00
response = requests.post(deploy_url, headers=portainer_headers, json=portainer_deploy_payload)
2023-10-03 01:34:40 +00:00
response.raise_for_status() # Raise HTTPError for bad requests
2023-10-03 19:29:22 +00:00
deploy_response = response.json()
2023-10-03 01:34:40 +00:00
except requests.exceptions.RequestException as err:
2023-10-06 20:40:34 +00:00
raise Exception(f'Could not deploy portainer stack: {err}.\n\n Response: {response.content}')
2023-10-03 01:34:40 +00:00
2023-10-03 19:29:22 +00:00
### Add Webhook to Gitea ###
webhook_payload = {
"type": "gitea",
2023-10-03 19:58:51 +00:00
"branch_filter": f"{args.DEPLOY_BRANCH}",
2023-10-03 19:29:22 +00:00
"config": {
2023-10-03 19:58:51 +00:00
"url": f"{args.PORTAINER}/api/stacks/webhooks/{deploy_webhook}",
2023-10-03 19:29:22 +00:00
"content_type": "json"
},
"events": ["push"], # You can specify other events as needed
"active": True
}
2023-10-03 19:58:51 +00:00
webhook_url = f'{gitea}/api/v1/repos/{repo_path}/hooks'
2023-10-03 19:29:22 +00:00
webhook_headers = {
2023-10-03 19:58:51 +00:00
"Authorization": f"token {args.GITEA_API_KEY}"
2023-10-03 19:29:22 +00:00
}
try:
response = requests.post(webhook_url, headers=webhook_headers, json=webhook_payload)
response.raise_for_status() # Raise HTTPError for bad requests
webhook_response = response.json()
except requests.exceptions.RequestException as err:
2023-10-06 20:40:34 +00:00
raise Exception(f'Could not add webhook to Gitea: {err}.\n\n Response: {response.content}')
2023-10-03 01:34:40 +00:00
print(f'Successfully deployed project')