diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 725bc1e..3fde793 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -11,6 +11,8 @@ jobs: container: puppet/pdk:3.4.0.1.55.g4519dd0 steps: - uses: actions/checkout@v4 + - name: Fix git ownership + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Run static validations run: /usr/local/bin/pdk bundle exec rake validate lint check env: @@ -20,10 +22,11 @@ jobs: name: 'Unit tests' runs-on: ubuntu-latest timeout-minutes: 60 -# container: puppet/puppet-dev-tools:4.x container: puppet/pdk:3.4.0.1.55.g4519dd0 steps: - uses: actions/checkout@v4 + - name: Fix git ownership + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Run unit tests on Puppet 8 run: /usr/local/bin/pdk test unit --puppet-version=8 env: @@ -42,6 +45,8 @@ jobs: container: puppet/pdk:3.4.0.1.55.g4519dd0 steps: - uses: actions/checkout@v4 + - name: Fix git ownership + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Run unit tests on Puppet 6 run: /usr/local/bin/pdk test unit --puppet-version=6 env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 19e4835..ddfc75b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Fix git ownership + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: "PDK Build" uses: docker://puppet/pdk:3.4.0.1.55.g4519dd0 with: diff --git a/.kiro/puppet-pabawi-module/design.md b/.kiro/puppet-pabawi-module/design.md index deb15bb..e871679 100644 --- a/.kiro/puppet-pabawi-module/design.md +++ b/.kiro/puppet-pabawi-module/design.md @@ -2,7 +2,7 @@ ## Overview -This Puppet module provides a flexible, modular approach to installing and configuring the Pabawi application with support for multiple installation methods (npm, docker), proxy configurations (nginx with SSL), and various integrations (Bolt, PuppetDB, and extensible integration framework). The design emphasizes separation of concerns through a class-based architecture where each component (proxy, installation, integrations) can be independently enabled, disabled, or swapped with alternative implementations. +This Puppet module provides a flexible, modular approach to installing and configuring the Pabawi application with support for multiple installation methods (npm, docker), proxy configurations (nginx with SSL), and various integrations (Bolt, PuppetDB, Puppetserver, Hiera, Ansible, SSH, Proxmox, and AWS). The design emphasizes separation of concerns through a class-based architecture where each component (proxy, installation, integrations) can be independently enabled, disabled, or swapped with alternative implementations. The module follows Puppet best practices with Hiera-driven configuration, allowing users to customize behavior through data rather than code modifications. The architecture supports extensibility for future integrations while maintaining backward compatibility and sensible defaults. @@ -12,7 +12,7 @@ The module follows Puppet best practices with Hiera-driven configuration, allowi graph TD A[pabawi::init] --> B{proxy_manage?} A --> C{install_manage?} - A --> D[Integration Manager] + A --> D[Integrations Loop] B -->|true| E[Dynamic Proxy Class] E --> F[pabawi::proxy::nginx] @@ -22,13 +22,14 @@ graph TD H --> I[pabawi::install::npm] H --> J[pabawi::install::docker] - D --> K{bolt_enable?} - D --> L{puppetdb_enable?} - D --> M[Other Integrations] - - K -->|true| N[pabawi::integrations::bolt] - L -->|true| O[pabawi::integrations::puppetdb] - M --> P[pabawi::integrations::*] + D --> N[pabawi::integrations::bolt] + D --> O[pabawi::integrations::puppetdb] + D --> P[pabawi::integrations::puppetserver] + D --> Q2[pabawi::integrations::hiera] + D --> R2[pabawi::integrations::ansible] + D --> S2[pabawi::integrations::ssh] + D --> T2[pabawi::integrations::proxmox] + D --> U2[pabawi::integrations::aws] F --> Q[SSL Configuration] Q --> R[Self-Signed Certs] @@ -63,14 +64,10 @@ sequenceDiagram Install->>Install: Install pabawi (npm/docker) end - Init->>Integrations: Process integration flags - - alt bolt_enable == true - Integrations->>Integrations: Configure Bolt integration - end + Init->>Integrations: Process integrations array - alt puppetdb_enable == true - Integrations->>Integrations: Configure PuppetDB integration + loop For each integration in array + Integrations->>Integrations: include pabawi::integrations:: end Integrations-->>Init: Integrations configured @@ -117,12 +114,10 @@ sequenceDiagram ```puppet class pabawi ( Boolean $proxy_manage = true, - String $proxy_class = 'pabawi::proxy::nginx', + String[1] $proxy_class = 'pabawi::proxy::nginx', Boolean $install_manage = true, - String $install_class = 'pabawi::install::npm', - Boolean $bolt_enable = false, - Boolean $puppetdb_enable = false, - Hash[String, Boolean] $integrations = {}, + String[1] $install_class = 'pabawi::install::npm', + Array[Enum['puppetdb', 'puppetserver', 'hiera', 'bolt', 'ansible', 'ssh', 'proxmox', 'aws']] $integrations = [], ) { # Class implementation } @@ -132,13 +127,13 @@ class pabawi ( - Load and validate Hiera configuration parameters - Conditionally include proxy class based on proxy_manage flag - Conditionally include installation class based on install_manage flag -- Iterate through integration flags and include appropriate integration classes +- Iterate through integrations array and include appropriate integration classes - Ensure proper ordering and dependencies between components **Validation Rules**: - proxy_class must be a valid Puppet class name - install_class must be a valid Puppet class name -- Integration flags must be boolean values +- integrations must contain only valid integration names ### Component 2: Nginx Proxy Class (pabawi::proxy::nginx) @@ -263,26 +258,87 @@ class pabawi::integrations::puppetdb ( - Set up query timeout and connection parameters - Create integration configuration files +### Component 7: SSH Integration Class (pabawi::integrations::ssh) + +**Purpose**: Configure Pabawi integration with direct SSH execution (connection pool, sudo support) + +**Interface**: +```puppet +class pabawi::integrations::ssh ( + Boolean $enabled = true, + Hash $settings = {}, +) { + # Class implementation +} +``` + +**Responsibilities**: +- Configure SSH connection defaults (host, port, user, key path) +- Set connection pool and concurrency limits +- Configure sudo settings for privilege escalation +- Write SSH_* environment variables to .env via concat fragment + +### Component 8: Proxmox Integration Class (pabawi::integrations::proxmox) + +**Purpose**: Configure Pabawi integration with Proxmox Virtual Environment for VM/LXC management + +**Interface**: +```puppet +class pabawi::integrations::proxmox ( + Boolean $enabled = true, + Hash $settings = {}, + Optional[String[1]] $ssl_ca_source = undef, + Optional[String[1]] $ssl_cert_source = undef, + Optional[String[1]] $ssl_key_source = undef, +) { + # Class implementation +} +``` + +**Responsibilities**: +- Configure Proxmox host, port, and authentication (token or username/password) +- Deploy SSL certificates for Proxmox API connection +- Write PROXMOX_* environment variables to .env via concat fragment +- Support token-based (recommended) and username/password authentication + +### Component 9: AWS Integration Class (pabawi::integrations::aws) + +**Purpose**: Configure Pabawi integration with AWS EC2 for inventory, lifecycle, and provisioning + +**Interface**: +```puppet +class pabawi::integrations::aws ( + Boolean $enabled = true, + Hash $settings = {}, +) { + # Class implementation +} +``` + +**Responsibilities**: +- Configure AWS credentials (access key, profile, or default chain) +- Set default region and multi-region discovery +- Write AWS_* environment variables to .env via concat fragment +- Support three authentication modes: explicit keys, named profile, default credential chain + ## Data Models ### Model 1: Module Configuration ```puppet type Pabawi::Config = Struct[{ - proxy_manage => Boolean, - proxy_class => String[1], - install_manage => Boolean, - install_class => String[1], - bolt_enable => Boolean, - puppetdb_enable => Boolean, - integrations => Hash[String[1], Boolean], + proxy_manage => Boolean, + proxy_class => String[1], + install_manage => Boolean, + install_class => String[1], + integrations => Array[String[1]], }] ``` **Validation Rules**: - All class names must be non-empty strings - Boolean flags must be explicitly true or false -- Integration hash keys must be non-empty strings +- integrations must be an array of valid integration names ### Model 2: SSL Configuration @@ -363,32 +419,12 @@ INPUT: config of type Pabawi::Config OUTPUT: configured integrations BEGIN - // Process built-in integrations - IF config.bolt_enable = true THEN - ASSERT config.bolt_project_path is defined - includeClass('pabawi::integrations::bolt') - END IF - - IF config.puppetdb_enable = true THEN - ASSERT config.puppetdb_server_url is defined - includeClass('pabawi::integrations::puppetdb') - END IF - - // Process custom integrations from hash - FOR each (name, enabled) IN config.integrations DO - ASSERT enabled is Boolean - - IF enabled = true THEN - integrationClass ← "pabawi::integrations::" + name - - IF classExists(integrationClass) THEN - includeClass(integrationClass) - ELSE - logWarning("Integration class not found: " + integrationClass) - END IF - END IF + // Process integrations from array + FOR each name IN config.integrations.unique DO + integrationClass ← "pabawi::integrations::" + name + includeClass(integrationClass) END FOR - + RETURN success END ``` @@ -676,16 +712,15 @@ include pabawi pabawi::proxy_manage: true pabawi::install_manage: true -pabawi::bolt_enable: true -pabawi::bolt_project_path: '/opt/bolt-project' - -pabawi::puppetdb_enable: true -pabawi::puppetdb_server_url: 'https://puppetdb.example.com:8081' - pabawi::integrations: - terraform: true - ansible: true - custom_integration: true + - bolt + - puppetdb + - puppetserver + - hiera + - ansible + - ssh + - proxmox + - aws # In manifest include pabawi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4c0158c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,118 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2025-04-12 + +### Added + +- **Proxmox integration** (`pabawi::integrations::proxmox`) — Proxmox VE + support for VM/LXC provisioning and lifecycle management. Token and + username/password authentication, SSL certificate deployment via + `ssl_ca_source`, `ssl_cert_source`, `ssl_key_source` parameters. +- **AWS integration** (`pabawi::integrations::aws`) — AWS EC2 inventory + discovery, lifecycle actions (start/stop/reboot/terminate), and instance + provisioning. Supports explicit access keys, named profiles, and the + default AWS credential chain. +- **SSH integration** added to the `integrations` enum — the manifest existed + since 0.2.0 but was not accepted by the `Enum` type on `pabawi::integrations`. +- New example `examples/ssh_ansible_proxmox.pp` — agentless infrastructure + setup with SSH, Ansible, and Proxmox. +- New example `examples/hiera_full_integrations.yaml` — complete Hiera data + covering all eight integrations. +- SSH, Proxmox, and AWS integration reference sections in README with + parameter tables, Hiera examples, and generated `.env` samples. + +### Changed + +- **Docker installation refactored to systemd** — `pabawi::install::docker` + now runs the container as a native systemd service via `docker run`, + removing the dependency on the `puppetlabs/docker` module. New parameters: + `manage_docker`, `docker_package`, `bind_address`, `database_host_dir`, + `container_uid`, `container_gid`, `command_whitelist`, + `command_whitelist_allow_all`, `docker_extra_args`. +- **NPM installation** — added `bind_address` parameter that defaults to + `127.0.0.1` when `pabawi::proxy_manage` is true, `0.0.0.0` otherwise. +- `pabawi::integrations` enum expanded from + `['puppetdb','puppetserver','hiera','bolt','ansible']` to + `['puppetdb','puppetserver','hiera','bolt','ansible','ssh','proxmox','aws']`. +- Integration SSL certificate deployment (PuppetDB, Puppet Server) refactored + to use `ensure_resource` for directory management, avoiding duplicate + resource declarations when multiple integrations share parent directories. +- `examples/full_integrations.pp` slimmed to a minimal Puppet-centric setup + (Bolt, PuppetDB, Puppet Server, Hiera). Full verbose examples moved to + `hiera_full_integrations.yaml`. +- `examples/docker_custom_ssl.pp` updated to use `v1.0.0` image and proper + `auth_enabled`/`jwt_secret` parameters. +- README rewritten: added SSH/Proxmox/AWS integration reference, updated all + inline examples to use current parameter names and default paths, replaced + large inline Hiera examples with pointers to `examples/` directory. +- Hiera defaults (`data/common.yaml`) updated with commented examples for all + eight integrations. +- Design document updated with Proxmox (Component 8), SSH (Component 7), and + AWS (Component 9) sections; architecture diagram expanded. +- Spec tests updated to cover `ssh`, `proxmox`, and `aws` integration values. + +### Fixed + +- `.env` concat fragment ordering now correctly sequences all integrations: + Bolt (20), PuppetDB (21), Puppet Server (22), Hiera (23), Ansible (24), + SSH (25), Proxmox (26), AWS (27). +- Fixed service exec path in NPM installation (contributed by @tam116 in #9). + +### Removed + +- `puppetlabs/docker` is no longer a required dependency — Docker installation + uses direct `docker run` via systemd instead. + +## [0.2.0] - 2025-02-15 + +### Changed + +- Refactored all integrations to use a `settings` hash parameter instead of + individual parameters, providing a uniform interface across integrations. +- Updated integration configuration examples and documentation to use the new + settings hash pattern. +- Updated module dependencies version ranges. + +## [0.1.1] - 2025-01-20 + +### Changed + +- Updated supported OS matrix (Ubuntu 24.04, Debian 13, RHEL 8/9/10). +- Upgraded `puppetlabs_spec_helper` dependency. + +### Fixed + +- Various Gemfile dependency fixes for CI compatibility. + +## [0.1.0] - 2025-01-10 + +### Added + +- Initial release. +- Core module with `pabawi::init` entry point, proxy management, and + installation management. +- NPM installation method (`pabawi::install::npm`). +- Docker installation method (`pabawi::install::docker`). +- Nginx reverse proxy with SSL (`pabawi::proxy::nginx`). +- Bolt integration (`pabawi::integrations::bolt`). +- PuppetDB integration (`pabawi::integrations::puppetdb`). +- Puppet Server integration (`pabawi::integrations::puppetserver`). +- Hiera integration (`pabawi::integrations::hiera`). +- Ansible integration (`pabawi::integrations::ansible`). +- SSH integration (`pabawi::integrations::ssh`). +- Hiera-driven configuration with `data/common.yaml` defaults. +- `Pabawi::Config`, `Pabawi::SSL::Config`, `Pabawi::Integration::Config` types. +- `pabawi::validate_configuration` function. +- rspec-puppet spec tests. +- GitHub Actions CI/CD workflows. +- Usage examples in `examples/`. + +[1.0.0]: https://github.com/example42/puppet-pabawi/compare/0.2.0...1.0.0 +[0.2.0]: https://github.com/example42/puppet-pabawi/compare/0.1.1...0.2.0 +[0.1.1]: https://github.com/example42/puppet-pabawi/compare/0.1.0...0.1.1 +[0.1.0]: https://github.com/example42/puppet-pabawi/releases/tag/0.1.0 diff --git a/README.md b/README.md index 604d7a0..46140b8 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ Puppet module for managing Pabawi - a unified interface for Puppet ecosystem too - [PuppetDB](#puppetdb-integration) - [Puppet Server](#puppet-server-integration) - [Hiera](#hiera-integration) + - [SSH](#ssh-integration) + - [Proxmox](#proxmox-integration) + - [AWS](#aws-integration) - [Examples](#examples) - [Reference](#reference) - [Limitations](#limitations) @@ -31,7 +34,7 @@ Puppet module for managing Pabawi - a unified interface for Puppet ecosystem too ## Description -Pabawi is a unified web interface for interacting with various Puppet ecosystem tools including Bolt, PuppetDB, Puppet Server, Hiera, and Ansible. This Puppet module manages the installation and configuration of Pabawi and its integrations. +Pabawi is a unified web interface for interacting with various Puppet ecosystem tools including Bolt, PuppetDB, Puppet Server, Hiera, Ansible, SSH, Proxmox, and AWS. This Puppet module manages the installation and configuration of Pabawi and its integrations. **Key Features:** - `.env` file-based configuration for all integrations @@ -220,8 +223,8 @@ DATABASE_PATH=... CONCURRENT_EXECUTION_LIMIT=10 # Bolt Integration (order: 20) +BOLT_ENABLED=true BOLT_PROJECT_PATH=/opt/bolt-project -COMMAND_WHITELIST=["ls","pwd"] ... # PuppetDB Integration (order: 21) @@ -240,6 +243,18 @@ HIERA_ENABLED=true # Ansible Integration (order: 24) ANSIBLE_ENABLED=true ... + +# SSH Integration (order: 25) +SSH_ENABLED=true +... + +# Proxmox Integration (order: 26) +PROXMOX_ENABLED=true +... + +# AWS Integration (order: 27) +AWS_ENABLED=true +... ``` ### Content Sources @@ -569,6 +584,164 @@ HIERA_ENVIRONMENTS=["production","development","staging"] HIERA_FACT_SOURCE_PREFER_PUPPETDB=true ``` +### SSH Integration + +Manages direct SSH execution configuration. + +**Parameters:** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `enabled` | Boolean | true | Whether the integration is enabled (sets SSH_ENABLED in .env) | +| `settings` | Hash | {} | Hash of configuration settings (see below) | + +**Settings Hash Keys:** + +| Key | Type | Description | +|-----|------|-------------| +| `config_path` | String | Path to SSH config file | +| `default_user` | String | Default SSH username | +| `default_port` | Integer | Default SSH port | +| `default_key` | String | Path to default SSH private key | +| `host_key_check` | Boolean | Enable host key checking | +| `connection_timeout` | Integer | Connection timeout (seconds) | +| `command_timeout` | Integer | Command timeout (seconds) | +| `max_connections` | Integer | Maximum total connections | +| `max_connections_per_host` | Integer | Maximum connections per host | +| `idle_timeout` | Integer | Idle connection timeout (seconds) | +| `concurrency_limit` | Integer | Concurrent execution limit | + +**Example:** + +```yaml +pabawi::integrations: + - ssh + +pabawi::integrations::ssh::settings: + default_user: 'automation' + default_port: 22 + default_key: '/opt/pabawi/ssh/id_ed25519' + host_key_check: true + connection_timeout: 30 + command_timeout: 300 + max_connections: 50 + concurrency_limit: 10 +``` + +**Generated .env entries:** +``` +SSH_ENABLED=true +SSH_DEFAULT_USER=automation +SSH_DEFAULT_PORT=22 +SSH_DEFAULT_KEY=/opt/pabawi/ssh/id_ed25519 +SSH_HOST_KEY_CHECK=true +SSH_CONNECTION_TIMEOUT=30 +SSH_COMMAND_TIMEOUT=300 +SSH_MAX_CONNECTIONS=50 +SSH_CONCURRENCY_LIMIT=10 +``` + +### Proxmox Integration + +Manages Proxmox VE connection for VM/LXC provisioning and lifecycle management. + +**Parameters:** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `enabled` | Boolean | true | Whether the integration is enabled (sets PROXMOX_ENABLED in .env) | +| `settings` | Hash | {} | Hash of configuration settings (see below) | +| `ssl_ca_source` | String | undef | URL to download CA cert from | +| `ssl_cert_source` | String | undef | URL to download client cert from | +| `ssl_key_source` | String | undef | URL to download private key from | + +**Settings Hash Keys:** + +| Key | Type | Description | +|-----|------|-------------| +| `host` | String | Proxmox host (required) | +| `port` | Integer | Proxmox API port (default 8006) | +| `token` | String | API token (recommended auth method) | +| `username` | String | Username (alternative auth) | +| `password` | String | Password (alternative auth) | +| `realm` | String | Authentication realm | +| `ssl_reject_unauthorized` | Boolean | Reject unauthorized certificates | +| `ssl_ca` | String | Path to CA certificate | +| `ssl_cert` | String | Path to client certificate | +| `ssl_key` | String | Path to private key | +| `timeout` | Integer | API timeout (milliseconds) | +| `priority` | Integer | Integration priority | + +**Example:** + +```yaml +pabawi::integrations: + - proxmox + +pabawi::integrations::proxmox::settings: + host: 'proxmox.example.com' + port: 8006 + token: 'user@pam!tokenid=token-value' + ssl_reject_unauthorized: true + timeout: 30000 +``` + +**Generated .env entries:** +``` +PROXMOX_ENABLED=true +PROXMOX_HOST=proxmox.example.com +PROXMOX_PORT=8006 +PROXMOX_TOKEN=user@pam!tokenid=token-value +PROXMOX_SSL_REJECT_UNAUTHORIZED=true +PROXMOX_TIMEOUT=30000 +``` + +### AWS Integration + +Manages AWS EC2 integration for inventory discovery, lifecycle actions, and provisioning. + +**Parameters:** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `enabled` | Boolean | true | Whether the integration is enabled (sets AWS_ENABLED in .env) | +| `settings` | Hash | {} | Hash of configuration settings (see below) | + +**Settings Hash Keys:** + +| Key | Type | Description | +|-----|------|-------------| +| `access_key_id` | String | AWS access key ID (explicit auth) | +| `secret_access_key` | String | AWS secret access key (explicit auth) | +| `default_region` | String | Default AWS region | +| `regions` | Array[String] | Regions to query for inventory | +| `session_token` | String | AWS session token (temporary credentials) | +| `profile` | String | AWS named profile (recommended) | +| `endpoint` | String | Custom AWS endpoint URL | + +**Example:** + +```yaml +pabawi::integrations: + - aws + +# Using named profile (recommended) +pabawi::integrations::aws::settings: + profile: 'pabawi-prod' + default_region: 'us-east-1' + regions: + - 'us-east-1' + - 'eu-west-1' +``` + +**Generated .env entries:** +``` +AWS_ENABLED=true +AWS_PROFILE=pabawi-prod +AWS_DEFAULT_REGION=us-east-1 +AWS_REGIONS=["us-east-1","eu-west-1"] +``` + ## Examples ### Complete Multi-Integration Setup @@ -587,19 +760,22 @@ pabawi::integrations: - puppetdb - puppetserver - hiera + - ssh + - proxmox + - aws # Bolt integration configuration pabawi::integrations::bolt::manage_package: true pabawi::integrations::bolt::settings: - project_path: '/opt/bolt-project' + project_path: '/opt/pabawi/bolt-project' execution_timeout: 300000 pabawi::integrations::bolt::project_path_source: 'https://github.com/myorg/bolt-project.git' # Ansible integration configuration pabawi::integrations::ansible::manage_package: true pabawi::integrations::ansible::settings: - inventory_path: '/etc/ansible/inventory' - playbook_path: '/etc/ansible/playbooks' + inventory_path: '/opt/pabawi/ansible/inventory' + playbook_path: '/opt/pabawi/ansible/playbooks' execution_timeout: 300000 pabawi::integrations::ansible::inventory_source: 'https://github.com/myorg/ansible-inventory.git' pabawi::integrations::ansible::playbook_source: 'https://github.com/myorg/ansible-playbooks.git' @@ -609,9 +785,6 @@ pabawi::integrations::puppetdb::settings: server_url: 'https://puppetdb.myorg.com' port: 8081 ssl_enabled: true - ssl_ca: '/etc/pabawi/ssl/puppetdb/ca.pem' - ssl_cert: '/etc/pabawi/ssl/puppetdb/cert.pem' - ssl_key: '/etc/pabawi/ssl/puppetdb/key.pem' ssl_reject_unauthorized: true pabawi::integrations::puppetdb::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' pabawi::integrations::puppetdb::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem' @@ -622,9 +795,6 @@ pabawi::integrations::puppetserver::settings: server_url: 'https://puppet.myorg.com' port: 8140 ssl_enabled: true - ssl_ca: '/etc/pabawi/ssl/puppetserver/ca.pem' - ssl_cert: '/etc/pabawi/ssl/puppetserver/cert.pem' - ssl_key: '/etc/pabawi/ssl/puppetserver/key.pem' ssl_reject_unauthorized: true inactivity_threshold: 3600 cache_ttl: 300000 @@ -633,9 +803,8 @@ pabawi::integrations::puppetserver::ssl_cert_source: 'file:///etc/puppetlabs/pup pabawi::integrations::puppetserver::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem' # Hiera integration configuration -pabawi::integrations::hiera::manage_package: false pabawi::integrations::hiera::settings: - control_repo_path: '/opt/control-repo' + control_repo_path: '/opt/pabawi/control-repo' config_path: 'hiera.yaml' environments: - 'production' @@ -643,16 +812,42 @@ pabawi::integrations::hiera::settings: fact_source_prefer_puppetdb: true pabawi::integrations::hiera::control_repo_source: 'https://github.com/myorg/control-repo.git' +# SSH integration configuration +pabawi::integrations::ssh::settings: + default_user: 'automation' + default_port: 22 + default_key: '/opt/pabawi/ssh/id_ed25519' + host_key_check: true + connection_timeout: 30 + max_connections: 50 + concurrency_limit: 10 + +# Proxmox integration configuration +pabawi::integrations::proxmox::settings: + host: 'proxmox.myorg.com' + port: 8006 + token: 'automation@pve!pabawi=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ssl_reject_unauthorized: true + timeout: 30000 + +# AWS integration configuration +pabawi::integrations::aws::settings: + profile: 'pabawi-prod' + default_region: 'us-east-1' + regions: + - 'us-east-1' + - 'eu-west-1' + # Nginx proxy settings pabawi::proxy::nginx::ssl_enable: true pabawi::proxy::nginx::ssl_self_signed: false -pabawi::proxy::nginx::ssl_cert: '/etc/ssl/certs/pabawi.crt' -pabawi::proxy::nginx::ssl_key: '/etc/ssl/private/pabawi.key' +pabawi::proxy::nginx::ssl_cert_source: 'puppet:///modules/site/ssl/pabawi.crt' +pabawi::proxy::nginx::ssl_key_source: 'puppet:///modules/site/ssl/pabawi.key' pabawi::proxy::nginx::listen_port: 443 pabawi::proxy::nginx::backend_port: 3000 # NPM installation settings -pabawi::install::npm::install_dir: '/opt/pabawi' +pabawi::install::npm::install_dir: '/opt/pabawi/app' pabawi::install::npm::repo_url: 'https://github.com/example42/pabawi.git' pabawi::install::npm::version: 'v1.0.0' pabawi::install::npm::auth_enabled: true @@ -695,237 +890,19 @@ pabawi::integrations: # Configure Bolt pabawi::integrations::bolt::settings: - project_path: '/opt/bolt-project' + project_path: '/opt/pabawi/bolt-project' ``` ### Complete Hiera Configuration Examples -#### NPM Installation with Full Integration Stack - -Complete Hiera configuration for NPM-based installation with Bolt, Hiera, PuppetDB, and Puppet Server integrations: - -```yaml ---- -# File: data/common.yaml or data/nodes/.yaml - -# Installation method -pabawi::install_manage: true -pabawi::install_class: 'pabawi::install::npm' - -# NPM installation settings -pabawi::install::npm::install_dir: '/opt/pabawi' -pabawi::install::npm::repo_url: 'https://github.com/example42/pabawi.git' -pabawi::install::npm::version: 'main' -pabawi::install::npm::user: 'pabawi' -pabawi::install::npm::group: 'pabawi' -pabawi::install::npm::auth_enabled: true -pabawi::install::npm::jwt_secret: 'change-this-to-a-secure-random-string' -pabawi::install::npm::log_level: 'info' -pabawi::install::npm::concurrent_execution_limit: 10 +For full Hiera configuration examples covering all integrations (Bolt, PuppetDB, Puppet Server, Hiera, Ansible, SSH, Proxmox, AWS), see the `examples/` directory: -# Proxy configuration -pabawi::proxy_manage: true -pabawi::proxy_class: 'pabawi::proxy::nginx' - -# Nginx proxy settings -pabawi::proxy::nginx::server_name: 'pabawi.example.com' -pabawi::proxy::nginx::listen_port: 443 -pabawi::proxy::nginx::backend_port: 3000 -pabawi::proxy::nginx::ssl_enable: true -pabawi::proxy::nginx::ssl_self_signed: false -pabawi::proxy::nginx::ssl_cert: '/etc/ssl/certs/pabawi.example.com.crt' -pabawi::proxy::nginx::ssl_key: '/etc/ssl/private/pabawi.example.com.key' - -# Enable integrations -pabawi::integrations: - - bolt - - hiera - - puppetdb - - puppetserver - -# Bolt Integration -pabawi::integrations::bolt::enabled: true -pabawi::integrations::bolt::manage_package: true -pabawi::integrations::bolt::settings: - project_path: '/opt/pabawi-bolt-project' - execution_timeout: 300000 -pabawi::integrations::bolt::project_path_source: 'https://github.com/example/bolt-project.git' - -# Hiera Integration -pabawi::integrations::hiera::enabled: true -pabawi::integrations::hiera::manage_package: false -pabawi::integrations::hiera::settings: - control_repo_path: '/opt/pabawi-control-repo' - config_path: 'hiera.yaml' - environments: - - 'production' - - 'development' - - 'staging' - fact_source_prefer_puppetdb: true -pabawi::integrations::hiera::control_repo_source: 'https://github.com/example/control-repo.git' - -# PuppetDB Integration -pabawi::integrations::puppetdb::enabled: true -pabawi::integrations::puppetdb::settings: - server_url: 'https://puppetdb.example.com' - port: 8081 - ssl_enabled: true - ssl_ca: '/etc/pabawi/ssl/puppetdb/ca.pem' - ssl_cert: '/etc/pabawi/ssl/puppetdb/cert.pem' - ssl_key: '/etc/pabawi/ssl/puppetdb/key.pem' - ssl_reject_unauthorized: true -pabawi::integrations::puppetdb::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' -pabawi::integrations::puppetdb::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/%{facts.fqdn}.pem' -pabawi::integrations::puppetdb::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/%{facts.fqdn}.pem' - -# Puppet Server Integration -pabawi::integrations::puppetserver::enabled: true -pabawi::integrations::puppetserver::settings: - server_url: 'https://puppet.example.com' - port: 8140 - ssl_enabled: true - ssl_ca: '/etc/pabawi/ssl/puppetserver/ca.pem' - ssl_cert: '/etc/pabawi/ssl/puppetserver/cert.pem' - ssl_key: '/etc/pabawi/ssl/puppetserver/key.pem' - ssl_reject_unauthorized: true - inactivity_threshold: 3600 - cache_ttl: 300000 - circuit_breaker_threshold: 5 - circuit_breaker_timeout: 60000 - circuit_breaker_reset_timeout: 30000 -pabawi::integrations::puppetserver::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' -pabawi::integrations::puppetserver::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/%{facts.fqdn}.pem' -pabawi::integrations::puppetserver::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/%{facts.fqdn}.pem' -``` - -#### Docker Installation with Full Integration Stack - -Complete Hiera configuration for Docker-based installation with the same integrations: - -```yaml ---- -# File: data/common.yaml or data/nodes/.yaml - -# Installation method -pabawi::install_manage: true -pabawi::install_class: 'pabawi::install::docker' - -# Docker installation settings -pabawi::install::docker::install_dir: '/opt/pabawi' -pabawi::install::docker::image: 'example42/pabawi:latest' -pabawi::install::docker::container_name: 'pabawi' -pabawi::install::docker::backend_port: 3000 -pabawi::install::docker::user: 'pabawi' -pabawi::install::docker::group: 'pabawi' -pabawi::install::docker::auth_enabled: true -pabawi::install::docker::jwt_secret: 'change-this-to-a-secure-random-string' -pabawi::install::docker::log_level: 'info' -pabawi::install::docker::concurrent_execution_limit: 10 -pabawi::install::docker::volumes: - - '/opt/pabawi-bolt-project:/app/bolt-project:ro' - - '/opt/pabawi-control-repo:/app/control-repo:ro' - - '/etc/pabawi/ssl:/app/ssl:ro' - - '/opt/pabawi/data:/app/data' - -# Proxy configuration -pabawi::proxy_manage: true -pabawi::proxy_class: 'pabawi::proxy::nginx' - -# Nginx proxy settings -pabawi::proxy::nginx::server_name: 'pabawi.example.com' -pabawi::proxy::nginx::listen_port: 443 -pabawi::proxy::nginx::backend_port: 3000 -pabawi::proxy::nginx::ssl_enable: true -pabawi::proxy::nginx::ssl_self_signed: false -pabawi::proxy::nginx::ssl_cert: '/etc/ssl/certs/pabawi.example.com.crt' -pabawi::proxy::nginx::ssl_key: '/etc/ssl/private/pabawi.example.com.key' - -# Enable integrations -pabawi::integrations: - - bolt - - hiera - - puppetdb - - puppetserver - -# Bolt Integration -pabawi::integrations::bolt::enabled: true -pabawi::integrations::bolt::manage_package: true -pabawi::integrations::bolt::settings: - project_path: '/app/bolt-project' # Path inside container - execution_timeout: 300000 -pabawi::integrations::bolt::project_path_source: 'https://github.com/example/bolt-project.git' - -# Hiera Integration -pabawi::integrations::hiera::enabled: true -pabawi::integrations::hiera::manage_package: false -pabawi::integrations::hiera::settings: - control_repo_path: '/app/control-repo' # Path inside container - config_path: 'hiera.yaml' - environments: - - 'production' - - 'development' - - 'staging' - fact_source_prefer_puppetdb: true -pabawi::integrations::hiera::control_repo_source: 'https://github.com/example/control-repo.git' - -# PuppetDB Integration -pabawi::integrations::puppetdb::enabled: true -pabawi::integrations::puppetdb::settings: - server_url: 'https://puppetdb.example.com' - port: 8081 - ssl_enabled: true - ssl_ca: '/app/ssl/puppetdb/ca.pem' # Path inside container - ssl_cert: '/app/ssl/puppetdb/cert.pem' - ssl_key: '/app/ssl/puppetdb/key.pem' - ssl_reject_unauthorized: true -pabawi::integrations::puppetdb::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' -pabawi::integrations::puppetdb::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/%{facts.fqdn}.pem' -pabawi::integrations::puppetdb::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/%{facts.fqdn}.pem' - -# Puppet Server Integration -pabawi::integrations::puppetserver::enabled: true -pabawi::integrations::puppetserver::settings: - server_url: 'https://puppet.example.com' - port: 8140 - ssl_enabled: true - ssl_ca: '/app/ssl/puppetserver/ca.pem' # Path inside container - ssl_cert: '/app/ssl/puppetserver/cert.pem' - ssl_key: '/app/ssl/puppetserver/key.pem' - ssl_reject_unauthorized: true - inactivity_threshold: 3600 - cache_ttl: 300000 - circuit_breaker_threshold: 5 - circuit_breaker_timeout: 60000 - circuit_breaker_reset_timeout: 30000 -pabawi::integrations::puppetserver::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' -pabawi::integrations::puppetserver::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/%{facts.fqdn}.pem' -pabawi::integrations::puppetserver::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/%{facts.fqdn}.pem' -``` - -#### Key Differences Between NPM and Docker Configurations - -**NPM Installation:** -- `.env` file location: `/opt/pabawi/backend/.env` -- Paths reference host filesystem directly -- Bolt project path: `/opt/pabawi-bolt-project` -- Control repo path: `/opt/pabawi-control-repo` -- SSL certificates: `/etc/pabawi/ssl//` - -**Docker Installation:** -- `.env` file location: `/opt/pabawi/.env` (mounted into container) -- Paths reference container filesystem (mounted volumes) -- Bolt project path: `/app/bolt-project` (mounted from `/opt/pabawi-bolt-project`) -- Control repo path: `/app/control-repo` (mounted from `/opt/pabawi-control-repo`) -- SSL certificates: `/app/ssl//` (mounted from `/etc/pabawi/ssl`) -- Requires volume mounts in `pabawi::install::docker::volumes` - -**Important Notes:** -1. Replace `example.com` with your actual domain -2. Change JWT secret to a secure random string -3. Update git repository URLs to your actual repositories -4. Adjust SSL certificate paths if using different locations -5. For Docker, ensure volume mounts align with paths in settings -6. Use Puppet facts (e.g., `%{facts.fqdn}`) for dynamic certificate paths +- `examples/hiera_full_integrations.yaml` — Complete Hiera data with every integration +- `examples/full_integrations.pp` — Puppet-centric setup (Bolt, PuppetDB, Puppet Server, Hiera) +- `examples/ssh_ansible_proxmox.pp` — Agentless setup (SSH, Ansible, Proxmox) +- `examples/docker_custom_ssl.pp` — Docker installation with custom SSL +- `examples/basic_nginx_npm.pp` — Minimal defaults +- `examples/minimal_no_proxy.pp` — No proxy, npm only ## Reference @@ -965,7 +942,10 @@ puppet-pabawi/ │ ├── ansible.pp # Ansible integration │ ├── puppetdb.pp # PuppetDB integration │ ├── puppetserver.pp # Puppet Server integration -│ └── hiera.pp # Hiera integration +│ ├── hiera.pp # Hiera integration +│ ├── ssh.pp # SSH integration +│ ├── proxmox.pp # Proxmox integration +│ └── aws.pp # AWS integration ├── data/ │ └── common.yaml # Default Hiera data ├── examples/ # Usage examples diff --git a/data/common.yaml b/data/common.yaml index d217598..024aa0a 100644 --- a/data/common.yaml +++ b/data/common.yaml @@ -1,6 +1,10 @@ --- # Default configuration for Pabawi module # These values can be overridden in environment-specific or node-specific Hiera data +# +# All paths default to /opt/pabawi/ for both npm and Docker installs. +# Users only need to specify *_source parameters in Hiera to tell Puppet where +# to fetch git repos, certificates, or other files from. # Proxy management pabawi::proxy_manage: true @@ -20,6 +24,9 @@ pabawi::integrations: [] # - hiera # - puppetdb # - puppetserver +# - ssh +# - proxmox +# - aws # Integration Control: # 1. Array inclusion: Integration class is included (resources created, .env fragment added) @@ -28,58 +35,105 @@ pabawi::integrations: [] # This allows pre-configuration without enabling, and easy toggling via enabled parameter # Bolt integration configuration -# pabawi::integrations::bolt::enabled: true # Set to false to disable but keep configured -pabawi::integrations::bolt::project_path: '/opt/bolt-project' +# pabawi::integrations::bolt::enabled: true +# pabawi::integrations::bolt::settings: +# project_path: '/opt/pabawi/bolt-project' # default, override if needed +# execution_timeout: 300000 # pabawi::integrations::bolt::project_path_source: 'https://github.com/example/bolt-project.git' # pabawi::integrations::bolt::manage_package: false -# pabawi::integrations::bolt::command_whitelist: ['plan run', 'task run'] -# pabawi::integrations::bolt::command_whitelist_allow_all: false -# pabawi::integrations::bolt::execution_timeout: 300000 # PuppetDB integration configuration -# pabawi::integrations::puppetdb::enabled: true # Set to false to disable but keep configured -pabawi::integrations::puppetdb::server_url: 'https://puppetdb.example.com:8081' -# pabawi::integrations::puppetdb::port: 8081 -# pabawi::integrations::puppetdb::ssl_enabled: true -# pabawi::integrations::puppetdb::ssl_cert: '/etc/puppetlabs/puppet/ssl/certs/agent.pem' -# pabawi::integrations::puppetdb::ssl_key: '/etc/puppetlabs/puppet/ssl/private_keys/agent.pem' -# pabawi::integrations::puppetdb::ssl_ca: '/etc/puppetlabs/puppet/ssl/certs/ca.pem' -# pabawi::integrations::puppetdb::ssl_reject_unauthorized: true +# pabawi::integrations::puppetdb::enabled: true +# pabawi::integrations::puppetdb::settings: +# server_url: 'https://puppetdb.example.com' +# port: 8081 +# ssl_enabled: true +# ssl_ca: '/opt/pabawi/certs/puppetdb/ca.pem' # default, override if needed +# ssl_cert: '/opt/pabawi/certs/puppetdb/cert.pem' # default, override if needed +# ssl_key: '/opt/pabawi/certs/puppetdb/key.pem' # default, override if needed +# ssl_reject_unauthorized: true +# pabawi::integrations::puppetdb::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' +# pabawi::integrations::puppetdb::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem' +# pabawi::integrations::puppetdb::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem' # Puppet Server integration configuration -# pabawi::integrations::puppetserver::enabled: true # Set to false to disable but keep configured -pabawi::integrations::puppetserver::server_url: 'https://puppet.example.com:8140' -# pabawi::integrations::puppetserver::port: 8140 -# pabawi::integrations::puppetserver::ssl_enabled: true -# pabawi::integrations::puppetserver::ssl_cert: '/etc/puppetlabs/puppet/ssl/certs/agent.pem' -# pabawi::integrations::puppetserver::ssl_key: '/etc/puppetlabs/puppet/ssl/private_keys/agent.pem' -# pabawi::integrations::puppetserver::ssl_ca: '/etc/puppetlabs/puppet/ssl/certs/ca.pem' -# pabawi::integrations::puppetserver::ssl_reject_unauthorized: true -# pabawi::integrations::puppetserver::inactivity_threshold: 3600 -# pabawi::integrations::puppetserver::cache_ttl: 300000 -# pabawi::integrations::puppetserver::circuit_breaker_threshold: 5 -# pabawi::integrations::puppetserver::circuit_breaker_timeout: 60000 -# pabawi::integrations::puppetserver::circuit_breaker_reset_timeout: 30000 +# pabawi::integrations::puppetserver::enabled: true +# pabawi::integrations::puppetserver::settings: +# server_url: 'https://puppet.example.com' +# port: 8140 +# ssl_enabled: true +# ssl_ca: '/opt/pabawi/certs/puppetserver/ca.pem' # default, override if needed +# ssl_cert: '/opt/pabawi/certs/puppetserver/cert.pem' # default, override if needed +# ssl_key: '/opt/pabawi/certs/puppetserver/key.pem' # default, override if needed +# ssl_reject_unauthorized: true +# inactivity_threshold: 3600 +# cache_ttl: 300000 +# circuit_breaker_threshold: 5 +# circuit_breaker_timeout: 60000 +# circuit_breaker_reset_timeout: 30000 +# pabawi::integrations::puppetserver::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' +# pabawi::integrations::puppetserver::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem' +# pabawi::integrations::puppetserver::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem' # Hiera integration configuration -# pabawi::integrations::hiera::enabled: true # Set to false to disable but keep configured -pabawi::integrations::hiera::control_repo_path: '/opt/control-repo' +# pabawi::integrations::hiera::enabled: true +# pabawi::integrations::hiera::settings: +# control_repo_path: '/opt/pabawi/control-repo' # default, override if needed +# config_path: 'hiera_pabawi.yaml' +# environments: ['production'] +# fact_source_prefer_puppetdb: true +# fact_source_local_path: '/opt/pabawi/facts' # pabawi::integrations::hiera::control_repo_source: 'https://github.com/example/control-repo.git' # pabawi::integrations::hiera::manage_package: false -# pabawi::integrations::hiera::config_path: 'hiera_pabawi.yaml' -# pabawi::integrations::hiera::environments: ['production'] -# pabawi::integrations::hiera::fact_source_prefer_puppetdb: true -# pabawi::integrations::hiera::fact_source_local_path: '/var/lib/puppet/facts' # Ansible integration configuration -# pabawi::integrations::ansible::enabled: true # Set to false to disable but keep configured -pabawi::integrations::ansible::inventory_path: '/etc/ansible/inventory' +# pabawi::integrations::ansible::enabled: true +# pabawi::integrations::ansible::settings: +# inventory_path: '/opt/pabawi/ansible/inventory' # default, override if needed +# playbook_path: '/opt/pabawi/ansible/playbooks' # default, override if needed +# execution_timeout: 300000 +# config: '/opt/pabawi/ansible/ansible.cfg' # pabawi::integrations::ansible::inventory_source: 'https://github.com/example/ansible-inventory.git' -# pabawi::integrations::ansible::playbook_path: '/etc/ansible/playbooks' # pabawi::integrations::ansible::playbook_source: 'https://github.com/example/ansible-playbooks.git' # pabawi::integrations::ansible::manage_package: false -# pabawi::integrations::ansible::execution_timeout: 300000 -# pabawi::integrations::ansible::config: '/etc/ansible/ansible.cfg' + +# SSH integration configuration +# pabawi::integrations::ssh::enabled: true +# pabawi::integrations::ssh::settings: +# config_path: '/opt/pabawi/ssh/config' +# default_user: 'root' +# default_port: 22 +# default_key: '/opt/pabawi/ssh/id_rsa' + +# Proxmox integration configuration +# pabawi::integrations::proxmox::enabled: true +# pabawi::integrations::proxmox::settings: +# host: 'proxmox.example.com' +# port: 8006 +# token: 'user@pam!tokenid=token-value' +# ssl_reject_unauthorized: true +# ssl_ca: '/opt/pabawi/certs/proxmox/ca.pem' # default, override if needed +# ssl_cert: '/opt/pabawi/certs/proxmox/cert.pem' # default, override if needed +# ssl_key: '/opt/pabawi/certs/proxmox/key.pem' # default, override if needed +# timeout: 30000 +# priority: 7 +# pabawi::integrations::proxmox::ssl_ca_source: 'file:///etc/ssl/certs/proxmox-ca.pem' +# pabawi::integrations::proxmox::ssl_cert_source: 'file:///etc/ssl/certs/proxmox-cert.pem' +# pabawi::integrations::proxmox::ssl_key_source: 'file:///etc/ssl/private/proxmox-key.pem' + +# AWS integration configuration +# pabawi::integrations::aws::enabled: true +# pabawi::integrations::aws::settings: +# profile: 'pabawi-prod' +# default_region: 'us-east-1' +# regions: +# - 'us-east-1' +# - 'eu-west-1' +# Or with explicit credentials: +# pabawi::integrations::aws::settings: +# access_key_id: 'AKIAIOSFODNN7EXAMPLE' +# secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' +# default_region: 'us-east-1' # Nginx proxy configuration pabawi::proxy::nginx::ssl_enable: true @@ -103,7 +157,7 @@ pabawi::install::npm::concurrent_execution_limit: 5 pabawi::install::docker::image: 'example42/pabawi:latest' pabawi::install::docker::container_name: 'pabawi' pabawi::install::docker::auto_restart: true -pabawi::install::docker::install_dir: '/opt/pabawi/app' +pabawi::install::docker::install_dir: '/opt/pabawi' pabawi::install::docker::log_level: 'info' pabawi::install::docker::auth_enabled: false # pabawi::install::docker::jwt_secret: 'your-secret-key-here' diff --git a/examples/docker_custom_ssl.pp b/examples/docker_custom_ssl.pp index 812c745..d65bb57 100644 --- a/examples/docker_custom_ssl.pp +++ b/examples/docker_custom_ssl.pp @@ -14,9 +14,7 @@ } class { 'pabawi::install::docker': - image => 'example42/pabawi:v0.8.0', - environment => { - 'NODE_ENV' => 'production', - 'PORT' => '3000', - }, + image => 'example42/pabawi:v1.0.0', + auth_enabled => true, + jwt_secret => 'change-this-to-a-secure-random-string', } diff --git a/examples/full_integrations.pp b/examples/full_integrations.pp index a6409a9..a370480 100644 --- a/examples/full_integrations.pp +++ b/examples/full_integrations.pp @@ -1,32 +1,50 @@ -# Example: Full installation with all integrations enabled +# Example: Puppet ecosystem setup # -# This example demonstrates a complete Pabawi setup with -# Bolt, PuppetDB, and Hiera integrations enabled. +# Minimal configuration for a Puppet-centric deployment with Bolt, +# PuppetDB, Puppet Server, and Hiera. SSL certificates are sourced +# from the local Puppet agent. All other settings use defaults. +# +# For the full Hiera version with every integration, see +# hiera_full_integrations.yaml in this directory. class { 'pabawi': - integrations => ['bolt', 'puppetdb', 'hiera'], + integrations => ['bolt', 'puppetdb', 'puppetserver', 'hiera'], } -# Configure Bolt integration via Hiera or class parameters +# Bolt — clone project from git class { 'pabawi::integrations::bolt': - project_path => '/opt/bolt-project', - command_whitelist => ['plan run', 'task run'], - execution_timeout => 300000, - manage_package => false, + project_path_source => 'https://github.com/example/bolt-project.git', } -# Configure PuppetDB integration +# PuppetDB — deploy SSL certs from Puppet agent class { 'pabawi::integrations::puppetdb': - server_url => 'https://puppetdb.example.com:8081', - ssl_ca_source => 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem', - ssl_cert_source => 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem', - ssl_key_source => 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem', - ssl_reject_unauthorized => true, + settings => { + 'server_url' => 'https://puppetdb.example.com', + 'port' => 8081, + 'ssl_enabled' => true, + }, + ssl_ca_source => 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem', + ssl_cert_source => 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem', + ssl_key_source => 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem', +} + +# Puppet Server — reuse same certs +class { 'pabawi::integrations::puppetserver': + settings => { + 'server_url' => 'https://puppet.example.com', + 'port' => 8140, + 'ssl_enabled' => true, + }, + ssl_ca_source => 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem', + ssl_cert_source => 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem', + ssl_key_source => 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem', } -# Configure Hiera integration +# Hiera — clone control repo from git class { 'pabawi::integrations::hiera': - control_repo_path => '/opt/control-repo', + settings => { + 'config_path' => 'hiera_pabawi.yaml', + 'environments' => ['production', 'development'], + }, control_repo_source => 'https://github.com/example/control-repo.git', - environments => ['production', 'development'], } diff --git a/examples/hiera_full_integrations.yaml b/examples/hiera_full_integrations.yaml new file mode 100644 index 0000000..a9a7a64 --- /dev/null +++ b/examples/hiera_full_integrations.yaml @@ -0,0 +1,144 @@ +# Example: Full Hiera configuration with all integrations +# +# This file shows the complete Hiera data for a Pabawi deployment with +# every integration enabled. Place in data/common.yaml or a node-specific +# Hiera file and simply `include pabawi` in your manifest. +# +# All paths default to /opt/pabawi/. Only override what you need. +# SSL certificate sources use the local Puppet agent certs by default. +--- + +# Installation +pabawi::install_class: 'pabawi::install::npm' + +# Proxy +pabawi::proxy_manage: true +pabawi::proxy::nginx::ssl_enable: true +pabawi::proxy::nginx::ssl_self_signed: true +pabawi::proxy::nginx::listen_port: 443 +pabawi::proxy::nginx::backend_port: 3000 + +# NPM installation +pabawi::install::npm::install_dir: '/opt/pabawi/app' +pabawi::install::npm::repo_url: 'https://github.com/example42/pabawi.git' +pabawi::install::npm::version: 'main' +pabawi::install::npm::user: 'pabawi' +pabawi::install::npm::group: 'pabawi' +pabawi::install::npm::log_level: 'info' +pabawi::install::npm::auth_enabled: true +pabawi::install::npm::jwt_secret: 'change-this-to-a-secure-random-string' +pabawi::install::npm::database_path: '/opt/pabawi/data/pabawi.db' +pabawi::install::npm::concurrent_execution_limit: 10 + +# All integrations enabled +pabawi::integrations: + - bolt + - puppetdb + - puppetserver + - hiera + - ansible + - ssh + - proxmox + - aws + +# --- Bolt --- +pabawi::integrations::bolt::enabled: true +pabawi::integrations::bolt::manage_package: true +pabawi::integrations::bolt::settings: + project_path: '/opt/pabawi/bolt-project' + execution_timeout: 300000 +pabawi::integrations::bolt::project_path_source: 'https://github.com/example/bolt-project.git' + +# --- PuppetDB --- +pabawi::integrations::puppetdb::enabled: true +pabawi::integrations::puppetdb::settings: + server_url: 'https://puppetdb.example.com' + port: 8081 + ssl_enabled: true + ssl_ca: '/opt/pabawi/certs/puppetdb/ca.pem' + ssl_cert: '/opt/pabawi/certs/puppetdb/cert.pem' + ssl_key: '/opt/pabawi/certs/puppetdb/key.pem' + ssl_reject_unauthorized: true +pabawi::integrations::puppetdb::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' +pabawi::integrations::puppetdb::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem' +pabawi::integrations::puppetdb::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem' + +# --- Puppet Server --- +pabawi::integrations::puppetserver::enabled: true +pabawi::integrations::puppetserver::settings: + server_url: 'https://puppet.example.com' + port: 8140 + ssl_enabled: true + ssl_ca: '/opt/pabawi/certs/puppetserver/ca.pem' + ssl_cert: '/opt/pabawi/certs/puppetserver/cert.pem' + ssl_key: '/opt/pabawi/certs/puppetserver/key.pem' + ssl_reject_unauthorized: true + inactivity_threshold: 3600 + cache_ttl: 300000 + circuit_breaker_threshold: 5 + circuit_breaker_timeout: 60000 + circuit_breaker_reset_timeout: 30000 +pabawi::integrations::puppetserver::ssl_ca_source: 'file:///etc/puppetlabs/puppet/ssl/certs/ca.pem' +pabawi::integrations::puppetserver::ssl_cert_source: 'file:///etc/puppetlabs/puppet/ssl/certs/agent.pem' +pabawi::integrations::puppetserver::ssl_key_source: 'file:///etc/puppetlabs/puppet/ssl/private_keys/agent.pem' + +# --- Hiera --- +pabawi::integrations::hiera::enabled: true +pabawi::integrations::hiera::settings: + control_repo_path: '/opt/pabawi/control-repo' + config_path: 'hiera_pabawi.yaml' + environments: + - 'production' + - 'development' + fact_source_prefer_puppetdb: true +pabawi::integrations::hiera::control_repo_source: 'https://github.com/example/control-repo.git' + +# --- Ansible --- +pabawi::integrations::ansible::enabled: true +pabawi::integrations::ansible::manage_package: true +pabawi::integrations::ansible::settings: + inventory_path: '/opt/pabawi/ansible/inventory' + playbook_path: '/opt/pabawi/ansible/playbooks' + execution_timeout: 300000 +pabawi::integrations::ansible::inventory_source: 'https://github.com/example/ansible-inventory.git' +pabawi::integrations::ansible::playbook_source: 'https://github.com/example/ansible-playbooks.git' + +# --- SSH --- +pabawi::integrations::ssh::enabled: true +pabawi::integrations::ssh::settings: + config_path: '/opt/pabawi/ssh/config' + default_user: 'root' + default_port: 22 + default_key: '/opt/pabawi/ssh/id_rsa' + host_key_check: true + connection_timeout: 30 + command_timeout: 300 + max_connections: 50 + max_connections_per_host: 5 + idle_timeout: 300 + concurrency_limit: 10 + +# --- Proxmox --- +pabawi::integrations::proxmox::enabled: true +pabawi::integrations::proxmox::settings: + host: 'proxmox.example.com' + port: 8006 + token: 'user@pam!tokenid=token-value' + ssl_reject_unauthorized: true + ssl_ca: '/opt/pabawi/certs/proxmox/ca.pem' + ssl_cert: '/opt/pabawi/certs/proxmox/cert.pem' + ssl_key: '/opt/pabawi/certs/proxmox/key.pem' + timeout: 30000 + priority: 7 +pabawi::integrations::proxmox::ssl_ca_source: 'file:///etc/ssl/certs/proxmox-ca.pem' +pabawi::integrations::proxmox::ssl_cert_source: 'file:///etc/ssl/certs/proxmox-cert.pem' +pabawi::integrations::proxmox::ssl_key_source: 'file:///etc/ssl/private/proxmox-key.pem' + +# --- AWS --- +pabawi::integrations::aws::enabled: true +pabawi::integrations::aws::settings: + profile: 'pabawi-prod' + default_region: 'us-east-1' + regions: + - 'us-east-1' + - 'eu-west-1' diff --git a/examples/ssh_ansible_proxmox.pp b/examples/ssh_ansible_proxmox.pp new file mode 100644 index 0000000..c14b079 --- /dev/null +++ b/examples/ssh_ansible_proxmox.pp @@ -0,0 +1,46 @@ +# Example: Agentless infrastructure with SSH, Ansible, and Proxmox +# +# This setup is for environments without Puppet agents where you manage +# nodes via SSH and Ansible, and provision VMs/LXCs on Proxmox. + +class { 'pabawi': + integrations => ['ssh', 'ansible', 'proxmox'], +} + +# SSH — direct execution with connection pool +class { 'pabawi::integrations::ssh': + settings => { + 'default_user' => 'automation', + 'default_port' => 22, + 'default_key' => '/opt/pabawi/ssh/id_ed25519', + 'host_key_check' => true, + 'connection_timeout' => 30, + 'command_timeout' => 300, + 'max_connections' => 50, + 'max_connections_per_host' => 5, + 'concurrency_limit' => 10, + }, +} + +# Ansible — clone inventory and playbooks from git +class { 'pabawi::integrations::ansible': + manage_package => true, + settings => { + 'inventory_path' => '/opt/pabawi/ansible/inventory', + 'playbook_path' => '/opt/pabawi/ansible/playbooks', + 'execution_timeout' => 300000, + }, + inventory_source => 'https://github.com/example/ansible-inventory.git', + playbook_source => 'https://github.com/example/ansible-playbooks.git', +} + +# Proxmox — token authentication (recommended) +class { 'pabawi::integrations::proxmox': + settings => { + 'host' => 'proxmox.example.com', + 'port' => 8006, + 'token' => 'automation@pve!pabawi=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', + 'ssl_reject_unauthorized' => true, + 'timeout' => 30000, + }, +} diff --git a/functions/validate_configuration.pp b/functions/validate_configuration.pp index b6ae6fc..6a08f1e 100644 --- a/functions/validate_configuration.pp +++ b/functions/validate_configuration.pp @@ -11,82 +11,78 @@ # # @example Validate a configuration # $config = { -# 'proxy_manage' => true, -# 'proxy_class' => 'pabawi::proxy::nginx', +# 'proxy_manage' => true, +# 'proxy_class' => 'pabawi::proxy::nginx', +# 'install_manage' => true, +# 'install_class' => 'pabawi::install::npm', +# 'integrations' => ['bolt', 'puppetdb'], # } # $valid = pabawi::validate_configuration($config) # function pabawi::validate_configuration(Hash $config) >> Boolean { # Check for required keys $required_keys = ['proxy_manage', 'install_manage'] - + $required_keys.each |$key| { unless $config[$key] =~ NotUndef { fail("Configuration missing required key: ${key}") } } - + # Validate proxy_manage is boolean unless $config['proxy_manage'] =~ Boolean { fail("proxy_manage must be a Boolean, got: ${config['proxy_manage']}") } - + # Validate install_manage is boolean unless $config['install_manage'] =~ Boolean { fail("install_manage must be a Boolean, got: ${config['install_manage']}") } - + # If proxy is managed, validate proxy_class if $config['proxy_manage'] { if $config['proxy_class'] { unless $config['proxy_class'] =~ String[1] { - fail("proxy_class must be a non-empty String") + fail('proxy_class must be a non-empty String') } - + unless $config['proxy_class'] =~ /^[a-z][a-z0-9_]*(::[a-z][a-z0-9_]*)*$/ { fail("proxy_class must be a valid Puppet class name: ${config['proxy_class']}") } } } - + # If installation is managed, validate install_class if $config['install_manage'] { if $config['install_class'] { unless $config['install_class'] =~ String[1] { - fail("install_class must be a non-empty String") + fail('install_class must be a non-empty String') } - + unless $config['install_class'] =~ /^[a-z][a-z0-9_]*(::[a-z][a-z0-9_]*)*$/ { fail("install_class must be a valid Puppet class name: ${config['install_class']}") } } } - - # Validate integration flags if present - if $config['bolt_enable'] { - unless $config['bolt_enable'] =~ Boolean { - fail("bolt_enable must be a Boolean") - } - } - - if $config['puppetdb_enable'] { - unless $config['puppetdb_enable'] =~ Boolean { - fail("puppetdb_enable must be a Boolean") - } - } - + + # Validate integrations if present if $config['integrations'] { - unless $config['integrations'] =~ Hash { - fail("integrations must be a Hash") + unless $config['integrations'] =~ Array { + fail('integrations must be an Array') } - - $config['integrations'].each |$name, $enabled| { - unless $enabled =~ Boolean { - fail("Integration '${name}' must have a Boolean value") + + $valid_integrations = [ + 'puppetdb', 'puppetserver', 'hiera', + 'bolt', 'ansible', 'ssh', 'proxmox', 'aws', + ] + + $config['integrations'].each |$name| { + unless $name in $valid_integrations { + fail("Unknown integration: '${name}'. Valid: ${valid_integrations.join(', ')}") } } } - + # All validations passed true } diff --git a/manifests/init.pp b/manifests/init.pp index 70c8eea..c366923 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -22,10 +22,10 @@ # # @param integrations # Array of integration names to enable. Only the following integrations are -# supported: puppetdb, puppetserver, hiera, bolt, ansible. Duplicates are -# automatically removed. Integration-specific configuration is managed via -# class parameters in Hiera. -# Example: ['bolt', 'puppetdb', 'hiera'] +# supported: puppetdb, puppetserver, hiera, bolt, ansible, ssh, proxmox, aws. +# Duplicates are automatically removed. Integration-specific configuration +# is managed via class parameters in Hiera. +# Example: ['bolt', 'puppetdb', 'hiera', 'ssh', 'proxmox', 'aws'] # # @example Basic usage with defaults # include pabawi @@ -50,7 +50,7 @@ String[1] $proxy_class = 'pabawi::proxy::nginx', Boolean $install_manage = true, String[1] $install_class = 'pabawi::install::npm', - Array[Enum['puppetdb', 'puppetserver', 'hiera', 'bolt', 'ansible']] $integrations = [], + Array[Enum['puppetdb', 'puppetserver', 'hiera', 'bolt', 'ansible', 'ssh', 'proxmox', 'aws']] $integrations = [], ) { # Validate proxy_class is a valid class name format if $proxy_manage { @@ -82,13 +82,6 @@ # Process integrations - deduplicate then include each class $integrations.unique.each |String $name| { - $integration_class = "pabawi::integrations::${name}" - - notify { "pabawi_integration_${name}": - message => "Enabling integration: ${integration_class}", - loglevel => 'notice', - } - - include $integration_class + include "pabawi::integrations::${name}" } } diff --git a/manifests/install/docker.pp b/manifests/install/docker.pp index 9a358eb..adfa6b2 100644 --- a/manifests/install/docker.pp +++ b/manifests/install/docker.pp @@ -1,56 +1,90 @@ -# @summary Install Pabawi using Docker containers +# @summary Install Pabawi as a Docker container managed by systemd # -# This class manages the installation of Pabawi using Docker, -# including Docker setup, image management, and container lifecycle. +# This class runs the Pabawi Docker container as a native systemd service +# using `docker run` directly, without requiring the puppetlabs/docker module. +# Docker packages can optionally be installed by this class. # # @param manage_docker -# Whether to manage the Docker installation. Set to false if Docker is managed elsewhere. +# Whether to install Docker packages. Default: false (assumes Docker is +# already installed and managed elsewhere). +# +# @param docker_package +# Name of the Docker package to install when manage_docker is true. # # @param image -# Docker image to use for Pabawi +# Docker image to use for Pabawi. # # @param container_name -# Name for the Docker container +# Name for the Docker container. # # @param environment -# Environment variables to pass to the container +# Environment variables to pass to the container. # # @param volumes -# Volume mounts for the container +# Volume mounts for the container (Docker -v format strings). # # @param ports -# Port mappings for the container (host => container) +# Port mappings for the container (host => container). +# +# @param bind_address +# IP address to bind port mappings to. Defaults to 127.0.0.1 when +# pabawi::proxy_manage is true (looked up from Hiera), 0.0.0.0 otherwise. # # @param auto_restart -# Whether to automatically restart the container on failure +# Whether systemd should restart the container on failure. +# +# @param install_dir +# Directory for configuration files (.env). # # @param log_level -# Application log level (debug, info, warn, error) +# Application log level (debug, info, warn, error). # # @param auth_enabled -# Whether authentication is enabled +# Whether authentication is enabled. # # @param jwt_secret -# JWT secret for authentication (required if auth_enabled is true) +# JWT secret for authentication. If not provided, a random secret is +# generated automatically. # # @param database_path -# Path to application database file +# Path to application database file inside the container. +# Must match a writable path in the container image (default: /opt/pabawi/data/pabawi.db). # # @param concurrent_execution_limit -# Maximum number of concurrent executions +# Maximum number of concurrent executions. # # @param command_whitelist -# Array of allowed commands for execution control +# Array of allowed commands for execution control. # # @param command_whitelist_allow_all -# Whether to bypass command whitelist and allow all commands +# Whether to bypass command whitelist and allow all commands. +# +# @param database_host_dir +# Host directory to mount for the database. Will be mapped to the +# container directory containing database_path. +# +# @param container_uid +# UID of the application user inside the container. Used to set +# ownership on the host database directory. # -# @example Basic usage +# @param container_gid +# GID of the application group inside the container. +# +# @param docker_extra_args +# Additional arguments to pass to `docker run`. +# +# @example Basic usage (Docker already installed) # include pabawi::install::docker # +# @example Install Docker packages and use custom image +# class { 'pabawi::install::docker': +# manage_docker => true, +# image => 'example42/pabawi:v1.2.3', +# } +# # @example Custom configuration # class { 'pabawi::install::docker': -# image => 'pabawi/pabawi:v1.2.3', +# image => 'example42/pabawi:v1.2.3', # environment => { # 'NODE_ENV' => 'production', # 'PORT' => '3000', @@ -59,25 +93,45 @@ # } # class pabawi::install::docker ( - Boolean $manage_docker = true, + Boolean $manage_docker = false, + String[1] $docker_package = 'docker-ce', String[1] $image = 'example42/pabawi:latest', String[1] $container_name = 'pabawi', Hash[String[1], String] $environment = {}, Array[String[1]] $volumes = [], Hash[String[1], String[1]] $ports = { '3000' => '3000' }, + String[1] $bind_address = lookup('pabawi::proxy_manage', Boolean, 'first', false) ? { + true => '127.0.0.1', + default => '0.0.0.0', + }, Boolean $auto_restart = true, Stdlib::Absolutepath $install_dir = '/opt/pabawi', String[1] $log_level = 'info', Boolean $auth_enabled = false, - Optional[String[1]] $jwt_secret = undef, - Stdlib::Absolutepath $database_path = '/var/lib/pabawi/pabawi.db', + String[1] $jwt_secret = fqdn_rand_string(64), + Stdlib::Absolutepath $database_path = '/opt/pabawi/data/pabawi.db', + Stdlib::Absolutepath $database_host_dir = '/opt/pabawi/data', + Integer $container_uid = 1001, + Integer $container_gid = 1001, Integer $concurrent_execution_limit = 5, Array[String[1]] $command_whitelist = [], Boolean $command_whitelist_allow_all = false, + String $docker_extra_args = '', ) { - # Validate auth configuration - if $auth_enabled and !$jwt_secret { - fail('pabawi::install::docker: jwt_secret is required when auth_enabled is true') + # Docker binary path + $docker_bin = '/usr/bin/docker' + + # Optionally install Docker packages + if $manage_docker { + package { $docker_package: + ensure => installed, + } + + service { 'docker': + ensure => running, + enable => true, + require => Package[$docker_package], + } } # Create installation directory for .env file @@ -88,17 +142,16 @@ mode => '0755', } - # Create database directory - $database_dir = dirname($database_path) - exec { "create_database_dir_${database_path}": - command => "mkdir -p ${database_dir}", + # Create database host directory with container user ownership + exec { "create_database_dir_${database_host_dir}": + command => "mkdir -p ${database_host_dir}", path => ['/usr/bin', '/bin'], - creates => $database_dir, + creates => $database_host_dir, } - -> file { $database_dir: + -> file { $database_host_dir: ensure => directory, - owner => 'root', - group => 'root', + owner => $container_uid, + group => $container_gid, mode => '0755', } @@ -121,9 +174,10 @@ target => 'pabawi_env_file', content => @("EOT"), # Pabawi Base Configuration + HOST=${bind_address} LOG_LEVEL=${log_level} AUTH_ENABLED=${auth_enabled} - JWT_SECRET=${pick($jwt_secret, 'not-set')} + JWT_SECRET=${jwt_secret} DATABASE_PATH=${database_path} CONCURRENT_EXECUTION_LIMIT=${concurrent_execution_limit} COMMAND_WHITELIST=${command_whitelist_json} @@ -132,34 +186,91 @@ order => '10', } - # Conditionally manage Docker installation - if $manage_docker { - class { 'docker': - ensure => present, - } + # Build docker run arguments + $database_container_dir = dirname($database_path) + $port_args = $ports.map |$host, $container| { "-p ${bind_address}:${host}:${container}" }.join(' ') + $all_volumes = $volumes + ["${database_host_dir}:${database_container_dir}"] + $volume_args = $all_volumes.map |$v| { "-v ${v}" }.join(' ') + $env_args = $environment.map |$key, $value| { "-e ${key}=${value}" }.join(' ') + + $docker_run_args = [ + '--rm', + "--name ${container_name}", + "--env-file ${env_file_path}", + $port_args, + $volume_args, + $env_args, + $docker_extra_args, + $image, + ].filter |$arg| { $arg != '' }.join(' ') + + # Pull the image before starting the service + $_pull_require = $manage_docker ? { + true => Service['docker'], + default => [], + } + + exec { "docker_pull_${container_name}": + command => "${docker_bin} pull ${image}", + path => ['/usr/bin', '/bin'], + unless => "${docker_bin} image inspect ${image} > /dev/null 2>&1", + require => $_pull_require, + } + + $_restart_policy = $auto_restart ? { + true => 'on-failure', + default => 'no', + } + + # Create systemd service file + file { "/etc/systemd/system/${container_name}.service": + ensure => file, + mode => '0644', + owner => 'root', + group => 'root', + content => @("EOT"), + [Unit] + Description=Pabawi Docker Container + After=network-online.target docker.service + Wants=network-online.target + Requires=docker.service + + [Service] + Type=simple + ExecStartPre=-${docker_bin} stop ${container_name} + ExecStartPre=-${docker_bin} rm ${container_name} + ExecStart=${docker_bin} run ${docker_run_args} + ExecStop=${docker_bin} stop ${container_name} + Restart=${_restart_policy} + RestartSec=10 + StandardOutput=journal + StandardError=journal + + [Install] + WantedBy=multi-user.target + | EOT + notify => Exec["systemd_reload_${container_name}"], } - # Pull the Docker image - docker::image { $image: - ensure => present, - require => $manage_docker ? { - true => Class['docker'], - default => undef, - }, + # Reload systemd when service file changes + exec { "systemd_reload_${container_name}": + command => 'systemctl daemon-reload', + path => ['/usr/bin', '/bin'], + refreshonly => true, } - # Create and run the container - docker::run { $container_name: - image => $image, - env => $environment.map |$key, $value| { "${key}=${value}" }, - volumes => $volumes + ["${env_file_path}:/app/.env:ro"], - ports => $ports.map |$host, $container| { "${host}:${container}" }, - restart => $auto_restart ? { - true => 'always', - default => 'no', - }, - require => [ - Docker::Image[$image], + # Manage the container service + service { $container_name: + ensure => running, + enable => true, + require => [ + File["/etc/systemd/system/${container_name}.service"], + Exec["docker_pull_${container_name}"], + Exec["systemd_reload_${container_name}"], + Concat['pabawi_env_file'], + ], + subscribe => [ + File["/etc/systemd/system/${container_name}.service"], Concat['pabawi_env_file'], ], } diff --git a/manifests/install/npm.pp b/manifests/install/npm.pp index 05a7b6c..21e19d4 100644 --- a/manifests/install/npm.pp +++ b/manifests/install/npm.pp @@ -42,6 +42,10 @@ # @param concurrent_execution_limit # Maximum number of concurrent executions # +# @param bind_address +# IP address for the application to listen on. Defaults to 127.0.0.1 when +# pabawi::proxy_manage is true (looked up from Hiera), 0.0.0.0 otherwise. +# # @example Basic usage # include pabawi::install::npm # @@ -65,6 +69,10 @@ Optional[String[1]] $jwt_secret = undef, Stdlib::Absolutepath $database_path = '/opt/pabawi/data/pabawi.db', Integer $concurrent_execution_limit = 5, + String[1] $bind_address = lookup('pabawi::proxy_manage', Boolean, 'first', false) ? { + true => '127.0.0.1', + default => '0.0.0.0', + }, ) { # Validate auth configuration if $auth_enabled and !$jwt_secret { @@ -117,10 +125,10 @@ # Create .env file using concat $env_file_path = "${install_dir}/backend/.env" concat { 'pabawi_env_file': - path => $env_file_path, - owner => $user, - group => $group, - mode => '0600', + path => $env_file_path, + owner => $user, + group => $group, + mode => '0600', } # Base configuration fragment @@ -128,6 +136,7 @@ target => 'pabawi_env_file', content => @("EOT"), # Pabawi Base Configuration + HOST=${bind_address} LOG_LEVEL=${log_level} AUTH_ENABLED=${auth_enabled} JWT_SECRET=${pick($jwt_secret, 'not-set')} @@ -213,7 +222,7 @@ Type=simple User=${user} Group=${group} - WorkingDirectory=${install_dir} + WorkingDirectory=${install_dir}/backend ExecStart=/usr/bin/node ${install_dir}/backend/dist/server.js Restart=on-failure RestartSec=10 diff --git a/manifests/integrations/ansible.pp b/manifests/integrations/ansible.pp index 17de3f7..23110dd 100644 --- a/manifests/integrations/ansible.pp +++ b/manifests/integrations/ansible.pp @@ -29,12 +29,8 @@ # }, # } # -# @example With git repositories +# @example Minimal usage with git repositories (paths default to /opt/pabawi/ansible/*) # class { 'pabawi::integrations::ansible': -# settings => { -# 'inventory_path' => '/opt/pabawi/ansible/inventory', -# 'playbook_path' => '/opt/pabawi/ansible/playbooks', -# }, # inventory_source => 'https://github.com/example/ansible-inventory.git', # playbook_source => 'https://github.com/example/ansible-playbooks.git', # } @@ -46,15 +42,12 @@ Optional[String[1]] $inventory_source = undef, Optional[String[1]] $playbook_source = undef, ) { - # Validate required parameters when integration is enabled - if $enabled { - if $inventory_source and !$settings['inventory_path'] { - fail('pabawi::integrations::ansible: settings[\'inventory_path\'] is required when inventory_source is provided') - } - if $playbook_source and !$settings['playbook_path'] { - fail('pabawi::integrations::ansible: settings[\'playbook_path\'] is required when playbook_source is provided') - } + # Merge sane defaults for local paths when source is provided but path is not + $_default_settings = { + 'inventory_path' => '/opt/pabawi/ansible/inventory', + 'playbook_path' => '/opt/pabawi/ansible/playbooks', } + $_settings = $_default_settings + $settings # Manage ansible package if requested if $manage_package { @@ -65,7 +58,7 @@ # Clone inventory repository if source is provided if $inventory_source { - $inventory_path = $settings['inventory_path'] + $inventory_path = $_settings['inventory_path'] # Ensure parent directory exists $inventory_parent_dir = dirname($inventory_path) exec { "create_ansible_inventory_parent_dir_${inventory_path}": @@ -84,7 +77,7 @@ # Clone playbook repository if source is provided if $playbook_source { - $playbook_path = $settings['playbook_path'] + $playbook_path = $_settings['playbook_path'] # Ensure parent directory exists $playbook_parent_dir = dirname($playbook_path) exec { "create_ansible_playbook_parent_dir_${playbook_path}": @@ -103,7 +96,7 @@ # Transform settings hash values to .env format # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' - $env_vars = $settings.reduce({}) |$memo, $pair| { + $env_vars = $_settings.reduce({}) |$memo, $pair| { $key = $pair[0] $value = $pair[1] @@ -131,7 +124,7 @@ target => 'pabawi_env_file', content => @("EOT"), # Ansible Integration - ANSIBLE_ENABLED=${enabled ? { true => 'true', false => 'false' }} + ANSIBLE_ENABLED=${enabled} ${env_lines} | EOT order => '24', diff --git a/manifests/integrations/aws.pp b/manifests/integrations/aws.pp new file mode 100644 index 0000000..1f7a4e4 --- /dev/null +++ b/manifests/integrations/aws.pp @@ -0,0 +1,94 @@ +# @summary Configure Pabawi integration with AWS EC2 +# +# This class manages the integration between Pabawi and AWS EC2, +# writing configuration to the .env file for EC2 inventory discovery, +# lifecycle management, and provisioning. +# +# Authentication supports three modes: +# 1. Explicit access key + secret key (via settings) +# 2. AWS named profile (via settings) +# 3. Default credential chain (env vars, ~/.aws/credentials, instance profile) +# +# @param enabled +# Whether the integration is enabled (sets AWS_ENABLED in .env) +# +# @param settings +# Hash of Pabawi application configuration settings written to .env with AWS_ prefix. +# Supported keys: access_key_id, secret_access_key, default_region, regions, +# session_token, profile, endpoint +# +# @example Basic usage with access keys +# class { 'pabawi::integrations::aws': +# settings => { +# 'access_key_id' => 'AKIAIOSFODNN7EXAMPLE', +# 'secret_access_key' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', +# 'default_region' => 'us-east-1', +# 'regions' => ['us-east-1', 'eu-west-1'], +# }, +# } +# +# @example Using AWS profile (recommended for non-EC2 hosts) +# class { 'pabawi::integrations::aws': +# settings => { +# 'profile' => 'pabawi-prod', +# 'default_region' => 'us-east-1', +# 'regions' => ['us-east-1', 'us-west-2'], +# }, +# } +# +# @example Using default credential chain (recommended on EC2 with instance profile) +# class { 'pabawi::integrations::aws': +# settings => { +# 'default_region' => 'us-east-1', +# }, +# } +# +# @example Via Hiera +# pabawi::integrations::aws::enabled: true +# pabawi::integrations::aws::settings: +# profile: 'pabawi-prod' +# default_region: 'us-east-1' +# regions: +# - 'us-east-1' +# - 'eu-west-1' +# +class pabawi::integrations::aws ( + Boolean $enabled = true, + Hash $settings = {}, +) { + # Transform settings hash values to .env format + # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' + $env_vars = $settings.reduce({}) |$memo, $pair| { + $key = $pair[0] + $value = $pair[1] + + # Transform value based on type + $transformed = $value ? { + Array => inline_template('[<%= @value.map { |v| "\"#{v}\"" }.join(",") %>]'), + Boolean => $value ? { true => 'true', false => 'false' }, + Integer => String($value), + String => $value, + Undef => 'not-set', + default => pick($value, 'not-set'), + } + + $env_key = upcase($key) + $memo + { "AWS_${env_key}" => $transformed } + } + + # Build environment variable lines + $env_lines = $env_vars.map |$key, $value| { + "${key}=${value}" + }.join("\n") + + # Add configuration to .env file via concat fragment + concat::fragment { 'pabawi_env_aws': + target => 'pabawi_env_file', + content => @("EOT"), + # AWS Integration + AWS_ENABLED=${enabled} + ${env_lines} + | EOT + order => '27', + } +} diff --git a/manifests/integrations/bolt.pp b/manifests/integrations/bolt.pp index 250bc6e..83efa79 100644 --- a/manifests/integrations/bolt.pp +++ b/manifests/integrations/bolt.pp @@ -25,11 +25,8 @@ # }, # } # -# @example With git repository +# @example Minimal usage with git repository (project_path defaults to /opt/pabawi/bolt-project) # class { 'pabawi::integrations::bolt': -# settings => { -# 'project_path' => '/opt/pabawi/bolt-project', -# }, # project_path_source => 'https://github.com/example/bolt-project.git', # } # @@ -39,12 +36,11 @@ Boolean $manage_package = false, Optional[String[1]] $project_path_source = undef, ) { - # Validate required parameters when integration is enabled - if $enabled { - if $project_path_source and !$settings['project_path'] { - fail('pabawi::integrations::bolt: settings[\'project_path\'] is required when project_path_source is provided') - } + # Merge sane defaults for local paths when source is provided but path is not + $_default_settings = { + 'project_path' => '/opt/pabawi/bolt-project', } + $_settings = $_default_settings + $settings # Manage bolt package if requested if $manage_package { @@ -55,7 +51,7 @@ # Clone git repository if source is provided if $project_path_source { - $project_path = $settings['project_path'] + $project_path = $_settings['project_path'] # Ensure parent directory exists $parent_dir = dirname($project_path) exec { "create_bolt_parent_dir_${project_path}": @@ -74,7 +70,7 @@ # Transform settings hash values to .env format # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' - $env_vars = $settings.reduce({}) |$memo, $pair| { + $env_vars = $_settings.reduce({}) |$memo, $pair| { $key = $pair[0] $value = $pair[1] @@ -102,7 +98,7 @@ target => 'pabawi_env_file', content => @("EOT"), # Bolt Integration - BOLT_ENABLED=${enabled ? { true => 'true', false => 'false' }} + BOLT_ENABLED=${enabled} ${env_lines} | EOT order => '20', diff --git a/manifests/integrations/hiera.pp b/manifests/integrations/hiera.pp index 37e0cd0..f7e55b9 100644 --- a/manifests/integrations/hiera.pp +++ b/manifests/integrations/hiera.pp @@ -27,13 +27,8 @@ # }, # } # -# @example With git repository +# @example Minimal usage with git repository (control_repo_path defaults to /opt/pabawi/control-repo) # class { 'pabawi::integrations::hiera': -# settings => { -# 'control_repo_path' => '/opt/pabawi/control-repo', -# 'config_path' => 'hiera_pabawi.yaml', -# 'environments' => ['production'], -# }, # control_repo_source => 'https://github.com/example/control-repo.git', # } # @@ -43,12 +38,11 @@ Boolean $manage_package = false, Optional[String[1]] $control_repo_source = undef, ) { - # Validate required parameters when integration is enabled - if $enabled { - if $control_repo_source and !$settings['control_repo_path'] { - fail('pabawi::integrations::hiera: settings[\'control_repo_path\'] is required when control_repo_source is provided') - } + # Merge sane defaults for local paths when source is provided but path is not + $_default_settings = { + 'control_repo_path' => '/opt/pabawi/control-repo', } + $_settings = $_default_settings + $settings # Manage hiera package if requested if $manage_package { @@ -59,7 +53,7 @@ # Clone git repository if source is provided if $control_repo_source { - $control_repo_path = $settings['control_repo_path'] + $control_repo_path = $_settings['control_repo_path'] # Ensure parent directory exists $parent_dir = dirname($control_repo_path) exec { "create_hiera_parent_dir_${control_repo_path}": @@ -78,7 +72,7 @@ # Transform settings hash values to .env format # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' - $env_vars = $settings.reduce({}) |$memo, $pair| { + $env_vars = $_settings.reduce({}) |$memo, $pair| { $key = $pair[0] $value = $pair[1] @@ -106,7 +100,7 @@ target => 'pabawi_env_file', content => @("EOT"), # Hiera Integration - HIERA_ENABLED=${enabled ? { true => 'true', false => 'false' }} + HIERA_ENABLED=${enabled} ${env_lines} | EOT order => '23', diff --git a/manifests/integrations/proxmox.pp b/manifests/integrations/proxmox.pp new file mode 100644 index 0000000..488af34 --- /dev/null +++ b/manifests/integrations/proxmox.pp @@ -0,0 +1,256 @@ +# @summary Configure Pabawi integration with Proxmox VE +# +# This class manages the integration between Pabawi and Proxmox Virtual Environment, +# including SSL certificate deployment and .env configuration for VM/LXC management. +# +# @param enabled +# Whether the integration is enabled (sets PROXMOX_ENABLED in .env) +# +# @param settings +# Hash of Pabawi application configuration settings written to .env with PROXMOX_ prefix. +# Supported keys: host, port, token, username, password, realm, +# ssl_reject_unauthorized, ssl_ca, ssl_cert, ssl_key, timeout, priority +# +# @param ssl_ca_source +# Source URL for SSL CA certificate (supports file://, https://, or local path). +# Deploys to path specified in settings['ssl_ca'] +# +# @param ssl_cert_source +# Source URL for SSL certificate (supports file://, https://, or local path). +# Deploys to path specified in settings['ssl_cert'] +# +# @param ssl_key_source +# Source URL for SSL private key (supports file://, https://, or local path). +# Deploys to path specified in settings['ssl_key'] +# +# @example Basic usage with token authentication (recommended) +# class { 'pabawi::integrations::proxmox': +# settings => { +# 'host' => 'proxmox.example.com', +# 'port' => 8006, +# 'token' => 'user@pam!tokenid=token-value', +# }, +# } +# +# @example With username/password and SSL certificates +# class { 'pabawi::integrations::proxmox': +# settings => { +# 'host' => 'proxmox.example.com', +# 'port' => 8006, +# 'username' => 'root@pam', +# 'password' => 'secret', +# 'realm' => 'pam', +# 'ssl_reject_unauthorized' => true, +# 'ssl_ca' => '/opt/pabawi/certs/proxmox/ca.pem', +# 'ssl_cert' => '/opt/pabawi/certs/proxmox/cert.pem', +# 'ssl_key' => '/opt/pabawi/certs/proxmox/key.pem', +# 'timeout' => 30000, +# 'priority' => 7, +# }, +# ssl_ca_source => 'file:///etc/ssl/certs/proxmox-ca.pem', +# ssl_cert_source => 'file:///etc/ssl/certs/proxmox-cert.pem', +# ssl_key_source => 'file:///etc/ssl/private/proxmox-key.pem', +# } +# +# @example Via Hiera +# pabawi::integrations::proxmox::enabled: true +# pabawi::integrations::proxmox::settings: +# host: 'proxmox.example.com' +# port: 8006 +# token: 'user@pam!tokenid=token-value' +# ssl_reject_unauthorized: true +# timeout: 30000 +# +class pabawi::integrations::proxmox ( + Boolean $enabled = true, + Hash $settings = {}, + Optional[String[1]] $ssl_ca_source = undef, + Optional[String[1]] $ssl_cert_source = undef, + Optional[String[1]] $ssl_key_source = undef, +) { + # Merge sane defaults for SSL paths + $_default_settings = { + 'ssl_ca' => '/opt/pabawi/certs/proxmox/ca.pem', + 'ssl_cert' => '/opt/pabawi/certs/proxmox/cert.pem', + 'ssl_key' => '/opt/pabawi/certs/proxmox/key.pem', + } + $_settings = $_default_settings + $settings + + # Deploy SSL certificates if sources are provided + if $ssl_ca_source { + $ssl_ca_path = $_settings['ssl_ca'] + $ssl_ca_dir = dirname($ssl_ca_path) + $ssl_ca_parent = dirname($ssl_ca_dir) + $ssl_ca_mode = '0644' + ensure_resource('file', $ssl_ca_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_ca_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_ca_parent] }) + + # Handle file:// URLs + if $ssl_ca_source =~ /^file:\/\/(.+)$/ { + file { $ssl_ca_path: + ensure => file, + source => regsubst($ssl_ca_source, '^file://', ''), + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], + } + } + # Handle https:// URLs + elsif $ssl_ca_source =~ /^https:\/\/.+$/ { + exec { 'download_proxmox_ssl_ca': + command => "curl -sL -o ${ssl_ca_path} ${ssl_ca_source}", + path => ['/usr/bin', '/bin'], + creates => $ssl_ca_path, + require => File[$ssl_ca_dir], + } + -> file { $ssl_ca_path: + ensure => file, + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + } + } + # Handle direct file paths + else { + file { $ssl_ca_path: + ensure => file, + source => $ssl_ca_source, + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], + } + } + } + + if $ssl_cert_source { + $ssl_cert_path = $_settings['ssl_cert'] + $ssl_cert_dir = dirname($ssl_cert_path) + $ssl_cert_parent = dirname($ssl_cert_dir) + $ssl_cert_mode = '0644' + ensure_resource('file', $ssl_cert_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_cert_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_cert_parent] }) + + # Handle file:// URLs + if $ssl_cert_source =~ /^file:\/\/(.+)$/ { + file { $ssl_cert_path: + ensure => file, + source => regsubst($ssl_cert_source, '^file://', ''), + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], + } + } + # Handle https:// URLs + elsif $ssl_cert_source =~ /^https:\/\/.+$/ { + exec { 'download_proxmox_ssl_cert': + command => "curl -sL -o ${ssl_cert_path} ${ssl_cert_source}", + path => ['/usr/bin', '/bin'], + creates => $ssl_cert_path, + require => File[$ssl_cert_dir], + } + -> file { $ssl_cert_path: + ensure => file, + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + } + } + # Handle direct file paths + else { + file { $ssl_cert_path: + ensure => file, + source => $ssl_cert_source, + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], + } + } + } + + if $ssl_key_source { + $ssl_key_path = $_settings['ssl_key'] + $ssl_key_dir = dirname($ssl_key_path) + $ssl_key_parent = dirname($ssl_key_dir) + $ssl_key_mode = '0600' + ensure_resource('file', $ssl_key_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_key_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_key_parent] }) + + # Handle file:// URLs + if $ssl_key_source =~ /^file:\/\/(.+)$/ { + file { $ssl_key_path: + ensure => file, + source => regsubst($ssl_key_source, '^file://', ''), + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], + } + } + # Handle https:// URLs + elsif $ssl_key_source =~ /^https:\/\/.+$/ { + exec { 'download_proxmox_ssl_key': + command => "curl -sL -o ${ssl_key_path} ${ssl_key_source}", + path => ['/usr/bin', '/bin'], + creates => $ssl_key_path, + require => File[$ssl_key_dir], + } + -> file { $ssl_key_path: + ensure => file, + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + } + } + # Handle direct file paths + else { + file { $ssl_key_path: + ensure => file, + source => $ssl_key_source, + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], + } + } + } + + # Transform settings hash values to .env format + # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' + $env_vars = $_settings.reduce({}) |$memo, $pair| { + $key = $pair[0] + $value = $pair[1] + + # Transform value based on type + $transformed = $value ? { + Array => inline_template('[<%= @value.map { |v| "\"#{v}\"" }.join(",") %>]'), + Boolean => $value ? { true => 'true', false => 'false' }, + Integer => String($value), + String => $value, + Undef => 'not-set', + default => pick($value, 'not-set'), + } + + $env_key = upcase($key) + $memo + { "PROXMOX_${env_key}" => $transformed } + } + + # Build environment variable lines + $env_lines = $env_vars.map |$key, $value| { + "${key}=${value}" + }.join("\n") + + # Add configuration to .env file via concat fragment + concat::fragment { 'pabawi_env_proxmox': + target => 'pabawi_env_file', + content => @("EOT"), + # Proxmox Integration + PROXMOX_ENABLED=${enabled} + ${env_lines} + | EOT + order => '26', + } +} diff --git a/manifests/integrations/puppetdb.pp b/manifests/integrations/puppetdb.pp index 395a051..0fdec88 100644 --- a/manifests/integrations/puppetdb.pp +++ b/manifests/integrations/puppetdb.pp @@ -56,40 +56,35 @@ Optional[String[1]] $ssl_cert_source = undef, Optional[String[1]] $ssl_key_source = undef, ) { - # Validate required parameters when integration is enabled - if $enabled { - # Source-path consistency validation - if $ssl_ca_source and !$settings['ssl_ca'] { - fail('pabawi::integrations::puppetdb: settings[\'ssl_ca\'] is required when ssl_ca_source is provided') - } - if $ssl_cert_source and !$settings['ssl_cert'] { - fail('pabawi::integrations::puppetdb: settings[\'ssl_cert\'] is required when ssl_cert_source is provided') - } - if $ssl_key_source and !$settings['ssl_key'] { - fail('pabawi::integrations::puppetdb: settings[\'ssl_key\'] is required when ssl_key_source is provided') - } - - # SSL configuration validation - all three SSL sources should be provided together - $ssl_sources_provided = [$ssl_ca_source, $ssl_cert_source, $ssl_key_source].filter |$val| { $val != undef } - if $ssl_sources_provided.length > 0 and $ssl_sources_provided.length < 3 { - fail('pabawi::integrations::puppetdb: When SSL certificates are used, all three SSL sources (ssl_ca_source, ssl_cert_source, ssl_key_source) must be provided together') - } + # Merge sane defaults for local paths when source is provided but path is not + $_default_settings = { + 'ssl_ca' => '/opt/pabawi/certs/puppetdb/ca.pem', + 'ssl_cert' => '/opt/pabawi/certs/puppetdb/cert.pem', + 'ssl_key' => '/opt/pabawi/certs/puppetdb/key.pem', } + $_settings = $_default_settings + $settings + if ($ssl_ca_source or $ssl_cert_source or $ssl_key_source) and !($ssl_ca_source and $ssl_cert_source and $ssl_key_source) { + fail('When configuring pabawi::integrations::puppetdb SSL sources, ssl_ca_source, ssl_cert_source, and ssl_key_source must all be set together.') + } # Deploy SSL certificates if sources are provided if $ssl_ca_source { - $ssl_ca_path = $settings['ssl_ca'] - $ssl_ca_mode = '0644' + $ssl_ca_path = $_settings['ssl_ca'] + $ssl_ca_dir = dirname($ssl_ca_path) + $ssl_ca_parent = dirname($ssl_ca_dir) + $ssl_ca_mode = '0644' + ensure_resource('file', $ssl_ca_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_ca_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_ca_parent] }) # Handle file:// URLs if $ssl_ca_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_ca_path: - ensure => file, - source => $source_path, - mode => $ssl_ca_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_ca_source, '^file://', ''), + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], } } # Handle https:// URLs @@ -98,6 +93,7 @@ command => "curl -sL -o ${ssl_ca_path} ${ssl_ca_source}", path => ['/usr/bin', '/bin'], creates => $ssl_ca_path, + require => File[$ssl_ca_dir], } -> file { $ssl_ca_path: ensure => file, @@ -109,28 +105,33 @@ # Handle direct file paths else { file { $ssl_ca_path: - ensure => file, - source => $ssl_ca_source, - mode => $ssl_ca_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_ca_source, + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], } } } if $ssl_cert_source { - $ssl_cert_path = $settings['ssl_cert'] - $ssl_cert_mode = '0644' + $ssl_cert_path = $_settings['ssl_cert'] + $ssl_cert_dir = dirname($ssl_cert_path) + $ssl_cert_parent = dirname($ssl_cert_dir) + $ssl_cert_mode = '0644' + ensure_resource('file', $ssl_cert_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_cert_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_cert_parent] }) # Handle file:// URLs if $ssl_cert_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_cert_path: - ensure => file, - source => $source_path, - mode => $ssl_cert_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_cert_source, '^file://', ''), + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], } } # Handle https:// URLs @@ -139,6 +140,7 @@ command => "curl -sL -o ${ssl_cert_path} ${ssl_cert_source}", path => ['/usr/bin', '/bin'], creates => $ssl_cert_path, + require => File[$ssl_cert_dir], } -> file { $ssl_cert_path: ensure => file, @@ -150,28 +152,33 @@ # Handle direct file paths else { file { $ssl_cert_path: - ensure => file, - source => $ssl_cert_source, - mode => $ssl_cert_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_cert_source, + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], } } } if $ssl_key_source { - $ssl_key_path = $settings['ssl_key'] - $ssl_key_mode = '0600' + $ssl_key_path = $_settings['ssl_key'] + $ssl_key_dir = dirname($ssl_key_path) + $ssl_key_parent = dirname($ssl_key_dir) + $ssl_key_mode = '0600' + ensure_resource('file', $ssl_key_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_key_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_key_parent] }) # Handle file:// URLs if $ssl_key_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_key_path: - ensure => file, - source => $source_path, - mode => $ssl_key_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_key_source, '^file://', ''), + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], } } # Handle https:// URLs @@ -180,6 +187,7 @@ command => "curl -sL -o ${ssl_key_path} ${ssl_key_source}", path => ['/usr/bin', '/bin'], creates => $ssl_key_path, + require => File[$ssl_key_dir], } -> file { $ssl_key_path: ensure => file, @@ -191,18 +199,19 @@ # Handle direct file paths else { file { $ssl_key_path: - ensure => file, - source => $ssl_key_source, - mode => $ssl_key_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_key_source, + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], } } } # Transform settings hash values to .env format # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' - $env_vars = $settings.reduce({}) |$memo, $pair| { + $env_vars = $_settings.reduce({}) |$memo, $pair| { $key = $pair[0] $value = $pair[1] @@ -230,7 +239,7 @@ target => 'pabawi_env_file', content => @("EOT"), # PuppetDB Integration - PUPPETDB_ENABLED=${enabled ? { true => 'true', false => 'false' }} + PUPPETDB_ENABLED=${enabled} ${env_lines} | EOT order => '21', diff --git a/manifests/integrations/puppetserver.pp b/manifests/integrations/puppetserver.pp index 65bf554..b12f251 100644 --- a/manifests/integrations/puppetserver.pp +++ b/manifests/integrations/puppetserver.pp @@ -62,20 +62,16 @@ Optional[String[1]] $ssl_cert_source = undef, Optional[String[1]] $ssl_key_source = undef, ) { - # Validate required parameters when integration is enabled - if $enabled { - # Source-path consistency validation - if $ssl_ca_source and !$settings['ssl_ca'] { - fail('pabawi::integrations::puppetserver: settings[\'ssl_ca\'] is required when ssl_ca_source is provided') - } - if $ssl_cert_source and !$settings['ssl_cert'] { - fail('pabawi::integrations::puppetserver: settings[\'ssl_cert\'] is required when ssl_cert_source is provided') - } - if $ssl_key_source and !$settings['ssl_key'] { - fail('pabawi::integrations::puppetserver: settings[\'ssl_key\'] is required when ssl_key_source is provided') - } + # Merge sane defaults for local paths when source is provided but path is not + $_default_settings = { + 'ssl_ca' => '/opt/pabawi/certs/puppetserver/ca.pem', + 'ssl_cert' => '/opt/pabawi/certs/puppetserver/cert.pem', + 'ssl_key' => '/opt/pabawi/certs/puppetserver/key.pem', + } + $_settings = $_default_settings + $settings - # SSL configuration validation - all three SSL sources should be provided together + # Validate that SSL sources are provided together when any is specified + if $enabled { $ssl_sources_provided = [$ssl_ca_source, $ssl_cert_source, $ssl_key_source].filter |$val| { $val != undef } if $ssl_sources_provided.length > 0 and $ssl_sources_provided.length < 3 { fail('pabawi::integrations::puppetserver: When SSL certificates are used, all three SSL sources (ssl_ca_source, ssl_cert_source, ssl_key_source) must be provided together') @@ -84,18 +80,22 @@ # Deploy SSL certificates if sources are provided if $ssl_ca_source { - $ssl_ca_path = $settings['ssl_ca'] - $ssl_ca_mode = '0644' + $ssl_ca_path = $_settings['ssl_ca'] + $ssl_ca_dir = dirname($ssl_ca_path) + $ssl_ca_parent = dirname($ssl_ca_dir) + $ssl_ca_mode = '0644' + ensure_resource('file', $ssl_ca_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_ca_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_ca_parent] }) # Handle file:// URLs if $ssl_ca_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_ca_path: - ensure => file, - source => $source_path, - mode => $ssl_ca_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_ca_source, '^file://', ''), + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], } } # Handle https:// URLs @@ -104,6 +104,7 @@ command => "curl -sL -o ${ssl_ca_path} ${ssl_ca_source}", path => ['/usr/bin', '/bin'], creates => $ssl_ca_path, + require => File[$ssl_ca_dir], } -> file { $ssl_ca_path: ensure => file, @@ -115,28 +116,33 @@ # Handle direct file paths else { file { $ssl_ca_path: - ensure => file, - source => $ssl_ca_source, - mode => $ssl_ca_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_ca_source, + mode => $ssl_ca_mode, + owner => 'root', + group => 'root', + require => File[$ssl_ca_dir], } } } if $ssl_cert_source { - $ssl_cert_path = $settings['ssl_cert'] - $ssl_cert_mode = '0644' + $ssl_cert_path = $_settings['ssl_cert'] + $ssl_cert_dir = dirname($ssl_cert_path) + $ssl_cert_parent = dirname($ssl_cert_dir) + $ssl_cert_mode = '0644' + ensure_resource('file', $ssl_cert_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_cert_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_cert_parent] }) # Handle file:// URLs if $ssl_cert_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_cert_path: - ensure => file, - source => $source_path, - mode => $ssl_cert_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_cert_source, '^file://', ''), + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], } } # Handle https:// URLs @@ -145,6 +151,7 @@ command => "curl -sL -o ${ssl_cert_path} ${ssl_cert_source}", path => ['/usr/bin', '/bin'], creates => $ssl_cert_path, + require => File[$ssl_cert_dir], } -> file { $ssl_cert_path: ensure => file, @@ -156,28 +163,33 @@ # Handle direct file paths else { file { $ssl_cert_path: - ensure => file, - source => $ssl_cert_source, - mode => $ssl_cert_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_cert_source, + mode => $ssl_cert_mode, + owner => 'root', + group => 'root', + require => File[$ssl_cert_dir], } } } if $ssl_key_source { - $ssl_key_path = $settings['ssl_key'] - $ssl_key_mode = '0600' + $ssl_key_path = $_settings['ssl_key'] + $ssl_key_dir = dirname($ssl_key_path) + $ssl_key_parent = dirname($ssl_key_dir) + $ssl_key_mode = '0600' + ensure_resource('file', $ssl_key_parent, { ensure => directory, owner => 'root', group => 'root', mode => '0755' }) + ensure_resource('file', $ssl_key_dir, { ensure => directory, owner => 'root', group => 'root', mode => '0755', require => File[$ssl_key_parent] }) # Handle file:// URLs if $ssl_key_source =~ /^file:\/\/(.+)$/ { - $source_path = $1 file { $ssl_key_path: - ensure => file, - source => $source_path, - mode => $ssl_key_mode, - owner => 'root', - group => 'root', + ensure => file, + source => regsubst($ssl_key_source, '^file://', ''), + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], } } # Handle https:// URLs @@ -186,6 +198,7 @@ command => "curl -sL -o ${ssl_key_path} ${ssl_key_source}", path => ['/usr/bin', '/bin'], creates => $ssl_key_path, + require => File[$ssl_key_dir], } -> file { $ssl_key_path: ensure => file, @@ -197,18 +210,19 @@ # Handle direct file paths else { file { $ssl_key_path: - ensure => file, - source => $ssl_key_source, - mode => $ssl_key_mode, - owner => 'root', - group => 'root', + ensure => file, + source => $ssl_key_source, + mode => $ssl_key_mode, + owner => 'root', + group => 'root', + require => File[$ssl_key_dir], } } } # Transform settings hash values to .env format # Arrays -> JSON, Booleans -> lowercase strings, Integers -> strings, undef/empty -> 'not-set' - $env_vars = $settings.reduce({}) |$memo, $pair| { + $env_vars = $_settings.reduce({}) |$memo, $pair| { $key = $pair[0] $value = $pair[1] @@ -236,7 +250,7 @@ target => 'pabawi_env_file', content => @("EOT"), # Puppet Server Integration - PUPPETSERVER_ENABLED=${enabled ? { true => 'true', false => 'false' }} + PUPPETSERVER_ENABLED=${enabled} ${env_lines} | EOT order => '22', diff --git a/manifests/integrations/ssh.pp b/manifests/integrations/ssh.pp index 34e688f..6640614 100644 --- a/manifests/integrations/ssh.pp +++ b/manifests/integrations/ssh.pp @@ -1,42 +1,41 @@ # @summary Configure Pabawi integration with SSH # # This class manages the integration between Pabawi and SSH, -# including SSH connection configuration and .env file settings. +# writing connection pool and execution defaults to the .env file. # # @param enabled # Whether the integration is enabled (sets SSH_ENABLED in .env) # # @param settings -# Hash of SSH configuration settings that will be written to .env file -# with SSH_ prefix. Supported keys include: -# - host: SSH host to connect to -# - port: SSH port (default 22) -# - username: SSH username -# - private_key_path: Path to SSH private key -# - timeout: Connection timeout in milliseconds -# - known_hosts_path: Path to known_hosts file +# Hash of SSH configuration settings written to .env with SSH_ prefix. +# Supported keys: config_path, default_user, default_port, default_key, +# host_key_check, connection_timeout, command_timeout, max_connections, +# max_connections_per_host, idle_timeout, concurrency_limit, +# sudo_enabled, sudo_command, sudo_passwordless, sudo_user, priority # # @example Basic usage # class { 'pabawi::integrations::ssh': -# enabled => true, # settings => { -# 'host' => 'remote.example.com', -# 'port' => 22, -# 'username' => 'automation', -# 'private_key_path' => '/opt/pabawi/ssh/id_rsa', -# 'timeout' => 30000, +# 'default_user' => 'automation', +# 'default_port' => 22, +# 'default_key' => '/opt/pabawi/ssh/id_ed25519', +# 'host_key_check' => true, +# 'connection_timeout' => 30, +# 'command_timeout' => 300, +# 'max_connections' => 50, +# 'concurrency_limit' => 10, # }, # } # # @example Via Hiera # pabawi::integrations::ssh::enabled: true # pabawi::integrations::ssh::settings: -# host: 'remote.example.com' -# port: 22 -# username: 'automation' -# private_key_path: '/opt/pabawi/ssh/id_rsa' -# timeout: 30000 -# known_hosts_path: '/opt/pabawi/ssh/known_hosts' +# default_user: 'automation' +# default_port: 22 +# default_key: '/opt/pabawi/ssh/id_ed25519' +# host_key_check: true +# connection_timeout: 30 +# max_connections: 50 # class pabawi::integrations::ssh ( Boolean $enabled = true, @@ -72,7 +71,7 @@ target => 'pabawi_env_file', content => @("EOT"), # SSH Integration - SSH_ENABLED=${enabled ? { true => 'true', false => 'false' }} + SSH_ENABLED=${enabled} ${env_lines} | EOT order => '25', diff --git a/manifests/proxy/nginx.pp b/manifests/proxy/nginx.pp index aae5a98..823caae 100644 --- a/manifests/proxy/nginx.pp +++ b/manifests/proxy/nginx.pp @@ -78,13 +78,15 @@ } # Manage nginx service + $_nginx_service_require = $manage_package ? { + true => Package['nginx'], + default => undef, + } + service { 'nginx': - ensure => running, - enable => true, - require => $manage_package ? { - true => Package['nginx'], - default => undef, - }, + ensure => running, + enable => true, + require => $_nginx_service_require, } # Setup SSL if enabled @@ -181,6 +183,12 @@ $ssl_config_content = '' } + # Build require list for config file + $_config_file_require = $manage_package ? { + true => [File[$config_dir], Package['nginx']], + default => [File[$config_dir]], + } + file { $config_file: ensure => file, mode => '0644', @@ -194,13 +202,7 @@ 'command_whitelist' => $command_whitelist, 'command_whitelist_allow_all' => $command_whitelist_allow_all, }), - require => [ - File[$config_dir], - $manage_package ? { - true => Package['nginx'], - default => [], - }, - ], + require => $_config_file_require, notify => Service['nginx'], } diff --git a/metadata.json b/metadata.json index 5447114..7aec553 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "example42-pabawi", - "version": "0.2.0", + "version": "1.0.0", "author": "Pabawi Team", "summary": "Puppet module for installing and configuring Pabawi application", "license": "Apache-2.0", @@ -12,10 +12,6 @@ "name": "puppetlabs/stdlib", "version_requirement": ">= 6.0.0 < 10.0.0" }, - { - "name": "puppetlabs/docker", - "version_requirement": ">= 4.0.0 < 10.0.0" - }, { "name": "puppetlabs/vcsrepo", "version_requirement": ">= 5.0.0 < 7.0.0" diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 39d29dd..85844b0 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -106,32 +106,54 @@ context 'with custom integrations array' do let(:params) do { - integrations: ['terraform'], + integrations: ['proxmox'], } end it { is_expected.to compile.with_all_deps } it 'includes listed integration classes' do - is_expected.to contain_class('pabawi::integrations::terraform') + is_expected.to contain_class('pabawi::integrations::proxmox') end it 'does not include unlisted integration classes' do is_expected.not_to contain_class('pabawi::integrations::ansible') end - it 'creates notify resource for each integration' do - is_expected.to contain_notify('pabawi_integration_terraform').with( - 'message' => 'Enabling integration: pabawi::integrations::terraform', - 'loglevel' => 'notice', - ) + end + + context 'with aws integration' do + let(:params) do + { + integrations: ['aws'], + } + end + + it { is_expected.to compile.with_all_deps } + + it 'includes aws integration class' do + is_expected.to contain_class('pabawi::integrations::aws') + end + end + + context 'with ssh integration' do + let(:params) do + { + integrations: ['ssh'], + } + end + + it { is_expected.to compile.with_all_deps } + + it 'includes ssh integration class' do + is_expected.to contain_class('pabawi::integrations::ssh') end end context 'with multiple integrations' do let(:params) do { - integrations: ['bolt', 'puppetdb', 'custom'], + integrations: ['bolt', 'puppetdb', 'ssh', 'proxmox', 'aws'], } end @@ -140,19 +162,21 @@ it 'includes all listed integration classes' do is_expected.to contain_class('pabawi::integrations::bolt') is_expected.to contain_class('pabawi::integrations::puppetdb') - is_expected.to contain_class('pabawi::integrations::custom') + is_expected.to contain_class('pabawi::integrations::ssh') + is_expected.to contain_class('pabawi::integrations::proxmox') + is_expected.to contain_class('pabawi::integrations::aws') end end context 'with invalid integration type' do let(:params) do { - integrations: [123], + integrations: ['nonexistent'], } end it 'fails with validation error' do - is_expected.to compile.and_raise_error(/expects an Array\[String/) + is_expected.to compile.and_raise_error(/expects.*Enum/) end end end diff --git a/types/config.pp b/types/config.pp index 8ce2121..3024b6f 100644 --- a/types/config.pp +++ b/types/config.pp @@ -1,11 +1,9 @@ # Pabawi::Config type # Defines the structure for main module configuration type Pabawi::Config = Struct[{ - proxy_manage => Boolean, - proxy_class => String[1], - install_manage => Boolean, - install_class => String[1], - bolt_enable => Boolean, - puppetdb_enable => Boolean, - integrations => Hash[String[1], Boolean], + proxy_manage => Boolean, + proxy_class => String[1], + install_manage => Boolean, + install_class => String[1], + integrations => Array[String[1]], }]