feat: Auto deploy stacks on komodo (no storage yet)
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Komodo Stack Role
|
||||
|
||||
This role automates the creation of stacks in Komodo via its REST API.
|
||||
|
||||
## Quick Start
|
||||
|
||||
The included `provision_komodo_stacks.yml` playbook automatically discovers all stacks from your `docker-compose/` folder structure and creates them in Komodo:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/production.yml playbooks/provision_komodo_stacks.yml
|
||||
```
|
||||
|
||||
This will scan for all `compose.yaml` files in the structure: `docker-compose/{server}/{app}/compose.yaml` and create corresponding stacks in Komodo.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A running Komodo instance with API access
|
||||
- Valid Komodo API token stored in `vault_komodo_api_token`
|
||||
- Existing server and repository configured in Komodo
|
||||
|
||||
## Role Variables
|
||||
|
||||
### Required Variables
|
||||
|
||||
- `komodo_stack_name`: Name for the stack (typically the app name, e.g., "changedetection")
|
||||
- `komodo_server_name`: Name of the server in Komodo where the stack should deploy
|
||||
- `komodo_repo_name`: Name of the repository in Komodo containing the stack definition
|
||||
- `komodo_run_directory`: Path within the repository to the compose.yaml file (e.g., `docker-compose/apps-1/changedetection`)
|
||||
|
||||
### Optional Variables
|
||||
|
||||
- `komodo_host`: Komodo instance URL (default: `https://komodo.{{ domain }}`)
|
||||
- `komodo_api_token`: API token for authentication (default: `{{ vault_komodo_api_token }}`)
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Automatic Discovery (Recommended)
|
||||
|
||||
Use the provided playbook to automatically discover and create all stacks from your repository:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventories/production.yml playbooks/provision_komodo_stacks.yml
|
||||
```
|
||||
|
||||
This scans the `docker-compose/` directory and creates stacks for each app found in the structure:
|
||||
|
||||
- `docker-compose/{server}/{app}/compose.yaml`
|
||||
|
||||
Example structure:
|
||||
|
||||
```
|
||||
docker-compose/
|
||||
apps-1/
|
||||
changedetection/
|
||||
compose.yaml
|
||||
turn/
|
||||
compose.yaml
|
||||
media-1/
|
||||
arr/
|
||||
compose.yaml
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Validates all required variables are provided
|
||||
2. Fetches list of servers from Komodo API and finds the server ID by name
|
||||
3. Fetches list of repositories from Komodo API and finds the repo ID by name
|
||||
4. Checks if a stack with the same name already exists
|
||||
5. Creates the stack if it doesn't exist (skips if it does)
|
||||
|
||||
## API Token Setup
|
||||
|
||||
Store your Komodo API token in an Ansible vault file:
|
||||
|
||||
```yaml
|
||||
# inventories/group_vars/all/vault.yml
|
||||
vault_komodo_api_token: 'your-api-token-here'
|
||||
```
|
||||
|
||||
Generate an API token in Komodo: Settings → API Keys → Create New Token
|
||||
|
||||
## Notes
|
||||
|
||||
- The role is idempotent - it won't create duplicate stacks
|
||||
- Server and repo must already exist in Komodo before running this role
|
||||
- Uses retry logic for API calls to handle temporary network issues
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
# Komodo API configuration
|
||||
komodo_core_address: "http://{{ komodo_core_host }}"
|
||||
|
||||
komodo_api_key: "{{ vault_komodo_api_key }}"
|
||||
komodo_api_secret: "{{ vault_komodo_api_secret }}"
|
||||
|
||||
# Stack configuration
|
||||
komodo_repo_name: "homelab"
|
||||
|
||||
# Optional stack configuration
|
||||
komodo_stack_environment: []
|
||||
komodo_stack_labels: {}
|
||||
@@ -0,0 +1,128 @@
|
||||
---
|
||||
- name: Validate required variables
|
||||
assert:
|
||||
that:
|
||||
- komodo_stack_name is defined and komodo_stack_name | length > 0
|
||||
- komodo_server_name is defined and komodo_server_name | length > 0
|
||||
- komodo_repo_name is defined and komodo_repo_name | length > 0
|
||||
- komodo_run_directory is defined and komodo_run_directory | length > 0
|
||||
- komodo_api_key is defined and komodo_api_key | length > 0
|
||||
- komodo_api_secret is defined and komodo_api_secret | length > 0
|
||||
fail_msg: "Missing required variables: komodo_stack_name, komodo_server_name, komodo_repo_name, komodo_run_directory, komodo_api_key, or komodo_api_secret"
|
||||
|
||||
- name: Get Komodo server by name
|
||||
uri:
|
||||
url: "{{ komodo_core_address }}/read/GetServer"
|
||||
method: POST
|
||||
headers:
|
||||
"X-Api-Key": "{{ komodo_api_key }}"
|
||||
"X-Api-Secret": "{{ komodo_api_secret }}"
|
||||
"Content-Type": "application/json"
|
||||
body_format: json
|
||||
body:
|
||||
server: "{{ komodo_server_name }}"
|
||||
status_code: 200
|
||||
validate_certs: false
|
||||
register: komodo_server
|
||||
retries: 3
|
||||
delay: 2
|
||||
|
||||
- name: Extract server ID
|
||||
set_fact:
|
||||
komodo_server_id: "{{ komodo_server.json._id['$oid'] }}"
|
||||
|
||||
- name: Debug server response
|
||||
debug:
|
||||
msg: "Server ID: {{ komodo_server_id }}"
|
||||
|
||||
- name: Get Komodo repo by name
|
||||
uri:
|
||||
url: "{{ komodo_core_address }}/read/GetRepo"
|
||||
method: POST
|
||||
headers:
|
||||
"X-Api-Key": "{{ komodo_api_key }}"
|
||||
"X-Api-Secret": "{{ komodo_api_secret }}"
|
||||
"Content-Type": "application/json"
|
||||
body_format: json
|
||||
body:
|
||||
repo: "{{ komodo_repo_name }}"
|
||||
status_code: 200
|
||||
validate_certs: false
|
||||
register: komodo_repo
|
||||
retries: 3
|
||||
delay: 2
|
||||
|
||||
- name: Extract repo ID
|
||||
set_fact:
|
||||
komodo_repo_id: "{{ komodo_repo.json._id['$oid'] }}"
|
||||
|
||||
- name: Debug repo response
|
||||
debug:
|
||||
msg: "Repo ID: {{ komodo_repo_id }}"
|
||||
|
||||
- name: Check if stack already exists
|
||||
uri:
|
||||
url: "{{ komodo_core_address }}/read/GetStack"
|
||||
method: POST
|
||||
headers:
|
||||
"X-Api-Key": "{{ komodo_api_key }}"
|
||||
"X-Api-Secret": "{{ komodo_api_secret }}"
|
||||
"Content-Type": "application/json"
|
||||
body_format: json
|
||||
body:
|
||||
stack: "{{ komodo_stack_name }}"
|
||||
status_code: [200, 500]
|
||||
validate_certs: false
|
||||
register: existing_stack
|
||||
retries: 3
|
||||
delay: 2
|
||||
failed_when: false
|
||||
|
||||
- name: Create Komodo stack
|
||||
uri:
|
||||
url: "{{ komodo_core_address }}/write/CreateStack"
|
||||
method: POST
|
||||
headers:
|
||||
"X-Api-Key": "{{ komodo_api_key }}"
|
||||
"X-Api-Secret": "{{ komodo_api_secret }}"
|
||||
"Content-Type": "application/json"
|
||||
body_format: json
|
||||
body:
|
||||
name: "{{ komodo_stack_name }}"
|
||||
config:
|
||||
server_id: "{{ komodo_server_id }}"
|
||||
linked_repo: "{{ komodo_repo_id }}"
|
||||
run_directory: "{{ komodo_run_directory }}"
|
||||
status_code: [200, 201]
|
||||
validate_certs: false
|
||||
register: stack_result
|
||||
when: existing_stack.status == 500
|
||||
retries: 3
|
||||
delay: 2
|
||||
|
||||
- name: Display stack creation result
|
||||
debug:
|
||||
msg: "Stack '{{ komodo_stack_name }}' created successfully with ID: {{ stack_result.json._id }}"
|
||||
when: existing_stack.status == 500 and stack_result is succeeded
|
||||
|
||||
- name: Display stack already exists message
|
||||
debug:
|
||||
msg: "Stack '{{ komodo_stack_name }}' already exists with ID: {{ existing_stack.json._id }}"
|
||||
when: existing_stack.status == 200
|
||||
|
||||
- name: Deploy stack
|
||||
uri:
|
||||
url: "{{ komodo_core_address }}/execute/DeployStack"
|
||||
method: POST
|
||||
headers:
|
||||
"X-Api-Key": "{{ komodo_api_key }}"
|
||||
"X-Api-Secret": "{{ komodo_api_secret }}"
|
||||
"Content-Type": "application/json"
|
||||
body_format: json
|
||||
body:
|
||||
stack: "{{ komodo_stack_name }}"
|
||||
status_code: 200
|
||||
validate_certs: false
|
||||
register: start_result
|
||||
retries: 3
|
||||
delay: 2
|
||||
Reference in New Issue
Block a user