174 lines
5.4 KiB
YAML
174 lines
5.4 KiB
YAML
---
|
|
# Portainer Stack Management via API
|
|
# Manages GitOps stacks across all Portainer endpoints
|
|
# Run with: ansible-playbook -i hosts.ini playbooks/portainer_stack_management.yml
|
|
|
|
- name: Portainer Stack Management
|
|
hosts: localhost
|
|
gather_facts: no
|
|
vars:
|
|
portainer_url: "https://192.168.0.200:9443"
|
|
portainer_username: "admin"
|
|
# portainer_password: "{{ vault_portainer_password }}" # Use ansible-vault
|
|
git_repo_url: "https://git.vish.gg/Vish/homelab.git"
|
|
|
|
# Portainer endpoints mapping
|
|
endpoints:
|
|
atlantis:
|
|
id: 1
|
|
name: "Atlantis"
|
|
stacks_path: "Atlantis"
|
|
calypso:
|
|
id: 2
|
|
name: "Calypso"
|
|
stacks_path: "Calypso"
|
|
concord_nuc:
|
|
id: 3
|
|
name: "Concord NUC"
|
|
stacks_path: "concord_nuc"
|
|
homelab_vm:
|
|
id: 4
|
|
name: "Homelab VM"
|
|
stacks_path: "homelab_vm"
|
|
rpi5:
|
|
id: 5
|
|
name: "RPi 5"
|
|
stacks_path: "raspberry-pi-5-vish"
|
|
|
|
tasks:
|
|
- name: Authenticate with Portainer
|
|
uri:
|
|
url: "{{ portainer_url }}/api/auth"
|
|
method: POST
|
|
body_format: json
|
|
body:
|
|
Username: "{{ portainer_username }}"
|
|
Password: "{{ portainer_password | default('admin') }}"
|
|
validate_certs: no
|
|
register: auth_response
|
|
no_log: true
|
|
|
|
- name: Set authentication token
|
|
set_fact:
|
|
portainer_token: "{{ auth_response.json.jwt }}"
|
|
|
|
- name: Get all endpoints
|
|
uri:
|
|
url: "{{ portainer_url }}/api/endpoints"
|
|
method: GET
|
|
headers:
|
|
Authorization: "Bearer {{ portainer_token }}"
|
|
validate_certs: no
|
|
register: endpoints_response
|
|
|
|
- name: Display available endpoints
|
|
debug:
|
|
msg: |
|
|
Available Portainer Endpoints:
|
|
{% for endpoint in endpoints_response.json %}
|
|
- ID: {{ endpoint.Id }}, Name: {{ endpoint.Name }}, Status: {{ endpoint.Status }}
|
|
{% endfor %}
|
|
|
|
- name: Get stacks for each endpoint
|
|
uri:
|
|
url: "{{ portainer_url }}/api/stacks"
|
|
method: GET
|
|
headers:
|
|
Authorization: "Bearer {{ portainer_token }}"
|
|
validate_certs: no
|
|
register: stacks_response
|
|
|
|
- name: Analyze GitOps stacks
|
|
set_fact:
|
|
gitops_stacks: "{{ stacks_response.json | selectattr('GitConfig', 'defined') | list }}"
|
|
non_gitops_stacks: "{{ stacks_response.json | rejectattr('GitConfig', 'defined') | list }}"
|
|
|
|
- name: Display GitOps status
|
|
debug:
|
|
msg: |
|
|
GitOps Stack Analysis:
|
|
- Total Stacks: {{ stacks_response.json | length }}
|
|
- GitOps Managed: {{ gitops_stacks | length }}
|
|
- Non-GitOps: {{ non_gitops_stacks | length }}
|
|
|
|
GitOps Stacks:
|
|
{% for stack in gitops_stacks %}
|
|
- {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
|
|
{% endfor %}
|
|
|
|
Non-GitOps Stacks:
|
|
{% for stack in non_gitops_stacks %}
|
|
- {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
|
|
{% endfor %}
|
|
|
|
- name: Check stack health
|
|
uri:
|
|
url: "{{ portainer_url }}/api/stacks/{{ item.Id }}/file"
|
|
method: GET
|
|
headers:
|
|
Authorization: "Bearer {{ portainer_token }}"
|
|
validate_certs: no
|
|
register: stack_files
|
|
loop: "{{ gitops_stacks }}"
|
|
failed_when: false
|
|
|
|
- name: Trigger GitOps sync for all stacks
|
|
uri:
|
|
url: "{{ portainer_url }}/api/stacks/{{ item.Id }}/git/redeploy"
|
|
method: PUT
|
|
headers:
|
|
Authorization: "Bearer {{ portainer_token }}"
|
|
body_format: json
|
|
body:
|
|
RepositoryReferenceName: "refs/heads/main"
|
|
PullImage: true
|
|
validate_certs: no
|
|
register: sync_results
|
|
loop: "{{ gitops_stacks }}"
|
|
when: sync_stacks | default(false) | bool
|
|
failed_when: false
|
|
|
|
- name: Display sync results
|
|
debug:
|
|
msg: |
|
|
GitOps Sync Results:
|
|
{% for result in sync_results.results %}
|
|
{% if result.skipped is not defined %}
|
|
- Stack: {{ gitops_stacks[loop.index0].Name }} - Status: {{ result.status | default('Failed') }}
|
|
{% endif %}
|
|
{% endfor %}
|
|
when: sync_stacks | default(false) | bool
|
|
|
|
- name: Generate stack health report
|
|
copy:
|
|
content: |
|
|
# Portainer Stack Health Report
|
|
Generated: {{ ansible_date_time.iso8601 }}
|
|
|
|
## Summary
|
|
- Total Stacks: {{ stacks_response.json | length }}
|
|
- GitOps Managed: {{ gitops_stacks | length }}
|
|
- Non-GitOps: {{ non_gitops_stacks | length }}
|
|
|
|
## GitOps Stacks
|
|
{% for stack in gitops_stacks %}
|
|
### {{ stack.Name }}
|
|
- Endpoint: {{ stack.EndpointId }}
|
|
- Status: {{ stack.Status }}
|
|
- Git Repository: {{ stack.GitConfig.URL if stack.GitConfig is defined else 'N/A' }}
|
|
- Git Reference: {{ stack.GitConfig.ReferenceName if stack.GitConfig is defined else 'N/A' }}
|
|
- Last Update: {{ stack.UpdatedAt }}
|
|
|
|
{% endfor %}
|
|
|
|
## Non-GitOps Stacks (Manual Management Required)
|
|
{% for stack in non_gitops_stacks %}
|
|
- {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
|
|
{% endfor %}
|
|
dest: "/tmp/portainer_stack_report_{{ ansible_date_time.epoch }}.md"
|
|
delegate_to: localhost
|
|
|
|
- name: Display report location
|
|
debug:
|
|
msg: "Stack health report saved to: /tmp/portainer_stack_report_{{ ansible_date_time.epoch }}.md"
|