Add portainer.sh management script (list/redeploy/deploy)
This commit is contained in:
Executable
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
# Portainer management script
|
||||
# Usage:
|
||||
# ./portainer.sh list
|
||||
# ./portainer.sh redeploy <stack-name>
|
||||
# ./portainer.sh deploy <stack-name> <compose-path>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CREDENTIALS_FILE="$SCRIPT_DIR/.credentials"
|
||||
|
||||
if [[ ! -f "$CREDENTIALS_FILE" ]]; then
|
||||
echo "Error: credentials file not found at $CREDENTIALS_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=.credentials
|
||||
source "$CREDENTIALS_FILE"
|
||||
|
||||
API="$PORTAINER_URL/api"
|
||||
ENDPOINT_ID="$PORTAINER_ENDPOINT_ID"
|
||||
AUTH_HEADER="X-API-Key: $PORTAINER_API_TOKEN"
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
api_get() {
|
||||
curl -sk -H "$AUTH_HEADER" "$API/$1"
|
||||
}
|
||||
|
||||
api_put() {
|
||||
curl -sk -X PUT -H "$AUTH_HEADER" -H "Content-Type: application/json" \
|
||||
-d "$2" "$API/$1"
|
||||
}
|
||||
|
||||
api_post() {
|
||||
curl -sk -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
|
||||
-d "$2" "$API/$1"
|
||||
}
|
||||
|
||||
get_stack_by_name() {
|
||||
local name="$1"
|
||||
api_get "stacks?filters=%7B%22EndpointID%22%3A${ENDPOINT_ID}%7D" \
|
||||
| python3 -c "
|
||||
import json, sys
|
||||
stacks = json.load(sys.stdin)
|
||||
matches = [s for s in stacks if s['Name'] == '$name']
|
||||
if not matches:
|
||||
sys.exit(1)
|
||||
s = matches[0]
|
||||
print(s['Id'])
|
||||
"
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Commands
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
cmd_list() {
|
||||
echo "Fetching stacks..."
|
||||
api_get "stacks?filters=%7B%22EndpointID%22%3A${ENDPOINT_ID}%7D" \
|
||||
| python3 -c "
|
||||
import json, sys
|
||||
stacks = json.load(sys.stdin)
|
||||
stacks.sort(key=lambda s: s['Id'])
|
||||
status_map = {1: 'active', 2: 'inactive'}
|
||||
print(f'{'ID':<6} {'STATUS':<10} NAME')
|
||||
print('-' * 40)
|
||||
for s in stacks:
|
||||
status = status_map.get(s['Status'], str(s['Status']))
|
||||
print(f\"{s['Id']:<6} {status:<10} {s['Name']}\")
|
||||
"
|
||||
}
|
||||
|
||||
cmd_redeploy() {
|
||||
local name="${1:-}"
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: $0 redeploy <stack-name>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Looking up stack '$name'..."
|
||||
local stack_id
|
||||
if ! stack_id=$(get_stack_by_name "$name"); then
|
||||
echo "Error: stack '$name' not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Found stack ID: $stack_id"
|
||||
|
||||
echo "Redeploying..."
|
||||
local response
|
||||
response=$(api_put "stacks/${stack_id}/git/redeploy?endpointId=${ENDPOINT_ID}" \
|
||||
'{"pullImage": true, "prune": false}')
|
||||
|
||||
python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
if 'message' in d and 'Id' not in d:
|
||||
print('Error:', d['message'])
|
||||
sys.exit(1)
|
||||
hash = d.get('GitConfig', {}).get('ConfigHash', 'unknown')[:10]
|
||||
print(f'Done. ConfigHash: {hash}')
|
||||
" <<< "$response"
|
||||
}
|
||||
|
||||
cmd_deploy() {
|
||||
local name="${1:-}"
|
||||
local compose_path="${2:-}"
|
||||
if [[ -z "$name" || -z "$compose_path" ]]; then
|
||||
echo "Usage: $0 deploy <stack-name> <compose-path>" >&2
|
||||
echo " Example: $0 deploy myapp myapp/docker-compose.yml" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating stack '$name' from $compose_path..."
|
||||
local payload
|
||||
payload=$(python3 -c "
|
||||
import json
|
||||
print(json.dumps({
|
||||
'name': '$name',
|
||||
'repositoryURL': '$GITEA_REPO_URL',
|
||||
'repositoryReferenceName': 'refs/heads/main',
|
||||
'composeFile': '$compose_path',
|
||||
'repositoryAuthentication': True,
|
||||
'repositoryUsername': '$GITEA_USER',
|
||||
'repositoryPassword': '$GITEA_TOKEN',
|
||||
}))
|
||||
")
|
||||
|
||||
local response
|
||||
response=$(api_post "stacks/create/standalone/repository?endpointId=${ENDPOINT_ID}" "$payload")
|
||||
|
||||
python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
if 'message' in d and 'Id' not in d:
|
||||
print('Error:', d['message'])
|
||||
sys.exit(1)
|
||||
print(f\"Done. Stack ID: {d['Id']}, Name: {d['Name']}\")
|
||||
" <<< "$response"
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Dispatch
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
command="${1:-}"
|
||||
case "$command" in
|
||||
list) cmd_list ;;
|
||||
redeploy) cmd_redeploy "${2:-}" ;;
|
||||
deploy) cmd_deploy "${2:-}" "${3:-}" ;;
|
||||
*)
|
||||
echo "Usage: $0 <command> [args]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " list List all stacks"
|
||||
echo " redeploy <stack-name> Pull latest git commit and redeploy"
|
||||
echo " deploy <stack-name> <path> Create new git-linked stack"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user