Add "Ansible-Executor"
parent
1afc7b2cd6
commit
11b2a468b4
1 changed files with 226 additions and 0 deletions
226
Ansible-Executor.-.md
Normal file
226
Ansible-Executor.-.md
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# Ansible Executor
|
||||
|
||||
> Playbook execution, inventory management, and SSH connection pooling.
|
||||
|
||||
## Overview
|
||||
|
||||
The `ansible` package provides a pure Go implementation for executing Ansible-style playbooks. It parses YAML inventories, manages SSH connections with pooling, and returns structured results for each task.
|
||||
|
||||
## Executor
|
||||
|
||||
The `Executor` is the primary entry point for running playbooks.
|
||||
|
||||
```go
|
||||
type Executor struct {
|
||||
// Internal: basePath, inventory, vars, facts, results
|
||||
}
|
||||
```
|
||||
|
||||
### Creating an Executor
|
||||
|
||||
```go
|
||||
import "forge.lthn.ai/core/go-devops/ansible"
|
||||
|
||||
// Create an executor rooted at the playbooks directory
|
||||
executor := ansible.NewExecutor("/path/to/playbooks")
|
||||
```
|
||||
|
||||
### Loading Inventory
|
||||
|
||||
```go
|
||||
// Load inventory from a YAML file
|
||||
executor.SetInventory("/path/to/inventory.yml")
|
||||
```
|
||||
|
||||
### Running a Playbook
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
result, err := executor.Run(ctx, "deploy.yml")
|
||||
if err != nil {
|
||||
log.Fatalf("playbook failed: %v", err)
|
||||
}
|
||||
|
||||
// Check results
|
||||
for _, taskResult := range result.Tasks {
|
||||
fmt.Printf("Task: %s — Changed: %t, RC: %d\n",
|
||||
taskResult.Name, taskResult.Changed, taskResult.RC)
|
||||
}
|
||||
```
|
||||
|
||||
## Inventory
|
||||
|
||||
The `Inventory` type models Ansible's host group structure with per-host and per-group variables.
|
||||
|
||||
```go
|
||||
type Inventory struct {
|
||||
Groups map[string]HostGroup // Named groups of hosts
|
||||
Vars map[string]any // Global inventory variables
|
||||
}
|
||||
|
||||
type HostGroup struct {
|
||||
Hosts []Host // Hosts in this group
|
||||
Vars map[string]any // Group-level variables
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
Name string // Hostname or IP
|
||||
Address string // Connection address
|
||||
Port int // SSH port (default: 22)
|
||||
User string // SSH user
|
||||
Vars map[string]any // Host-level variables
|
||||
}
|
||||
```
|
||||
|
||||
### Example Inventory
|
||||
|
||||
```yaml
|
||||
all:
|
||||
vars:
|
||||
ansible_user: deploy
|
||||
children:
|
||||
webservers:
|
||||
hosts:
|
||||
web1:
|
||||
ansible_host: 10.0.1.10
|
||||
ansible_port: 4819
|
||||
web2:
|
||||
ansible_host: 10.0.1.11
|
||||
databases:
|
||||
hosts:
|
||||
db1:
|
||||
ansible_host: 10.0.2.10
|
||||
vars:
|
||||
db_engine: postgresql
|
||||
```
|
||||
|
||||
## Plays and Tasks
|
||||
|
||||
### Play
|
||||
|
||||
A play targets a group of hosts with a list of tasks.
|
||||
|
||||
```go
|
||||
type Play struct {
|
||||
Name string // Play name
|
||||
Hosts string // Target host group
|
||||
Tasks []Task // Ordered task list
|
||||
Vars map[string]any
|
||||
}
|
||||
```
|
||||
|
||||
### Task
|
||||
|
||||
Each task executes a single module with arguments.
|
||||
|
||||
```go
|
||||
type Task struct {
|
||||
Name string // Human-readable task name
|
||||
Module string // Module to execute (e.g. "shell", "copy", "service")
|
||||
Args map[string]any // Module arguments
|
||||
Register string // Variable name to store result
|
||||
Notify []string // Handlers to trigger on change
|
||||
Tags []string // Tags for selective execution
|
||||
}
|
||||
```
|
||||
|
||||
### TaskResult
|
||||
|
||||
Every task execution produces a structured result.
|
||||
|
||||
```go
|
||||
type TaskResult struct {
|
||||
Name string // Task name
|
||||
Changed bool // Whether the task made changes
|
||||
Output string // stdout/stderr combined
|
||||
RC int // Return code (0 = success)
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Checking Results
|
||||
|
||||
```go
|
||||
result, err := executor.Run(ctx, "provision.yml")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tr := range result.Tasks {
|
||||
if tr.RC != 0 {
|
||||
fmt.Printf("FAILED: %s (rc=%d)\nOutput: %s\n", tr.Name, tr.RC, tr.Output)
|
||||
} else if tr.Changed {
|
||||
fmt.Printf("CHANGED: %s\n", tr.Name)
|
||||
} else {
|
||||
fmt.Printf("OK: %s\n", tr.Name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## SSH Connection Pooling
|
||||
|
||||
The `SSHClient` manages persistent SSH connections to avoid repeated handshakes during playbook execution.
|
||||
|
||||
```go
|
||||
type SSHClient struct {
|
||||
// Internal: connection pool, config, timeout
|
||||
}
|
||||
```
|
||||
|
||||
### How Pooling Works
|
||||
|
||||
1. On first connection to a host, the client establishes an SSH session
|
||||
2. Subsequent tasks reuse the existing connection
|
||||
3. Connections are automatically closed when the executor finishes
|
||||
4. Failed connections are evicted from the pool and re-established
|
||||
|
||||
This significantly reduces execution time for playbooks with many tasks per host, as the SSH handshake (key exchange, authentication) only happens once.
|
||||
|
||||
### Connection Configuration
|
||||
|
||||
The SSH client reads connection parameters from the inventory:
|
||||
|
||||
| Inventory Variable | Purpose |
|
||||
|-------------------|---------|
|
||||
| `ansible_host` | Connection address |
|
||||
| `ansible_port` | SSH port (default: 22) |
|
||||
| `ansible_user` | SSH username |
|
||||
| `ansible_ssh_private_key_file` | Path to private key |
|
||||
| `ansible_ssh_common_args` | Additional SSH arguments |
|
||||
|
||||
## Example: Full Workflow
|
||||
|
||||
```go
|
||||
import (
|
||||
"forge.lthn.ai/core/go-devops/ansible"
|
||||
"forge.lthn.ai/core/go-devops/build"
|
||||
)
|
||||
|
||||
// Build the project first
|
||||
builder := build.New(build.Config{
|
||||
ProjectDir: "./my-service",
|
||||
OutputDir: "./dist",
|
||||
})
|
||||
artifact, err := builder.Build(ctx)
|
||||
|
||||
// Then deploy with Ansible
|
||||
executor := ansible.NewExecutor("./playbooks")
|
||||
executor.SetInventory("./inventory/production.yml")
|
||||
|
||||
// Set extra vars including the build artefact path
|
||||
executor.SetVar("artifact_path", artifact.Path)
|
||||
executor.SetVar("service_version", "1.2.0")
|
||||
|
||||
result, err := executor.Run(ctx, "deploy.yml")
|
||||
if err != nil {
|
||||
log.Fatalf("deployment failed: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Deployment complete: %d tasks, %d changed\n",
|
||||
len(result.Tasks), result.ChangedCount())
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [[Home]] — Package overview
|
||||
- [[Build-System]] — Building artefacts before deployment
|
||||
- [[Infrastructure]] — Provisioning servers to deploy to
|
||||
Loading…
Add table
Reference in a new issue