chore: add ansible deploy simple logic, upgrade admin auth logic and docs
ci-agent / build (push) Failing after 1m55s
ci-agent / build (push) Failing after 1m55s
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package ansible
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Executor handles running Ansible playbooks
|
||||
type Executor struct {
|
||||
workDir string
|
||||
grpcServerHost string
|
||||
grpcServerPort string
|
||||
backendURL string
|
||||
}
|
||||
|
||||
// ExecutorConfig holds configuration for the Executor
|
||||
type ExecutorConfig struct {
|
||||
WorkDir string
|
||||
GRPCServerHost string
|
||||
GRPCServerPort string
|
||||
BackendURL string
|
||||
}
|
||||
|
||||
// NewExecutor creates a new Ansible executor
|
||||
func NewExecutor(cfg ExecutorConfig) *Executor {
|
||||
return &Executor{
|
||||
workDir: cfg.WorkDir,
|
||||
grpcServerHost: cfg.GRPCServerHost,
|
||||
grpcServerPort: cfg.GRPCServerPort,
|
||||
backendURL: cfg.BackendURL,
|
||||
}
|
||||
}
|
||||
|
||||
// DeployResult holds the result of a deployment
|
||||
type DeployResult struct {
|
||||
Host string
|
||||
Success bool
|
||||
Stdout string
|
||||
Stderr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// WorkDir returns the work directory path
|
||||
func (e *Executor) WorkDir() string {
|
||||
return e.workDir
|
||||
}
|
||||
|
||||
// Deploy runs Ansible playbook for the given inventory
|
||||
func (e *Executor) Deploy(ctx context.Context, inventoryPath string, deployType string) ([]DeployResult, error) {
|
||||
playbookName := "binary_deploy.yml"
|
||||
if deployType == "docker" {
|
||||
playbookName = "docker_deploy.yml"
|
||||
}
|
||||
|
||||
playbookPath := filepath.Join(e.workDir, playbookName)
|
||||
|
||||
cmd := exec.CommandContext(ctx, "ansible-playbook",
|
||||
"-i", inventoryPath,
|
||||
"-e", fmt.Sprintf("backend_url=%s", e.backendURL),
|
||||
playbookPath,
|
||||
)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
runErr := cmd.Run()
|
||||
|
||||
// Parse results per host (simplified - returns single result for all)
|
||||
return []DeployResult{
|
||||
{
|
||||
Host: "all",
|
||||
Success: runErr == nil,
|
||||
Stdout: stdout.String(),
|
||||
Stderr: stderr.String(),
|
||||
Err: runErr,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeployParallel runs Ansible playbook for multiple inventories in parallel
|
||||
func (e *Executor) DeployParallel(ctx context.Context, inventoryPaths []string, deployType string) (map[string][]DeployResult, error) {
|
||||
var wg sync.WaitGroup
|
||||
results := make(map[string][]DeployResult)
|
||||
errCh := make(chan error, len(inventoryPaths))
|
||||
|
||||
for _, path := range inventoryPaths {
|
||||
wg.Add(1)
|
||||
go func(p string) {
|
||||
defer wg.Done()
|
||||
res, err := e.Deploy(ctx, p, deployType)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
results[p] = res
|
||||
}(path)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errCh)
|
||||
|
||||
// Collect errors
|
||||
var errs []error
|
||||
for err := range errCh {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return results, fmt.Errorf("some deployments failed: %v", errs)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// WritePlaybook writes a playbook to the work directory
|
||||
func (e *Executor) WritePlaybook(name string, content string) error {
|
||||
path := filepath.Join(e.workDir, name)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, []byte(content), 0644)
|
||||
}
|
||||
|
||||
// WriteAllPlaybooks writes all playbooks to the work directory
|
||||
func (e *Executor) WriteAllPlaybooks() error {
|
||||
if err := e.WritePlaybook("binary_deploy.yml", BinaryDeployPlaybook); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.WritePlaybook("docker_deploy.yml", DockerDeployPlaybook)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ansible
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// InventoryHost represents a single host in the inventory
|
||||
type InventoryHost struct {
|
||||
Name string
|
||||
IP string
|
||||
Port int
|
||||
User string
|
||||
AuthMethod string
|
||||
SSHKey string
|
||||
Password string
|
||||
DeployType string
|
||||
Token string
|
||||
}
|
||||
|
||||
// Inventory represents an Ansible inventory file
|
||||
type Inventory struct {
|
||||
Hosts []InventoryHost
|
||||
}
|
||||
|
||||
const inventoryTemplateText = `{{ range .Hosts }}
|
||||
{{ .Name }} ansible_host={{ .IP }} ansible_port={{ .Port }} ansible_user={{ .User }} ansible_connection=ssh
|
||||
{{ if eq .AuthMethod "key" }}ansible_ssh_private_key_file={{ .SSHKey }}{{ end }}
|
||||
{{ if eq .AuthMethod "password" }}ansible_ssh_pass={{ .Password }}{{ end }}
|
||||
deploy_type={{ .DeployType }}
|
||||
agent_token={{ .Token }}
|
||||
agent_label={{ .Name }}
|
||||
|
||||
{{ end }}`
|
||||
|
||||
// GenerateInventory generates an Ansible inventory file from the given hosts
|
||||
func GenerateInventory(hosts []InventoryHost, outputPath string) error {
|
||||
tmpl, err := template.New("inventory").Parse(inventoryTemplateText)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse inventory template: %w", err)
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(outputPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create inventory directory: %w", err)
|
||||
}
|
||||
|
||||
file, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create inventory file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := tmpl.Execute(file, Inventory{Hosts: hosts}); err != nil {
|
||||
return fmt.Errorf("failed to execute inventory template: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package ansible
|
||||
|
||||
// BinaryDeployPlaybook returns the Ansible playbook for binary deployment
|
||||
const BinaryDeployPlaybook = `---
|
||||
- name: Deploy HellreigN Agent (Binary)
|
||||
hosts: all
|
||||
become: yes
|
||||
vars:
|
||||
agent_label: "{{ agent_label }}"
|
||||
agent_token: "{{ agent_token }}"
|
||||
backend_url: "{{ backend_url }}"
|
||||
install_dir: /opt/hellreign
|
||||
bin_name: hellreign-agent
|
||||
service_name: hellreign-agent
|
||||
cert_dir: "{{ install_dir }}/certs"
|
||||
|
||||
tasks:
|
||||
- name: Create installation directory
|
||||
file:
|
||||
path: "{{ install_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create certificates directory
|
||||
file:
|
||||
path: "{{ cert_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Download HellreigN Agent binary
|
||||
get_url:
|
||||
url: "https://gitea.d3m0k1d.ru/d3m0k1d/HellreigN/releases/latest/download/{{ bin_name }}"
|
||||
dest: "{{ install_dir }}/{{ bin_name }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Create agent configuration
|
||||
copy:
|
||||
content: |
|
||||
backend_url: "{{ backend_url }}"
|
||||
label: "{{ agent_label }}"
|
||||
registration_token: "{{ agent_token }}"
|
||||
cert_dir: "{{ cert_dir }}"
|
||||
dest: "{{ install_dir }}/config.yml"
|
||||
mode: '0644'
|
||||
|
||||
- name: Create systemd service file
|
||||
copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=HellreigN Agent
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart={{ install_dir }}/{{ bin_name }}
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=CONFIG_FILE={{ install_dir }}/config.yml
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
dest: /etc/systemd/system/{{ service_name }}.service
|
||||
mode: '0644'
|
||||
|
||||
- name: Reload systemd daemon
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start HellreigN Agent service
|
||||
systemd:
|
||||
name: "{{ service_name }}"
|
||||
enabled: yes
|
||||
state: started
|
||||
`
|
||||
|
||||
// DockerDeployPlaybook returns the Ansible playbook for Docker deployment
|
||||
const DockerDeployPlaybook = `---
|
||||
- name: Deploy HellreigN Agent (Docker)
|
||||
hosts: all
|
||||
become: yes
|
||||
vars:
|
||||
agent_label: "{{ agent_label }}"
|
||||
agent_token: "{{ agent_token }}"
|
||||
backend_url: "{{ backend_url }}"
|
||||
container_name: hellreign-agent-{{ agent_label }}
|
||||
image: "gitea.d3m0k1d.ru/d3m0k1d/hellreign-agent:latest"
|
||||
cert_dir: /etc/hellreign-agent/certs
|
||||
|
||||
tasks:
|
||||
- name: Install Docker (if not present)
|
||||
block:
|
||||
- name: Check if Docker is installed
|
||||
command: docker --version
|
||||
register: docker_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
|
||||
- name: Install Docker
|
||||
shell: |
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
when: docker_check.rc != 0
|
||||
|
||||
- name: Create certificates directory
|
||||
file:
|
||||
path: "{{ cert_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Pull HellreigN Agent image
|
||||
community.docker.docker_image:
|
||||
name: "{{ image }}"
|
||||
source: pull
|
||||
|
||||
- name: Create agent configuration
|
||||
copy:
|
||||
content: |
|
||||
backend_url: "{{ backend_url }}"
|
||||
label: "{{ agent_label }}"
|
||||
registration_token: "{{ agent_token }}"
|
||||
cert_dir: "{{ cert_dir }}"
|
||||
dest: "{{ cert_dir }}/config.yml"
|
||||
mode: '0644'
|
||||
|
||||
- name: Create and run HellreigN Agent container
|
||||
community.docker.docker_container:
|
||||
name: "{{ container_name }}"
|
||||
image: "{{ image }}"
|
||||
state: started
|
||||
restart_policy: always
|
||||
volumes:
|
||||
- "{{ cert_dir }}:/etc/hellreign-agent/certs"
|
||||
env:
|
||||
CONFIG_FILE: /etc/hellreign-agent/certs/config.yml
|
||||
`
|
||||
@@ -0,0 +1,5 @@
|
||||
package ansible
|
||||
|
||||
const BaseInvTemplate = `
|
||||
|
||||
`
|
||||
Reference in New Issue
Block a user